Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Add directBootAware if needed. (#2621)
Browse files Browse the repository at this point in the history
Fixes: #2081

Context? http://work.devdiv.io/727120

The [`//application/@android:directBootAware`][0] attribute *changes*
how process startup semantics work, in [non-obvious ways][1].

Consider the following:

 1. Start with an Android v7.0 Nougat device.
 2. Build, Install, & Run the [Direct Boot Sample][2]
 3. Create an alarm within the Direct Boot Sample app.
 4. Reboot the Android device.
 5. Wait for the alarm to go off without unlocking the device.

Expected results: An alarm goes off.

Actual results: Nothing happens, and `adb logcat` contains:

	java.lang.UnsatisfiedLinkError: No implementation found for void mono.android.Runtime.register(java.lang.String, java.lang.Class, java.lang.String) (tried Java_mono_android_Runtime_register and Java_mono_android_Runtime_register__Ljava_lang_String_2Ljava_lang_Class_2Ljava_lang_String_2)
	   at mono.android.Runtime.register(Native Method)
	   at md511db93b05d0fbee144be45ad6fb54d50.BootBroadcastReceiver.(BootBroadcastReceiver.java:15)
	   at java.lang.Class.newInstance(Native Method)
	   at android.app.ActivityThread.handleReceiver(ActivityThread.java:3671)
	   ...

One of the apparent changes when `directBootAware` is used is that
`<provider/>`s must *also* "opt-in" to `directBootAware`.
If a `<provider/>` *isn't* also `directBootAware`, then the
`<provider/>` can't be used before the device is unlocked.

Our Bootstrap code within `MonoRuntimeProvider` is "injected" via
`<provider/>`.  Meaning if the app uses `directBootAware` but the
`MonoRuntimeProvider` *isn't* `directBootAware`, then the app can't
use any managed code before the device has been unlocked.

Which is why the `UnsatisfiedLinkError` is generated: the
`MonoRuntimeProvider` hasn't been created, meaning `mono` hasn't been
initialized, meaning nothing can work *until the device is unlocked*.

The fix?  If *anything* within `AndroidManifest` sets
`//*[@android:directBootAware='true']`, then add a
`@android:directBootAware="true"` attribute to the generated
`<provider/>` for `mono.MonoRuntimeProvider`.  This will ensure that
managed code is properly initialized and can execute after the device
has been rebooted and before the user has unlocked the device.

[0]: https://developer.android.com/guide/topics/manifest/application-element#directBootAware
[1]: https://android-developers.googleblog.com/2016/04/developing-for-direct-boot.html
[2]: https://developer.xamarin.com/samples/monodroid/android-n/DirectBoot/
  • Loading branch information
dellis1972 authored and jonpryor committed Jan 18, 2019
1 parent afba30b commit 7c9dea8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,7 @@ public void SetupDependenciesForDesigner ()
Assert.IsTrue (appb.Build (proj, parameters: new [] { "DesignTimeBuild=True" }), "design-time build should have succeeded.");

//Now a full build
Assert.IsTrue (libb.Build (proj), "library build should have succeeded.");
Assert.IsTrue (libb.Build (lib), "library build should have succeeded.");
appb.Target = "SignAndroidPackage";
Assert.IsTrue (appb.Build (proj), "app build should have succeeded.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ public void DirectBootAwareAttribute ()
Assert.IsNotNull (le, "no activity element found");
Assert.IsTrue (doc.XPathSelectElements ("//activity[@android:directBootAware='true']", nsResolver).Any (),
"'activity' element is not generated as expected.");
Assert.IsTrue (doc.XPathSelectElements ("//provider[@android:name='mono.MonoRuntimeProvider' and @android:directBootAware='true']", nsResolver).Any (),
"'provider' element is not generated as expected.");
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -630,10 +630,12 @@ IList<string> AddMonoRuntimeProviders (XElement app)

XElement CreateMonoRuntimeProvider (string name, string processName, int initOrder)
{
var directBootAware = DirectBootAware ();
return new XElement ("provider",
new XAttribute (androidNs + "name", name),
new XAttribute (androidNs + "exported", "false"),
new XAttribute (androidNs + "initOrder", initOrder),
directBootAware ? new XAttribute (androidNs + "directBootAware", "true") : null,
processName == null ? null : new XAttribute (androidNs + "process", processName),
new XAttribute (androidNs + "authorities", PackageName + "." + name + ".__mono_init__"));
}
Expand All @@ -658,6 +660,26 @@ public bool ExtractNativeLibraries ()
return true;
}

/// <summary>
/// Returns true if an element has the @android:directBootAware attribute and its 'true'
/// </summary>
public bool DirectBootAware ()
{
var processAttrName = androidNs.GetName ("directBootAware");
var appAttr = app.Attribute (processAttrName);
bool value;
if (appAttr != null && bool.TryParse (appAttr.Value, out value) && value)
return true;
foreach (XElement el in app.Elements ()) {
var elAttr = el.Attribute (processAttrName);
if (elAttr != null && bool.TryParse (elAttr.Value, out value) && value)
return true;
}

// If android:directBootAware is omitted, returns false.
return false;
}

XElement ActivityFromTypeDefinition (TypeDefinition type, string name, int targetSdkVersion)
{
if (name.StartsWith ("_"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Material" android:extractNativeLibs="false">
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Material" android:extractNativeLibs="false" android:directBootAware="true">
</application>
</manifest>

0 comments on commit 7c9dea8

Please sign in to comment.