mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-21 14:41:03 +10:00
Implement the ability to re-unlock an already unlocked phone
+ Start to work on a better UI for reset
This commit is contained in:
@@ -306,7 +306,26 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
||||||
byte[] DoneCommand = new byte[] { 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 };
|
byte[] DoneCommand = new byte[] { 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 };
|
||||||
byte[] DoneResponse = Serial.SendCommand(DoneCommand, new byte[] { 0x06, 0x00, 0x00, 0x00 });
|
bool Started = false;
|
||||||
|
int count = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
byte[] DoneResponse = Serial.SendCommand(DoneCommand, new byte[] { 0x06, 0x00, 0x00, 0x00 });
|
||||||
|
Started = true;
|
||||||
|
}
|
||||||
|
catch (BadConnectionException)
|
||||||
|
{
|
||||||
|
LogFile.Log("Problem while starting programmer. Attempting again.", LogType.FileAndConsole);
|
||||||
|
}
|
||||||
|
} while (!Started || count >= 3);
|
||||||
|
if (count >= 3 && !Started)
|
||||||
|
{
|
||||||
|
LogFile.Log("Maximum number of attempts to start the programmer exceeded.", LogType.FileAndConsole);
|
||||||
|
throw new BadConnectionException();
|
||||||
|
}
|
||||||
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -29,7 +29,7 @@ namespace WPinternals
|
|||||||
internal ProgressUpdater ProgressUpdater = null;
|
internal ProgressUpdater ProgressUpdater = null;
|
||||||
|
|
||||||
// UIContext can be passed to BusyViewModel, when it needs to update progress-controls and it is created on a worker-thread.
|
// UIContext can be passed to BusyViewModel, when it needs to update progress-controls and it is created on a worker-thread.
|
||||||
internal BusyViewModel(string Message, string SubMessage = null, ulong? MaxProgressValue = null, SynchronizationContext UIContext = null, bool ShowAnimation = true)
|
internal BusyViewModel(string Message, string SubMessage = null, ulong? MaxProgressValue = null, SynchronizationContext UIContext = null, bool ShowAnimation = true, bool ShowRebootHelp = false)
|
||||||
{
|
{
|
||||||
LogFile.Log(Message);
|
LogFile.Log(Message);
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ namespace WPinternals
|
|||||||
this.Message = Message;
|
this.Message = Message;
|
||||||
this.SubMessage = SubMessage;
|
this.SubMessage = SubMessage;
|
||||||
this.ShowAnimation = ShowAnimation;
|
this.ShowAnimation = ShowAnimation;
|
||||||
|
this.ShowRebootHelp = ShowRebootHelp;
|
||||||
if (MaxProgressValue != null)
|
if (MaxProgressValue != null)
|
||||||
{
|
{
|
||||||
ProgressPercentage = 0;
|
ProgressPercentage = 0;
|
||||||
@@ -64,6 +65,11 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void SetShowRebootHelp(bool Value)
|
||||||
|
{
|
||||||
|
ShowRebootHelp = Value;
|
||||||
|
}
|
||||||
|
|
||||||
internal void SetProgress(ulong Value)
|
internal void SetProgress(ulong Value)
|
||||||
{
|
{
|
||||||
if (ProgressUpdater != null)
|
if (ProgressUpdater != null)
|
||||||
@@ -188,5 +194,21 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _ShowRebootHelp = false;
|
||||||
|
public bool ShowRebootHelp
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _ShowRebootHelp;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_ShowRebootHelp != value)
|
||||||
|
{
|
||||||
|
_ShowRebootHelp = value;
|
||||||
|
OnPropertyChanged("ShowRebootHelp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ namespace WPinternals
|
|||||||
|
|
||||||
internal void SetWorkingStatus(string Message, string SubMessage, ulong? MaxProgressValue, bool ShowAnimation = true, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
internal void SetWorkingStatus(string Message, string SubMessage, ulong? MaxProgressValue, bool ShowAnimation = true, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
||||||
{
|
{
|
||||||
ActivateSubContext(new BusyViewModel(Message, SubMessage, MaxProgressValue, UIContext: UIContext, ShowAnimation: ShowAnimation));
|
ActivateSubContext(new BusyViewModel(Message, SubMessage, MaxProgressValue, UIContext: UIContext, ShowAnimation: ShowAnimation, ShowRebootHelp: Status == WPinternalsStatus.WaitingForManualReset));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdateWorkingStatus(string Message, string SubMessage, ulong? CurrentProgressValue, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
internal void UpdateWorkingStatus(string Message, string SubMessage, ulong? CurrentProgressValue, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
||||||
@@ -147,6 +147,7 @@ namespace WPinternals
|
|||||||
LogFile.LogException(Ex);
|
LogFile.LogException(Ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Busy.SetShowRebootHelp(Status == WPinternalsStatus.WaitingForManualReset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,14 +192,16 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool AlreadyUnlocked = false;
|
||||||
if (DoUnlock)
|
if (DoUnlock)
|
||||||
{
|
{
|
||||||
NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||||
GPT GPT = FlashModel.ReadGPT();
|
GPT GPT = FlashModel.ReadGPT();
|
||||||
if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null))
|
if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null))
|
||||||
{
|
{
|
||||||
ExitMessage("Phone is already unlocked", null);
|
//ExitMessage("Phone is already unlocked", null);
|
||||||
return;
|
//return;
|
||||||
|
AlreadyUnlocked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,8 +239,10 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
if (DoFixBoot)
|
if (DoFixBoot)
|
||||||
await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
|
await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
|
||||||
else
|
else if (!AlreadyUnlocked)
|
||||||
await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
|
await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
|
||||||
|
else
|
||||||
|
await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -278,7 +282,18 @@ namespace WPinternals
|
|||||||
|
|
||||||
// If resources are not confirmed yet, then display view with device info and request for resources.
|
// If resources are not confirmed yet, then display view with device info and request for resources.
|
||||||
QualcommDownload Download = new QualcommDownload((QualcommSerial)PhoneNotifier.CurrentModel);
|
QualcommDownload Download = new QualcommDownload((QualcommSerial)PhoneNotifier.CurrentModel);
|
||||||
byte[] QualcommRootKeyHash = Download.GetRKH();
|
byte[] QualcommRootKeyHash;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QualcommRootKeyHash = Download.GetRKH();
|
||||||
|
}
|
||||||
|
catch (BadConnectionException)
|
||||||
|
{
|
||||||
|
// This is a Spec B device
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (RootKeyHash == null)
|
if (RootKeyHash == null)
|
||||||
RootKeyHash = QualcommRootKeyHash;
|
RootKeyHash = QualcommRootKeyHash;
|
||||||
else if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, QualcommRootKeyHash))
|
else if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, QualcommRootKeyHash))
|
||||||
|
|||||||
@@ -133,14 +133,14 @@ namespace WPinternals
|
|||||||
ExitSuccess("Bootloader restored successfully!");
|
ExitSuccess("Bootloader restored successfully!");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task LumiaV2UnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null)
|
internal static async Task LumiaV2UnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, bool ReUnlockDevice = false)
|
||||||
{
|
{
|
||||||
if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { };
|
if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { };
|
||||||
if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { };
|
if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { };
|
||||||
if (ExitSuccess == null) ExitSuccess = (m, s) => { };
|
if (ExitSuccess == null) ExitSuccess = (m, s) => { };
|
||||||
if (ExitFailure == null) ExitFailure = (m, s) => { };
|
if (ExitFailure == null) ExitFailure = (m, s) => { };
|
||||||
|
|
||||||
await LumiaUnlockBootloaderViewModel.LumiaUnlockUEFI(Notifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure);
|
await LumiaUnlockBootloaderViewModel.LumiaUnlockUEFI(Notifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure, ReUnlockDevice: ReUnlockDevice);
|
||||||
|
|
||||||
SetWorkingStatus("Booting phone...");
|
SetWorkingStatus("Booting phone...");
|
||||||
await Notifier.WaitForArrival();
|
await Notifier.WaitForArrival();
|
||||||
@@ -1625,7 +1625,7 @@ namespace WPinternals
|
|||||||
//
|
//
|
||||||
// Assumes phone in Flash mode
|
// Assumes phone in Flash mode
|
||||||
//
|
//
|
||||||
internal static async Task LumiaUnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, bool ExperimentalSpecBEFIESPUnlock = false, bool ExperimentalSpecAEFIESPUnlock = true)
|
internal static async Task LumiaUnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, bool ExperimentalSpecBEFIESPUnlock = false, bool ExperimentalSpecAEFIESPUnlock = true, bool ReUnlockDevice = false)
|
||||||
{
|
{
|
||||||
LogFile.BeginAction("UnlockBootloader");
|
LogFile.BeginAction("UnlockBootloader");
|
||||||
NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel;
|
NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel;
|
||||||
@@ -1705,8 +1705,30 @@ namespace WPinternals
|
|||||||
Partition BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP");
|
Partition BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP");
|
||||||
Partition EFIESP;
|
Partition EFIESP;
|
||||||
|
|
||||||
if (BACKUP_EFIESP == null)
|
if (BACKUP_EFIESP == null && !ReUnlockDevice)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Before:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | |
|
||||||
|
* | EFIESP |
|
||||||
|
* | Original |
|
||||||
|
* |___________________________________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | BACKUP_EFIESP | EFIESP |
|
||||||
|
* | Original | Unlocked |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
BACKUP_EFIESP = GPT.GetPartition("EFIESP");
|
BACKUP_EFIESP = GPT.GetPartition("EFIESP");
|
||||||
Guid OriginalPartitionTypeGuid = BACKUP_EFIESP.PartitionTypeGuid;
|
Guid OriginalPartitionTypeGuid = BACKUP_EFIESP.PartitionTypeGuid;
|
||||||
Guid OriginalPartitionGuid = BACKUP_EFIESP.PartitionGuid;
|
Guid OriginalPartitionGuid = BACKUP_EFIESP.PartitionGuid;
|
||||||
@@ -1724,6 +1746,43 @@ namespace WPinternals
|
|||||||
GPT.Partitions.Add(EFIESP);
|
GPT.Partitions.Add(EFIESP);
|
||||||
GPTChanged = true;
|
GPTChanged = true;
|
||||||
}
|
}
|
||||||
|
if (BACKUP_EFIESP == null && ReUnlockDevice)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Before:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | EFIESP BACKUP_EFIESP |
|
||||||
|
* | Unlocked Original |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | EFIESP | BACKUP_EFIESP |
|
||||||
|
* | Unlocked | Original |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
EFIESP = GPT.GetPartition("EFIESP");
|
||||||
|
EFIESP.LastSector = EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000
|
||||||
|
|
||||||
|
BACKUP_EFIESP = new Partition();
|
||||||
|
BACKUP_EFIESP.Name = "BACKUP_EFIESP";
|
||||||
|
BACKUP_EFIESP.Attributes = EFIESP.Attributes;
|
||||||
|
BACKUP_EFIESP.PartitionGuid = Guid.NewGuid();
|
||||||
|
BACKUP_EFIESP.PartitionTypeGuid = Guid.NewGuid();
|
||||||
|
BACKUP_EFIESP.FirstSector = EFIESP.LastSector + 1;
|
||||||
|
BACKUP_EFIESP.LastSector = BACKUP_EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000
|
||||||
|
GPT.Partitions.Add(BACKUP_EFIESP);
|
||||||
|
GPTChanged = true;
|
||||||
|
}
|
||||||
EFIESP = GPT.GetPartition("EFIESP");
|
EFIESP = GPT.GetPartition("EFIESP");
|
||||||
if ((UInt64)UnlockedEFIESP.Length > (EFIESP.SizeInSectors * 0x200))
|
if ((UInt64)UnlockedEFIESP.Length > (EFIESP.SizeInSectors * 0x200))
|
||||||
{
|
{
|
||||||
@@ -1896,7 +1955,24 @@ namespace WPinternals
|
|||||||
byte[] BackupUnlockedEFIESP = new byte[UnlockedEFIESP.Length];
|
byte[] BackupUnlockedEFIESP = new byte[UnlockedEFIESP.Length];
|
||||||
Buffer.BlockCopy(BackupEFIESP, 0, BackupUnlockedEFIESP, 0, BackupEFIESP.Length);
|
Buffer.BlockCopy(BackupEFIESP, 0, BackupUnlockedEFIESP, 0, BackupEFIESP.Length);
|
||||||
|
|
||||||
LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, BackupUnlockedEFIESP, IsSpecB);
|
try
|
||||||
|
{
|
||||||
|
LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, BackupUnlockedEFIESP, IsSpecB);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogFile.Log("Exception: " + ex.GetType().ToString(), LogType.FileOnly);
|
||||||
|
LogFile.Log("It seems that the backed up EFIESP partition is invalid.", LogType.FileOnly);
|
||||||
|
LogFile.Log("Using the FFU partition as a failsafe.", LogType.FileOnly);
|
||||||
|
|
||||||
|
BackupEFIESP = ProfileFFU.GetPartition("EFIESP");
|
||||||
|
|
||||||
|
// Copy the backed up unlocked EFIESP for future use
|
||||||
|
BackupUnlockedEFIESP = new byte[UnlockedEFIESP.Length];
|
||||||
|
Buffer.BlockCopy(BackupEFIESP, 0, BackupUnlockedEFIESP, 0, BackupEFIESP.Length);
|
||||||
|
|
||||||
|
LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, BackupUnlockedEFIESP, IsSpecB);
|
||||||
|
}
|
||||||
|
|
||||||
SetWorkingStatus("Boot optimization...", null, null);
|
SetWorkingStatus("Boot optimization...", null, null);
|
||||||
|
|
||||||
@@ -1922,15 +1998,72 @@ namespace WPinternals
|
|||||||
|
|
||||||
((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext();
|
((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext();
|
||||||
|
|
||||||
// EFIESP is appended at the end of the GPT
|
UInt32 OriginalEfiespFirstSector;
|
||||||
// BACKUP_EFIESP is at original location in GPT
|
if (!ReUnlockDevice)
|
||||||
Partition EFIESP = GPT.GetPartition("EFIESP");
|
{
|
||||||
UInt32 OriginalEfiespFirstSector = (UInt32)BACKUP_EFIESP.FirstSector;
|
/*
|
||||||
BACKUP_EFIESP.Name = "EFIESP";
|
* Before:
|
||||||
BACKUP_EFIESP.LastSector = OriginalEfiespLastSector;
|
*
|
||||||
BACKUP_EFIESP.PartitionGuid = EFIESP.PartitionGuid;
|
* ___________________________________________
|
||||||
BACKUP_EFIESP.PartitionTypeGuid = EFIESP.PartitionTypeGuid;
|
* | | |
|
||||||
GPT.Partitions.Remove(EFIESP);
|
* | BACKUP_EFIESP | EFIESP |
|
||||||
|
* | Original | Unlocked |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | EFIESP BACKUP_EFIESP |
|
||||||
|
* | Unlocked Original |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// EFIESP is appended at the end of the GPT
|
||||||
|
// BACKUP_EFIESP is at original location in GPT
|
||||||
|
Partition EFIESP = GPT.GetPartition("EFIESP");
|
||||||
|
OriginalEfiespFirstSector = (UInt32)BACKUP_EFIESP.FirstSector;
|
||||||
|
BACKUP_EFIESP.Name = "EFIESP";
|
||||||
|
BACKUP_EFIESP.LastSector = OriginalEfiespLastSector;
|
||||||
|
BACKUP_EFIESP.PartitionGuid = EFIESP.PartitionGuid;
|
||||||
|
BACKUP_EFIESP.PartitionTypeGuid = EFIESP.PartitionTypeGuid;
|
||||||
|
GPT.Partitions.Remove(EFIESP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Before:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | EFIESP | BACKUP_EFIESP |
|
||||||
|
* | Unlocked | Original |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After:
|
||||||
|
*
|
||||||
|
* ___________________________________________
|
||||||
|
* | | |
|
||||||
|
* | EFIESP BACKUP_EFIESP |
|
||||||
|
* | Unlocked Original |
|
||||||
|
* |____________________|______________________|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// EFIESP is expended to its full size
|
||||||
|
// BACKUP_EFIESP is removed
|
||||||
|
Partition EFIESP = GPT.GetPartition("EFIESP");
|
||||||
|
OriginalEfiespFirstSector = (UInt32)EFIESP.FirstSector;
|
||||||
|
EFIESP.LastSector = OriginalEfiespLastSector;
|
||||||
|
GPT.Partitions.Remove(BACKUP_EFIESP);
|
||||||
|
}
|
||||||
|
|
||||||
Partition IsUnlockedFlag = GPT.GetPartition("IS_UNLOCKED");
|
Partition IsUnlockedFlag = GPT.GetPartition("IS_UNLOCKED");
|
||||||
if (IsUnlockedFlag == null)
|
if (IsUnlockedFlag == null)
|
||||||
|
|||||||
@@ -597,13 +597,22 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
||||||
{
|
{
|
||||||
|
bool FailedToStartProgrammer = false;
|
||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
||||||
await Sahara.Reset(ProgrammerPath);
|
try
|
||||||
await Notifier.WaitForArrival();
|
{
|
||||||
|
await Sahara.Reset(ProgrammerPath);
|
||||||
|
await Notifier.WaitForArrival();
|
||||||
|
}
|
||||||
|
catch (BadConnectionException)
|
||||||
|
{
|
||||||
|
FailedToStartProgrammer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (ProgrammerPath == null || FailedToStartProgrammer)
|
||||||
{
|
{
|
||||||
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
||||||
|
|
||||||
@@ -623,16 +632,39 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
AutoEmergencyReset = false;
|
AutoEmergencyReset = false;
|
||||||
|
|
||||||
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
if (!FailedToStartProgrammer)
|
||||||
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
{
|
||||||
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
||||||
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
SetWorkingStatus("You need to manually reset your phone now!", "The phone is in emergency mode and you didn't provide an emergency programmer. This phone also doesn't seem to reboot after a timeout, so you got to help a bit. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.", null, false, WPinternalsStatus.WaitingForManualReset);
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
|
"The phone is in emergency mode and you didn't provide an emergency programmer." +
|
||||||
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
|
" The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.",
|
||||||
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogFile.Log("The phone is in emergency mode and we couldn't start the emergency programmer", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
|
"The phone is in emergency mode and we couldn't start the emergency programmer." +
|
||||||
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
|
" The unlock-sequence will resume automatically.",
|
||||||
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
|
}
|
||||||
await Notifier.WaitForRemoval();
|
await Notifier.WaitForRemoval();
|
||||||
|
|
||||||
UpdateWorkingStatus("Initializing flash...", null, null);
|
UpdateWorkingStatus("Initializing flash...", null, null);
|
||||||
@@ -1221,13 +1253,22 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
||||||
{
|
{
|
||||||
|
bool FailedToStartProgrammer = false;
|
||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
||||||
await Sahara.Reset(ProgrammerPath);
|
try
|
||||||
await Notifier.WaitForArrival();
|
{
|
||||||
|
await Sahara.Reset(ProgrammerPath);
|
||||||
|
await Notifier.WaitForArrival();
|
||||||
|
}
|
||||||
|
catch (BadConnectionException)
|
||||||
|
{
|
||||||
|
FailedToStartProgrammer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (ProgrammerPath == null || FailedToStartProgrammer)
|
||||||
{
|
{
|
||||||
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
||||||
|
|
||||||
@@ -1246,16 +1287,39 @@ namespace WPinternals
|
|||||||
if (!AutoEmergencyReset || Timeout)
|
if (!AutoEmergencyReset || Timeout)
|
||||||
{
|
{
|
||||||
AutoEmergencyReset = false;
|
AutoEmergencyReset = false;
|
||||||
|
if (!FailedToStartProgrammer)
|
||||||
|
{
|
||||||
|
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
"The phone is in emergency mode and you didn't provide an emergency programmer." +
|
||||||
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
" The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.",
|
||||||
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogFile.Log("The phone is in emergency mode and we couldn't start the emergency programmer", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
UpdateWorkingStatus("You need to manually reset your phone now!", "The phone is in emergency mode and you didn't provide an emergency programmer. This phone also doesn't seem to reboot after a timeout, so you got to help a bit. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.", null, WPinternalsStatus.WaitingForManualReset);
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
|
"The phone is in emergency mode and we couldn't start the emergency programmer." +
|
||||||
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
|
" The unlock-sequence will resume automatically.",
|
||||||
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
|
}
|
||||||
|
|
||||||
await Notifier.WaitForRemoval();
|
await Notifier.WaitForRemoval();
|
||||||
|
|
||||||
@@ -1442,12 +1506,22 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download)
|
||||||
{
|
{
|
||||||
|
bool FailedToStartProgrammer = false;
|
||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new QualcommSahara((QualcommSerial)Notifier.CurrentModel);
|
||||||
await Sahara.Reset(ProgrammerPath);
|
try
|
||||||
|
{
|
||||||
|
await Sahara.Reset(ProgrammerPath);
|
||||||
|
await Notifier.WaitForArrival();
|
||||||
|
}
|
||||||
|
catch (BadConnectionException)
|
||||||
|
{
|
||||||
|
FailedToStartProgrammer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (ProgrammerPath == null || FailedToStartProgrammer)
|
||||||
{
|
{
|
||||||
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
((QualcommSerial)Notifier.CurrentModel).Close(); // Prevent "Resource in use";
|
||||||
|
|
||||||
@@ -1467,15 +1541,39 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
AutoEmergencyReset = false;
|
AutoEmergencyReset = false;
|
||||||
|
|
||||||
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
if (!FailedToStartProgrammer)
|
||||||
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
{
|
||||||
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
LogFile.Log("The phone is in emergency mode and you didn't provide an emergency programmer", LogType.ConsoleOnly);
|
||||||
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("To prevent this, provide an emergency programmer next time you will unlock a bootloader", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
SetWorkingStatus("You need to manually reset your phone now!", "The phone is in emergency mode and you didn't provide an emergency programmer. This phone also doesn't seem to reboot after a timeout, so you got to help a bit. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.", null, false, WPinternalsStatus.WaitingForManualReset);
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
|
"The phone is in emergency mode and you didn't provide an emergency programmer." +
|
||||||
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
|
" The unlock-sequence will resume automatically. To prevent this, provide an emergency programmer next time you will unlock a bootloader.",
|
||||||
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogFile.Log("The phone is in emergency mode and we couldn't start the emergency programmer", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("This phone also doesn't seem to reboot after a timeout, so you got to help a bit", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly);
|
||||||
|
LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly);
|
||||||
|
|
||||||
|
SetWorkingStatus("You need to manually reset your phone now!",
|
||||||
|
"The phone is in emergency mode and we couldn't start the emergency programmer." +
|
||||||
|
" This phone also doesn't seem to reboot after a timeout, so you got to help a bit." +
|
||||||
|
" Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates." +
|
||||||
|
" The unlock-sequence will resume automatically.",
|
||||||
|
null, false, WPinternalsStatus.WaitingForManualReset);
|
||||||
|
}
|
||||||
|
|
||||||
await Notifier.WaitForRemoval();
|
await Notifier.WaitForRemoval();
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
</TextBlock.Foreground>
|
</TextBlock.Foreground>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock TextAlignment="Center" HorizontalAlignment="Center" Text="{Binding SubMessage}" Visibility="{Binding Path=SubMessage, Converter={StaticResource ObjectToVisibilityConverter}}" Margin="0,10,0,0" TextWrapping="WrapWithOverflow" />
|
<TextBlock TextAlignment="Center" HorizontalAlignment="Center" Text="{Binding SubMessage}" Visibility="{Binding Path=SubMessage, Converter={StaticResource ObjectToVisibilityConverter}}" Margin="0,10,0,0" TextWrapping="WrapWithOverflow" />
|
||||||
|
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,10,0,0" Visibility="{Binding Path=ShowRebootHelp, Converter={StaticResource VisibilityConverter}}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Image x:Name="RebootImage" Source="pack://application:,,,/PhoneReboot.png" Height="200"/>
|
||||||
|
<StackPanel Margin="20,0,0,0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<TextBlock TextAlignment="Center" HorizontalAlignment="Center" Text="To reboot your device manually, press the power and volume down button as shown in the picture at the same time until the phone vibrates, then release both buttons. The phone will get automatically detected by the tool and the process will continue where it left off." TextWrapping="WrapWithOverflow" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Logo.png" />
|
<Resource Include="Logo.png" />
|
||||||
|
<Resource Include="PhoneReboot.png" />
|
||||||
<Resource Include="WPinternals.ico" />
|
<Resource Include="WPinternals.ico" />
|
||||||
<Resource Include="aerobusy.gif" />
|
<Resource Include="aerobusy.gif" />
|
||||||
<Resource Include="Logo-Small.png" />
|
<Resource Include="Logo-Small.png" />
|
||||||
@@ -44,6 +45,10 @@
|
|||||||
<Content Remove="SBA" />
|
<Content Remove="SBA" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="PhoneReboot.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="PatchDefinitions.xml" />
|
<EmbeddedResource Include="PatchDefinitions.xml" />
|
||||||
<EmbeddedResource Include="SB" />
|
<EmbeddedResource Include="SB" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_LastSelectedProfileId>G:\Projects\Misc\WPinternals\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
<_LastSelectedProfileId>G:\Projects\WPinternals\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="App.xaml">
|
<Page Update="App.xaml">
|
||||||
|
|||||||
Reference in New Issue
Block a user