diff --git a/.external b/.external
index eaa12dfe14f..63a656c5bee 100644
--- a/.external
+++ b/.external
@@ -1,2 +1,2 @@
-xamarin/monodroid:master@0eef889156e92ff0d47e62477f10798b6d5b5c29
+xamarin/monodroid:master@27736a7ffc48d606ab45598f761e873f8572f46a
mono/mono:2020-02@ac596375c762c6b8dbe3c802f0ce626004eab51c
diff --git a/Documentation/guides/building-apps/build-properties.md b/Documentation/guides/building-apps/build-properties.md
index 7cbd10a0a27..6b3e75ff7ce 100644
--- a/Documentation/guides/building-apps/build-properties.md
+++ b/Documentation/guides/building-apps/build-properties.md
@@ -274,6 +274,30 @@ to `pkcs12`.
Added in Xamarin.Android 10.2.
+
+## AndroidDeviceUserId
+
+Allows deploying and debugging the application under guest
+or work accounts. The value is the `uid` value you get
+from the following adb command
+
+```
+adb shell pm list users
+```
+
+This will return the following data
+
+```
+Users:
+ UserInfo{0:Owner:c13} running
+ UserInfo{10:Guest:404}
+```
+
+The `uid` is the first integer value. In the example they
+are `0` and `10`.
+
+Added in Xamarin.Android 11.2
+
## AndroidDexTool
An enum-style property with valid
diff --git a/Documentation/release-notes/5311.md b/Documentation/release-notes/5311.md
new file mode 100644
index 00000000000..57411b39e99
--- /dev/null
+++ b/Documentation/release-notes/5311.md
@@ -0,0 +1,33 @@
+### Build and deployment performance
+
+* [GitHub PR 5311](https://github.com/xamarin/xamarin-android/pull/5311):
+ Add support for deploying and debugging against different user accounts
+ on Android Devices. In order to use this you need to specify the
+ `AndroidDeviceUserId` in either your csproj or on the command line.
+
+ ```xml
+
+ 10
+
+ ```
+
+ ```
+ msbuild foo.csproj /t:Install /p:AndroidDeviceUserId=10
+ ```
+
+ The value is the `uid` value you get from the following adb command
+
+ ```
+ adb shell pm list users
+ ```
+
+ This will return the following data
+
+ ```
+ Users:
+ UserInfo{0:Owner:c13} running
+ UserInfo{10:Guest:404}
+ ```
+
+ The `uid` is the first integer value. In the example they
+ are `0` and `10`.
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
index a5fc67ec10f..94de779bfc0 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
@@ -13,11 +13,14 @@ namespace Xamarin.Android.Build.Tests
{
public class DeviceTest: BaseTest
{
+ public const string GuestUserName = "guest1";
+
[OneTimeSetUp]
public void DeviceSetup ()
{
SetAdbLogcatBufferSize (64);
RunAdbCommand ("logcat -c");
+ CreateGuestUser (GuestUserName);
}
[TearDown]
@@ -42,6 +45,7 @@ protected override void CleanupTest ()
ClearAdbLogcat ();
+
base.CleanupTest ();
}
@@ -260,5 +264,42 @@ protected static string [] GetOverrideDirectoryPaths (string packageName)
$"/storage/sdcard/Android/data/{packageName}/files/.__override__",
};
}
+
+ protected static void CreateGuestUser (string username)
+ {
+ if (GetUserId (username) != -1)
+ RunAdbCommand ($"shell pm create-user --guest {username}");
+ }
+
+ protected static void DeleteGuestUser (string username)
+ {
+ int userId = GetUserId (username);
+ if (userId > 0)
+ RunAdbCommand ($"shell pm remove-user --guest {userId}");
+ }
+
+ protected static int GetUserId (string username)
+ {
+ string output = RunAdbCommand ($"shell pm list users");
+ Regex regex = new Regex (@"UserInfo{(?\d+):" + username, RegexOptions.Compiled);
+ Console.WriteLine (output);
+ var match = regex.Match (output);
+ if (match.Success) {
+ return int.Parse (match.Groups ["userId"].Value);
+ }
+ return -1;
+ }
+
+ protected static bool SwitchUser (string username)
+ {
+ int userId = GetUserId (username);
+ if (userId == -1)
+ userId = 0;
+ if (userId >= 0) {
+ RunAdbCommand ($"shell am switch-user {userId}");
+ return true;
+ }
+ return false;
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs
index 47b59aa6ee2..176d7001ee8 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs
@@ -40,6 +40,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug"
if (Builder.UseDotNet) {
SetProperty (KnownProperties.OutputType, "Exe");
SetProperty ("XamarinAndroidSupportSkipVerifyVersions", "True");
+ SetProperty ("_FastDeploymentDiagnosticLogging", "True");
// Workaround for AndroidX, see: https://github.com/xamarin/AndroidSupportComponents/pull/239
Imports.Add (new Import (() => "Directory.Build.targets") {
@@ -51,6 +52,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug"
"
});
} else {
+ SetProperty ("_FastDeploymentDiagnosticLogging", "True");
SetProperty ("AndroidApplication", "True");
SetProperty ("AndroidResgenClass", "Resource");
SetProperty ("AndroidResgenFile", () => "Resources\\Resource.designer" + Language.DefaultDesignerExtension);
diff --git a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs
index 6765e55bf53..1fa8c80ae0f 100644
--- a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs
+++ b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs
@@ -253,6 +253,7 @@ public void ApkSet ()
{
AssertHasDevices ();
+ appBuilder.BuildLogFile = "install.log";
Assert.IsTrue (appBuilder.RunTarget (app, "Install"), "App should have installed.");
var aab = Path.Combine (intermediate, "android", "bin", "UnnamedProject.UnnamedProject.apks");
diff --git a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs
index 6691d601239..bba76474bc1 100755
--- a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs
+++ b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs
@@ -8,6 +8,7 @@
using Mono.Debugging.Soft;
using NUnit.Framework;
using Xamarin.ProjectTools;
+using System.Collections.Generic;
namespace Xamarin.Android.Build.Tests
{
@@ -251,36 +252,63 @@ public override void OnCreate ()
/* embedAssemblies */ true,
/* fastDevType */ "Assemblies",
/* allowDeltaInstall */ false,
+ /* user */ null,
},
new object[] {
/* embedAssemblies */ false,
/* fastDevType */ "Assemblies",
/* allowDeltaInstall */ false,
+ /* user */ null,
},
new object[] {
/* embedAssemblies */ false,
/* fastDevType */ "Assemblies",
/* allowDeltaInstall */ true,
+ /* user */ null,
},
new object[] {
/* embedAssemblies */ false,
/* fastDevType */ "Assemblies:Dexes",
/* allowDeltaInstall */ false,
+ /* user */ null,
},
new object[] {
/* embedAssemblies */ false,
/* fastDevType */ "Assemblies:Dexes",
/* allowDeltaInstall */ true,
+ /* user */ null,
+ },
+ new object[] {
+ /* embedAssemblies */ true,
+ /* fastDevType */ "Assemblies",
+ /* allowDeltaInstall */ false,
+ /* user */ DeviceTest.GuestUserName,
+ },
+ new object[] {
+ /* embedAssemblies */ false,
+ /* fastDevType */ "Assemblies",
+ /* allowDeltaInstall */ false,
+ /* user */ DeviceTest.GuestUserName,
},
};
#pragma warning restore 414
[Test, Category ("SmokeTests"), Category ("Debugger")]
[TestCaseSource (nameof(DebuggerTestCases))]
- public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string fastDevType, bool allowDeltaInstall)
+ public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string fastDevType, bool allowDeltaInstall, string username)
{
AssertCommercialBuild ();
AssertHasDevices ();
+
+ int userId = GetUserId (username);
+ List parameters = new List ();
+ if (userId >= 0)
+ parameters.Add ($"AndroidDeviceUserId={userId}");
+ if (SwitchUser (username)) {
+ WaitFor (5);
+ ClickButton ("", "android:id/button1", "Yes continue");
+ }
+
var proj = new XamarinFormsAndroidApplicationProject () {
IsRelease = false,
EmbedAssembliesIntoApk = embedAssemblies,
@@ -291,7 +319,7 @@ public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string f
proj.SetDefaultTargetDevice ();
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
SetTargetFrameworkAndManifest (proj, b);
- Assert.True (b.Install (proj), "Project should have installed.");
+ Assert.True (b.Install (proj, parameters: parameters.ToArray ()), "Project should have installed.");
int breakcountHitCount = 0;
ManualResetEvent resetEvent = new ManualResetEvent (false);
@@ -324,11 +352,13 @@ public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string f
options.EvaluationOptions.UseExternalTypeResolver = true;
ClearAdbLogcat ();
b.BuildLogFile = "run.log";
- Assert.True (b.RunTarget (proj, "_Run", doNotCleanupOnUpdate: true, parameters: new string [] {
- $"AndroidSdbTargetPort={port}",
- $"AndroidSdbHostPort={port}",
- "AndroidAttachDebugger=True",
- }), "Project should have run.");
+
+ parameters.Add ($"AndroidSdbTargetPort={port}");
+ parameters.Add ($"AndroidSdbHostPort={port}");
+ parameters.Add ("AndroidAttachDebugger=True");
+
+ Assert.True (b.RunTarget (proj, "_Run", doNotCleanupOnUpdate: true,
+ parameters: parameters.ToArray ()), "Project should have run.");
Assert.IsTrue (WaitForDebuggerToStart (Path.Combine (Root, b.ProjectDirectory, "logcat.log")), "Activity should have started");
// we need to give a bit of time for the debug server to start up.