Skip to content

Commit 1d7092d

Browse files
authored
[Xamarin.Android.Build.Tasks] Fix issue where app will not install (dotnet#7719)
Fixes: https://dev.azure.com/DevDiv/DevDiv/_workitems/edit/1398544 It is quite common for users to switch between Debug and Release configurations 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 often need to dig into the diagnostic log to figure out what the actual issue is. This warning is produced when we try to run `adb uninstall -k @PACKAGE_NAME@`: adb uninstall -k com.xamarin.example The -k option uninstalls the application while retaining the data/cache. At the moment, there is no way to remove the remaining data. You will have to reinstall the application with the same signature, and fully uninstall it. 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: Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.xamarin.example signatures do not match newer version; ignoring! This is because the apps used 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. Fix a few things in this area. Introduce a new `<AndroidAdb/>` task which is responsible for making the calls to `adb`. We previously used `<Exec/>`, which made it hard to make any customizations around error messaging. We will check the result of the `<AndroidAdb/>` task when calling `adb uninstall -k @PACKAGE_NAME@` and if the output contains `adb shell cmd package uninstall` then we will automatically call `adb shell cmd package uninstall @PACKAGE_NAME@`. This will completely remove the app from the device and will allow the later `bundletool` invocation to work. We have also updated `<InstallApkSet/>` to look for error messages from `bundletool` and report them, so users will get better information. We now generate error messages like: [BT : 1.8.1] error : Installation of the app failed. 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 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! obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: [BT:1.8.1] Error: Installation of the app failed. obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: com.android.tools.build.bundletool.model.exceptions.CommandExecutionException obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: Installation of the app failed. obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.model.exceptions.InternalExceptionBuilder.build(InternalExceptionBuilder.java:57) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.DdmlibDevice.installApks(DdmlibDevice.java:192) 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) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.AdbRunner.run(AdbRunner.java:81) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.AdbRunner.run(AdbRunner.java:43) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.commands.InstallApksCommand.execute(InstallApksCommand.java:214) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.BundleToolMain.main(BundleToolMain.java:91) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.BundleToolMain.main(BundleToolMain.java:49) 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! obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.SplitApkInstallerBase.installCommit(SplitApkInstallerBase.java:99) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.SplitApkInstaller.install(SplitApkInstaller.java:85) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.ddmlib.internal.DeviceImpl.installPackages(DeviceImpl.java:1166) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: at com.android.tools.build.bundletool.device.DdmlibDevice.installApks(DdmlibDevice.java:176) obj/Release/android/bin/com.xamarin.toggledebugreleasewithsigning.apks : java error BT0000: ... 6 more Unit Test are added.
1 parent 35db527 commit 1d7092d

File tree

5 files changed

+186
-23
lines changed

5 files changed

+186
-23
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Microsoft.Android.Build.Tasks;
2+
using Microsoft.Build.Framework;
3+
using Microsoft.Build.Utilities;
4+
using System.IO;
5+
using System.Text;
6+
using Xamarin.Android.Tools;
7+
8+
namespace Xamarin.Android.Tasks {
9+
10+
public class AndroidAdb : AndroidToolTask
11+
{
12+
public override string TaskPrefix => "AADB";
13+
14+
public string AdbTarget { get; set; }
15+
public string Command { get; set; }
16+
public string Arguments { get; set; }
17+
18+
public bool IgnoreErrors { get; set; } = false;
19+
20+
[Output]
21+
public bool Result { get; set; } = true;
22+
23+
protected override string ToolName => OS.IsWindows ? "adb.exe" : "adb";
24+
25+
protected override string GenerateFullPathToTool ()
26+
{
27+
return Path.Combine (ToolPath, ToolExe);
28+
}
29+
30+
//adb $(AdbTarget) uninstall -k &quot;$(_AndroidPackage)&quot;
31+
//adb $(AdbTarget) uninstall $(_AndroidPackage)
32+
//adb $(AdbTarget) install -r &quot;$(ApkFileSigned)&quot;
33+
//adb $(AdbTarget) shell cmd package uninstall -k $(_AndroidPackage)
34+
protected override string GenerateCommandLineCommands ()
35+
{
36+
var sb = new StringBuilder ();
37+
if (!string.IsNullOrEmpty (AdbTarget))
38+
sb.Append ($" {AdbTarget} ");
39+
sb.AppendFormat ("{0} {1}", Command, Arguments);
40+
return sb.ToString ();
41+
}
42+
43+
protected override bool HandleTaskExecutionErrors ()
44+
{
45+
if (!Result)
46+
return true;
47+
return base.HandleTaskExecutionErrors ();
48+
}
49+
50+
protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance)
51+
{
52+
if (singleLine.Contains ("adb shell cmd package uninstall"))
53+
Result = false;
54+
base.LogEventsFromTextOutput (singleLine, messageImportance);
55+
}
56+
}
57+
}

