From 21760f8014e55081bd9bf0155235599fc2d99fe1 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 18 Jan 2023 22:59:37 +0000 Subject: [PATCH 1/2] [Xamarin.Android.Build.Tasks] Fix issue where app will not install Fixes https://i.azdo.io/1398544 It is quite common for users to switch from Debug to Release mode in order to test the app. However if the `Release` build is using a custom signing keystore you will generally see this warning and error. ``` warning MSB6006: "adb" exited with code 1. [BT : 1.8.1] error : Installation of the app failed. ``` This is not entirely helpful. Since you need to dig into the log to figure out what the actual issue is. The warning is produced when we try to run `adb uninstall -k `. ``` adb uninstall -k com.xamarin.foo (TaskId:361) The -k option uninstalls the application while retaining the data/cache. (TaskId:361) At the moment, there is no way to remove the remaining data. (TaskId:361) You will have to reinstall the application with the same signature, and fully uninstall it. (TaskId:361) If you truly wish to continue, execute 'adb shell cmd package uninstall -k'. ``` We are not entirely sure why the application gets into this state. But once it does you have to completely uninstall it. We currently ignore this error but that then results in the following. ``` Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.xamarin.toggledebugreleasewithsigning signatures do not match newer version; ignoring! ``` This is because the apps have different signing keystores. As a result they are incompatible. The only option once you get this issue is to uninstall the app manually and try again. However the error messaging is not obvious so users generally have no idea what to do. So lets fix a few things wrong in this area. Firstly lets introduce a new task `AndroidAdb` which is responsible for making the calls to `adb`. We currently use `Exec` which means its hard to make any customizations around error messaging. We will check the result of `AndroidAdb` when calling `adb uninstall -k ` and if we get message we will automatically fall back on calling `adb shell cmd package uninstall`. This will completely remove the app from the device and will allow the later `bundletool` call to work. We have also updated `InstallApkSet` to look for error messages from `bundletool` and report them, so users will get better information. So we now get error messages like ``` [BT : 1.8.1] error : Installation of the app failed. [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: The APKs have been extracted in the directory: /var/folders/5p/10yqy2kx6r9dnmnxh_nt6s0r0000gn/T/1389125984700138671 [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: 01:54:24 E/SplitApkInstallerBase: Failed to commit install session 1426763565 with command cmd package install-commit 1426763565. Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.xamarin.toggledebugreleasewithsigning signatures do not match newer version; ignoring! [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: [BT:1.8.1] Error: Installation of the app failed. [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: com.android.tools.build.bundletool.model.exceptions.CommandExecutionException [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: Installation of the app failed. [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.model.exceptions.InternalExceptionBuilder.build(InternalExceptionBuilder.java:57) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.DdmlibDevice.installApks(DdmlibDevice.java:192) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.commands.InstallApksCommand.lambda$execute$2(InstallApksCommand.java:214) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.AdbRunner.run(AdbRunner.java:81) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.AdbRunner.run(AdbRunner.java:43) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.commands.InstallApksCommand.execute(InstallApksCommand.java:214) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.BundleToolMain.main(BundleToolMain.java:91) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.BundleToolMain.main(BundleToolMain.java:49) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: Caused by: com.android.ddmlib.InstallException: Failed to commit install session 1426763565 with command cmd package install-commit 1426763565. Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.xamarin.toggledebugreleasewithsigning signatures do not match newer version; ignoring! [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.SplitApkInstallerBase.installCommit(SplitApkInstallerBase.java:99) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.SplitApkInstaller.install(SplitApkInstaller.java:85) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.internal.DeviceImpl.installPackages(DeviceImpl.java:1166) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.DdmlibDevice.installApks(DdmlibDevice.java:176) [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: ... 6 more [ToggleDebugReleaseWithSigning/UnnamedProject.csproj] obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: ``` Unit Test also added. --- .../Tasks/AndroidAdb.cs | 57 +++++++++++++++++++ .../Tasks/InstallApkSet.cs | 28 +++++++++ .../Tasks/JavaToolTask.cs | 27 ++++++++- .../Xamarin.Android.Common.targets | 52 ++++++++++------- .../Tests/InstallTests.cs | 45 +++++++++++++++ 5 files changed, 186 insertions(+), 23 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/AndroidAdb.cs diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidAdb.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidAdb.cs new file mode 100644 index 00000000000..9a2815ba8b9 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidAdb.cs @@ -0,0 +1,57 @@ +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System.IO; +using System.Text; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks { + + public class AndroidAdb : AndroidToolTask + { + public override string TaskPrefix => "AADB"; + + public string AdbTarget { get; set; } + public string Command { get; set; } + public string Arguments { get; set; } + + public bool IgnoreErrors { get; set; } = false; + + [Output] + public bool Result { get; set; } = true; + + protected override string ToolName => OS.IsWindows ? "adb.exe" : "adb"; + + protected override string GenerateFullPathToTool () + { + return Path.Combine (ToolPath, ToolExe); + } + + //adb $(AdbTarget) uninstall -k "$(_AndroidPackage)" + //adb $(AdbTarget) uninstall $(_AndroidPackage) + //adb $(AdbTarget) install -r "$(ApkFileSigned)" + //adb $(AdbTarget) shell cmd package uninstall -k $(_AndroidPackage) + protected override string GenerateCommandLineCommands () + { + var sb = new StringBuilder (); + if (!string.IsNullOrEmpty (AdbTarget)) + sb.Append ($" {AdbTarget} "); + sb.AppendFormat ("{0} {1}", Command, Arguments); + return sb.ToString (); + } + + protected override bool HandleTaskExecutionErrors () + { + if (!Result) + return true; + return base.HandleTaskExecutionErrors (); + } + + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) + { + if (singleLine.Contains ("adb shell cmd package uninstall")) + Result = false; + base.LogEventsFromTextOutput (singleLine, messageImportance); + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs b/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs index 022f8739f0c..af1957258ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs @@ -1,6 +1,9 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks @@ -14,6 +17,8 @@ public class InstallApkSet : BundleToolAdbTask { public override string TaskPrefix => "IAS"; + public override string DefaultErrorCode => "BT0000"; + [Required] public string ApkSet { get; set; } @@ -37,5 +42,28 @@ internal override CommandLineBuilder GetCommandLineBuilder () return cmd; } + + const string InstallErrorRegExString = @"(?com.android.tools.build.bundletool.model.exceptions.CommandExecutionException+):(?.+)"; + static readonly Regex installErrorRegEx = new Regex (InstallErrorRegExString, RegexOptions.Compiled); + + protected override IEnumerable GetCustomExpressions () + { + yield return installErrorRegEx; + } + + internal override bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap) + { + var match = installErrorRegEx.Match (singleLine); + if (match.Success) { + // error message + var error = match.Groups ["error"].Value; + var exception = match.Groups ["exception"].Value; + SetFileLineAndColumn (ApkSet, line: 1, column: 0); + AppendTextToErrorText (exception); + AppendTextToErrorText (error); + return LogFromException (exception, error);; + } + return base.ProcessOutput (singleLine, assemblyMap); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs b/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs index fdc90873533..50803b3f4b4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs @@ -128,7 +128,7 @@ protected override string GenerateFullPathToTool () return Path.Combine (ToolPath, ToolExe); } - bool LogFromException (string exception, string error) { + protected bool LogFromException (string exception, string error) { switch (exception) { case "java.lang.OutOfMemoryError": Log.LogCodedError ("XA5213", Properties.Resources.XA5213, ToolName, GenerateCommandLineCommands ()); @@ -138,7 +138,7 @@ bool LogFromException (string exception, string error) { } } - bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap) + internal virtual bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap) { var match = CodeErrorRegEx.Match (singleLine); var exceptionMatch = ExceptionRegEx.Match (singleLine); @@ -197,6 +197,23 @@ protected virtual void GetLineNumber (string match, out int line, out int column column = 0; } + protected virtual IEnumerable GetCustomExpressions () + { + return Enumerable.Empty (); + } + + protected void SetFileLineAndColumn (string file, int line = 0, int column = 0) + { + this.file = file; + this.line = line; + this.column = column; + } + + protected void AppendTextToErrorText (string text) + { + errorText.AppendLine (text); + } + protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) { errorLines.Add (singleLine); @@ -207,7 +224,11 @@ protected override void LogEventsFromTextOutput (string singleLine, MessageImpor } var match = CodeErrorRegEx.Match (singleLine); var exceptionMatch = ExceptionRegEx.Match (singleLine); - foundError = foundError || match.Success || exceptionMatch.Success; + var customMatch = false; + foreach (var customRegex in GetCustomExpressions ()) { + customMatch |= customRegex.Match (singleLine).Success; + } + foundError = foundError || match.Success || exceptionMatch.Success || customMatch; } } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 70a86d1ecab..8606402f9f1 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -17,6 +17,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -2658,21 +2659,12 @@ because xbuild doesn't support framework reference assemblies. <_DeployCommand>"$(AdbToolPath)adb" $(AdbTarget) install -r "$(ApkFileSigned)" - - - - - - <_AdbError Include="The command `$(_DeployCommand)` exited with code $(_DeployExitCode):" /> - <_AdbError Include="@(_DeployConsoleOutput->' %(Identity)')" /> - - @@ -2704,11 +2696,25 @@ because xbuild doesn't support framework reference assemblies. <_UninstallCommand>"$(AdbToolPath)adb" $(AdbTarget) uninstall -k "$(_AndroidPackage)" - + + + - + data + }); + proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "storepass.txt") { + TextContent = () => password.Replace ("file:", string.Empty), + Encoding = Encoding.ASCII, + }); + proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "keypass.txt") { + TextContent = () => password.Replace ("file:", string.Empty), + Encoding = Encoding.ASCII, + }); + + using (var builder = CreateApkBuilder (path)) { + Assert.IsTrue (builder.Install (proj), "Install should have succeeded."); + //Now toggle to Release + proj.IsRelease = true; + Assert.IsTrue (builder.Install (proj), "Second install should have succeeded."); + proj.IsRelease = false; + Assert.IsTrue (builder.Install (proj), "Third install should have succeeded."); + Assert.IsTrue (builder.Uninstall (proj), "unnstall should have succeeded."); + } + } + [Test] public void LoggingPropsShouldCreateOverrideDirForRelease () { From 9bb8076269e2d40d4bb8efc1b1a543ada1b7a7c0 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Fri, 27 Jan 2023 10:31:32 +0000 Subject: [PATCH 2/2] Fix Regex --- src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs b/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs index af1957258ec..2fa52781738 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs @@ -43,7 +43,7 @@ internal override CommandLineBuilder GetCommandLineBuilder () return cmd; } - const string InstallErrorRegExString = @"(?com.android.tools.build.bundletool.model.exceptions.CommandExecutionException+):(?.+)"; + const string InstallErrorRegExString = @"(?com.android.tools.build.bundletool.model.exceptions.CommandExecutionException):(?.+)"; static readonly Regex installErrorRegEx = new Regex (InstallErrorRegExString, RegexOptions.Compiled); protected override IEnumerable GetCustomExpressions ()