src/Xamarin.Android.Build.Tasks/Tasks/InstallApkSet.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using Microsoft.Build.Framework;
22
using Microsoft.Build.Utilities;
3+
using System.Collections.Generic;
34
using System.IO;
5+
using System.Linq;
6+
using System.Text.RegularExpressions;
47
using Xamarin.Android.Tools;
58

69
namespace Xamarin.Android.Tasks
@@ -14,6 +17,8 @@ public class InstallApkSet : BundleToolAdbTask
1417
{
1518
public override string TaskPrefix => "IAS";
1619

20+
public override string DefaultErrorCode => "BT0000";
21+
1722
[Required]
1823
public string ApkSet { get; set; }
1924

@@ -37,5 +42,28 @@ internal override CommandLineBuilder GetCommandLineBuilder ()
3742

3843
return cmd;
3944
}
45+
46+
const string InstallErrorRegExString = @"(?<exception>com.android.tools.build.bundletool.model.exceptions.CommandExecutionException):(?<error>.+)";
47+
static readonly Regex installErrorRegEx = new Regex (InstallErrorRegExString, RegexOptions.Compiled);
48+
49+
protected override IEnumerable<Regex> GetCustomExpressions ()
50+
{
51+
yield return installErrorRegEx;
52+
}
53+
54+
internal override bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap)
55+
{
56+
var match = installErrorRegEx.Match (singleLine);
57+
if (match.Success) {
58+
// error message
59+
var error = match.Groups ["error"].Value;
60+
var exception = match.Groups ["exception"].Value;
61+
SetFileLineAndColumn (ApkSet, line: 1, column: 0);
62+
AppendTextToErrorText (exception);
63+
AppendTextToErrorText (error);
64+
return LogFromException (exception, error);;
65+
}
66+
return base.ProcessOutput (singleLine, assemblyMap);
67+
}
4068
}
4169
}

src/Xamarin.Android.Build.Tasks/Tasks/JavaToolTask.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ protected override string GenerateFullPathToTool ()
128128
return Path.Combine (ToolPath, ToolExe);
129129
}
130130

131-
bool LogFromException (string exception, string error) {
131+
protected bool LogFromException (string exception, string error) {
132132
switch (exception) {
133133
case "java.lang.OutOfMemoryError":
134134
Log.LogCodedError ("XA5213", Properties.Resources.XA5213, ToolName, GenerateCommandLineCommands ());
@@ -138,7 +138,7 @@ bool LogFromException (string exception, string error) {
138138
}
139139
}
140140

141-
bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap)
141+
internal virtual bool ProcessOutput (string singleLine, AssemblyIdentityMap assemblyMap)
142142
{
143143
var match = CodeErrorRegEx.Match (singleLine);
144144
var exceptionMatch = ExceptionRegEx.Match (singleLine);
@@ -197,6 +197,23 @@ protected virtual void GetLineNumber (string match, out int line, out int column
197197
column = 0;
198198
}
199199

200+
protected virtual IEnumerable<Regex> GetCustomExpressions ()
201+
{
202+
return Enumerable.Empty<Regex> ();
203+
}
204+
205+
protected void SetFileLineAndColumn (string file, int line = 0, int column = 0)
206+
{
207+
this.file = file;
208+
this.line = line;
209+
this.column = column;
210+
}
211+
212+
protected void AppendTextToErrorText (string text)
213+
{
214+
errorText.AppendLine (text);
215+
}
216+
200217
protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
201218
{
202219
errorLines.Add (singleLine);
@@ -207,7 +224,11 @@ protected override void LogEventsFromTextOutput (string singleLine, MessageImpor
207224
}
208225
var match = CodeErrorRegEx.Match (singleLine);
209226
var exceptionMatch = ExceptionRegEx.Match (singleLine);
210-
foundError = foundError || match.Success || exceptionMatch.Success;
227+
var customMatch = false;
228+
foreach (var customRegex in GetCustomExpressions ()) {
229+
customMatch |= customRegex.Match (singleLine).Success;
230+
}
231+
foundError = foundError || match.Success || exceptionMatch.Success || customMatch;
211232
}
212233
}
213234
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
1717

1818
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="_WriteLockFile">
1919
<UsingTask TaskName="Xamarin.Android.Tasks.RemoveUnknownFiles" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
20+
<UsingTask TaskName="Xamarin.Android.Tasks.AndroidAdb" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
2021
<UsingTask TaskName="Xamarin.Android.Tasks.AndroidComputeResPaths" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
2122
<UsingTask TaskName="Xamarin.Android.Tasks.AndroidSignPackage" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
2223
<UsingTask TaskName="Xamarin.Android.Tasks.AndroidCreateDebugKey" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
@@ -2658,21 +2659,12 @@ because xbuild doesn't support framework reference assemblies.
26582659
<PropertyGroup>
26592660
<_DeployCommand>&quot;$(AdbToolPath)adb&quot; $(AdbTarget) install -r &quot;$(ApkFileSigned)&quot;</_DeployCommand>
26602661
</PropertyGroup>
2661-
<Exec
2662-
ContinueOnError="True"
2663-
Command="$(_DeployCommand)"
2664-
ConsoleToMSBuild="True">
2665-
<Output TaskParameter="ExitCode" PropertyName="_DeployExitCode" />
2666-
<Output TaskParameter="ConsoleOutput" ItemName="_DeployConsoleOutput" />
2667-
</Exec>
2668-
<ItemGroup>
2669-
<_AdbError Include="The command `$(_DeployCommand)` exited with code $(_DeployExitCode):" />
2670-
<_AdbError Include="@(_DeployConsoleOutput->' %(Identity)')" />
2671-
</ItemGroup>
2672-
<Error
2673-
Condition=" '$(_DeployExitCode)' != '0' "
2674-
Code="ADB0000"
2675-
Text="@(_AdbError, '%0a')"
2662+
<AndroidAdb
2663+
ToolExe="$(AdbToolExe)"
2664+
ToolPath="$(AdbToolPath)"
2665+
AdbTarget="$(AdbTarget)"
2666+
Command="install"
2667+
Arguments="-r &quot;$(ApkFileSigned)&quot;"
26762668
/>
26772669
</Target>
26782670

@@ -2704,11 +2696,25 @@ because xbuild doesn't support framework reference assemblies.
27042696
<PropertyGroup>
27052697
<_UninstallCommand>&quot;$(AdbToolPath)adb&quot; $(AdbTarget) uninstall -k &quot;$(_AndroidPackage)&quot;</_UninstallCommand>
27062698
</PropertyGroup>
2707-
<Exec
2708-
ContinueOnError="True"
2709-
Command="$(_UninstallCommand)"
2710-
ConsoleToMSBuild="True"
2699+
<AndroidAdb
27112700
Condition=" '$(EmbedAssembliesIntoApk)' == 'true' "
2701+
ContinueOnError="True"
2702+
ToolExe="$(AdbToolExe)"
2703+
ToolPath="$(AdbToolPath)"
2704+
AdbTarget="$(AdbTarget)"
2705+
Command="uninstall"
2706+
Arguments="-k $(_AndroidPackage)"
2707+
>
2708+
<Output TaskParameter="Result" PropertyName="_UninstallResult" />
2709+
</AndroidAdb>
2710+
<AndroidAdb
2711+
Condition=" '$(EmbedAssembliesIntoApk)' == 'true' And '$(_UninstallResult)' == 'false' "
2712+
ContinueOnError="True"
2713+
ToolExe="$(AdbToolExe)"
2714+
ToolPath="$(AdbToolPath)"
2715+
AdbTarget="$(AdbTarget)"
2716+
Command="shell cmd package uninstall"
2717+
Arguments="$(_AndroidPackage)"
27122718
/>
27132719
<InstallApkSet
27142720
ToolPath="$(JavaToolPath)"
@@ -2728,7 +2734,13 @@ because xbuild doesn't support framework reference assemblies.
27282734
</Target>
27292735

27302736
<Target Name="_Uninstall">
2731-
<Exec Command="&quot;$(AdbToolPath)adb&quot; $(AdbTarget) uninstall $(_AndroidPackage)" />
2737+
<AndroidAdb
2738+
ToolExe="$(AdbToolExe)"
2739+
ToolPath="$(AdbToolPath)"
2740+
AdbTarget="$(AdbTarget)"
2741+
Command="uninstall"
2742+
Arguments="$(_AndroidPackage)"
2743+
/>
27322744
</Target>
27332745

27342746
<Target Name="Uninstall"

tests/MSBuildDeviceIntegration/Tests/InstallTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,51 @@ public void ToggleFastDev ()
290290
}
291291
}
292292

293+
[Test]
294+
public void ToggleDebugReleaseWithSigning ([Values ("aab", "apk")] string packageFormat)
295+
{
296+
AssertCommercialBuild ();
297+
AssertHasDevices ();
298+
299+
string path = Path.Combine ("temp", TestName.Replace ("\"", string.Empty));
300+
byte [] data = ResourceData.GetKeystore ();
301+
string storepassfile = Path.Combine (Root, path, "storepass.txt");
302+
string keypassfile = Path.Combine (Root, path, "keypass.txt");
303+
var password = "file:android";
304+
305+
var proj = new XamarinAndroidApplicationProject {
306+
};
307+
proj.SetProperty (proj.ReleaseProperties, "AndroidSigningStorePass", $"file:{storepassfile}");
308+
proj.SetProperty (proj.ReleaseProperties, "AndroidSigningKeyPass", $"file:{keypassfile}");
309+
proj.SetProperty (proj.ReleaseProperties, "AndroidKeyStore", "True");
310+
proj.SetProperty (proj.ReleaseProperties, "AndroidSigningKeyStore", "test.keystore");
311+
proj.SetProperty (proj.ReleaseProperties, "AndroidSigningKeyAlias", "mykey");
312+
proj.SetAndroidSupportedAbis ("armeabi-v7a", "x86", "x86_64");
313+
proj.SetProperty (proj.ReleaseProperties, "AndroidPackageFormat", packageFormat);
314+
proj.SetProperty ("AndroidUseApkSigner", "true");
315+
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "test.keystore") {
316+
BinaryContent = () => data
317+
});
318+
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "storepass.txt") {
319+
TextContent = () => password.Replace ("file:", string.Empty),
320+
Encoding = Encoding.ASCII,
321+
});
322+
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "keypass.txt") {
323+
TextContent = () => password.Replace ("file:", string.Empty),
324+
Encoding = Encoding.ASCII,
325+
});
326+
327+
using (var builder = CreateApkBuilder (path)) {
328+
Assert.IsTrue (builder.Install (proj), "Install should have succeeded.");
329+
//Now toggle to Release
330+
proj.IsRelease = true;
331+
Assert.IsTrue (builder.Install (proj), "Second install should have succeeded.");
332+
proj.IsRelease = false;
333+
Assert.IsTrue (builder.Install (proj), "Third install should have succeeded.");
334+
Assert.IsTrue (builder.Uninstall (proj), "unnstall should have succeeded.");
335+
}
336+
}
337+
293338
[Test]
294339
public void LoggingPropsShouldCreateOverrideDirForRelease ()
295340
{

0 commit comments

Comments
 (0)