Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spec & prototype of Android App Bundles #2841

Merged
merged 3 commits into from
Apr 9, 2019

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Mar 15, 2019

Context: #2727

This is a prototype that gets us this far:

  • We generate a .aab file
  • We can generate a .apks file specific for an attached device
  • We can install the .apks file
  • The app starts successfully!

This workflow is achieved by:

  • The <Aapt2Link/> MSBuild task needs to pass --proto-format.
  • The <AppBundleBaseZip/> and <BundleToolBuildBundle/> MSBuild
    tasks run instead of <BuildApk/>.
  • The <BundleToolBuildApkSet/> and <BundleToolInstallApkSet/>
    tasks run instead of adb install. These are somewhat odd, but they
    use the attached device to decide which format APK set is needed.
    Otherwise the APK set was 200MB!

Some notes about Android App Bundles:

  • App bundles use android:extractNativeLibs="false", unless the
    target device's API level is too low.
  • $(AndroidUseAapt2) is required.
  • $(EmbedAssembliesIntoApk) is required.
  • $(_EmbeddedDSOsEnabled) is required, regardless of
    android:extractNativeLibs value in AndroidManifest.xml.
  • $(AndroidUseApkSigner) is turned off.
  • $(AndroidUseSharedRuntime) is turned off.

Java.Interop

Some changes are needed in MonoRuntimeProvider.Bundled.java in
java.interop before we can merge this.

We need the "split apks" to be in the list of APKs by calling
ApplicationInfo.splitPublicSourceDirs:

https://developer.android.com/reference/android/content/pm/ApplicationInfo.html#splitPublicSourceDirs

@jonathanpeppers jonathanpeppers added the do-not-merge PR should not be merged. label Mar 15, 2019
@jonathanpeppers jonathanpeppers changed the title spec & protype of Android App Bundles spec & prototype of Android App Bundles Mar 15, 2019
@erikpowa
Copy link
Contributor

erikpowa commented Mar 15, 2019

Shouldn't be there an oat folder aswell?

Edit:
nvm, probably JIT-ing.

@jonathanpeppers
Copy link
Member Author

Shouldn't be there an oat folder aswell?

I don't know, but our Java code is running: https://github.com/xamarin/xamarin-android/blob/f098d9c161a3d602c75f8e866bb22262bf25539e/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java#L23

Then our libmonodroid*.so native code is running: https://github.com/xamarin/xamarin-android/blob/f098d9c161a3d602c75f8e866bb22262bf25539e/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java#L49-L51

It crashed trying to load mono. There are some specifics with app bundles and android:extractNativeLibs="false" I'm looking into.

jonathanpeppers added a commit to jonathanpeppers/java.interop that referenced this pull request Mar 18, 2019
Context: dotnet/android#2841

In my initial work to support Android App Bundles in Xamarin.Android,
apps were crashing on startup with an error such as:

    monodroid: Using runtime path: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64
    monodroid: Trying to load sgen from: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64/libmonosgen-64bit-2.0.so
    monodroid: Trying to load sgen from: /system/lib64/libmonosgen-2.0.so
    monodroid: Cannot find 'libmonosgen-2.0.so'. Looked in the following locations:
    monodroid:   /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk!/lib/arm64-v8a
    monodroid: Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported.
    monodroid: Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration).

Android App Bundles use `android:extractNativeLibs="false"` also known
as "embedded DSOs". The file system showed the app contents are in
multiple APK files:

    $ run-as UnnamedProject.UnnamedProject ls -l /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/
    total 22663
    -rw-r--r-- 1 system system  2994137 2019-03-18 10:37 base.apk
    drwxr-xr-x 3 system system     3488 2019-03-18 10:37 lib
    -rw-r--r-- 1 system system 20161118 2019-03-18 10:37 split_config.arm64_v8a.apk
    -rw-r--r-- 1 system system    23203 2019-03-18 10:37 split_config.xxxhdpi.apk

The paths to these APK files were missing in `LoadApplication`:

https://github.com/xamarin/xamarin-android/blob/f098d9c161a3d602c75f8e866bb22262bf25539e/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java#L23

The only APK listed was:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk

It appears we need to use a new API that became available in API 21:

https://developer.android.com/reference/android/content/pm/ApplicationInfo.html#splitPublicSourceDirs

After using the value from `ApplicationInfo.splitPublicSourceDirs`:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.arm64_v8a.apk
    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.xxxhdpi.apk

Xamarin.Android apps deployed as an Android App Bundle were able to
startup properly after this change.

My initial research is showing we probably can't use "Fast Deployment"
in combination with Android App Bundles. So I don't think we should
make any changes to `MonoRuntimeProvider.Shared.java`.
jonathanpeppers added a commit to jonathanpeppers/java.interop that referenced this pull request Mar 19, 2019
Context: dotnet/android#2841

In my initial work to support Android App Bundles in Xamarin.Android,
apps were crashing on startup with an error such as:

    monodroid: Using runtime path: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64
    monodroid: Trying to load sgen from: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64/libmonosgen-64bit-2.0.so
    monodroid: Trying to load sgen from: /system/lib64/libmonosgen-2.0.so
    monodroid: Cannot find 'libmonosgen-2.0.so'. Looked in the following locations:
    monodroid:   /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk!/lib/arm64-v8a
    monodroid: Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported.
    monodroid: Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration).

Android App Bundles use `android:extractNativeLibs="false"` also known
as "embedded DSOs". The file system showed the app contents are in
multiple APK files:

    $ run-as UnnamedProject.UnnamedProject ls -l /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/
    total 22663
    -rw-r--r-- 1 system system  2994137 2019-03-18 10:37 base.apk
    drwxr-xr-x 3 system system     3488 2019-03-18 10:37 lib
    -rw-r--r-- 1 system system 20161118 2019-03-18 10:37 split_config.arm64_v8a.apk
    -rw-r--r-- 1 system system    23203 2019-03-18 10:37 split_config.xxxhdpi.apk

The paths to these APK files were missing in `LoadApplication`:

https://github.com/xamarin/xamarin-android/blob/f098d9c161a3d602c75f8e866bb22262bf25539e/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java#L23

The only APK listed was:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk

It appears we need to use a new API that became available in API 21:

https://developer.android.com/reference/android/content/pm/ApplicationInfo.html#splitPublicSourceDirs

After using the value from `ApplicationInfo.splitPublicSourceDirs`:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.arm64_v8a.apk
    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.xxxhdpi.apk

Xamarin.Android apps deployed as an Android App Bundle were able to
startup properly after this change.

My initial research is showing we probably can't use "Fast Deployment"
in combination with Android App Bundles. So I don't think we should
make any changes to `MonoRuntimeProvider.Shared.java`.

Unfortunately if your `targetSdkVersion` is 20 or less, this code
would not compile! So I included a source file with the original code:
`MonoRuntimeProvider.Bundled.20.java`. We will need to make sure this
file is used in xamarin-android for older `targetSdkVersion`:

https://github.com/xamarin/xamarin-android/blob/5fed357fcfc7c59484fb1eb9b3748bc0545c8a25/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets#L1896-L1899
jonathanpeppers added a commit to jonathanpeppers/java.interop that referenced this pull request Mar 19, 2019
Context: dotnet/android#2841

In my initial work to support Android App Bundles in Xamarin.Android,
apps were crashing on startup with an error such as:

    monodroid: Using runtime path: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64
    monodroid: Trying to load sgen from: /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/lib/arm64/libmonosgen-64bit-2.0.so
    monodroid: Trying to load sgen from: /system/lib64/libmonosgen-2.0.so
    monodroid: Cannot find 'libmonosgen-2.0.so'. Looked in the following locations:
    monodroid:   /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk!/lib/arm64-v8a
    monodroid: Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported.
    monodroid: Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration).

Android App Bundles use `android:extractNativeLibs="false"` also known
as "embedded DSOs". The file system showed the app contents are in
multiple APK files:

    $ run-as UnnamedProject.UnnamedProject ls -l /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/
    total 22663
    -rw-r--r-- 1 system system  2994137 2019-03-18 10:37 base.apk
    drwxr-xr-x 3 system system     3488 2019-03-18 10:37 lib
    -rw-r--r-- 1 system system 20161118 2019-03-18 10:37 split_config.arm64_v8a.apk
    -rw-r--r-- 1 system system    23203 2019-03-18 10:37 split_config.xxxhdpi.apk

The paths to these APK files were missing in `LoadApplication`:

https://github.com/xamarin/xamarin-android/blob/f098d9c161a3d602c75f8e866bb22262bf25539e/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java#L23

The only APK listed was:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/base.apk

It appears we need to use a new API that became available in API 21:

https://developer.android.com/reference/android/content/pm/ApplicationInfo.html#splitPublicSourceDirs

After using the value from `ApplicationInfo.splitPublicSourceDirs`:

    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.arm64_v8a.apk
    /data/app/UnnamedProject.UnnamedProject-BcBcWREBNkF09eleaCTkuw==/split_config.xxxhdpi.apk

Xamarin.Android apps deployed as an Android App Bundle were able to
startup properly after this change.

My initial research is showing we probably can't use "Fast Deployment"
in combination with Android App Bundles. So I don't think we should
make any changes to `MonoRuntimeProvider.Shared.java`.

Unfortunately if your `targetSdkVersion` is 20 or less, this code
would not compile! So I included a source file with the original code:
`MonoRuntimeProvider.Bundled.20.java`.

I added various tests changes to make sure things are working:

* Added an `ExampleActivity` and `ExampleInstrumentation` to the
  `SupportDeclarations` for `JavaCallableWrapperGeneratorTests`.
* Updated `TypeNameMapGeneratorTests` to use these types, too.
* Added `GenerateActivity` and `GenerateInstrumentation` tests. The
  former tests API 28 vs API 20.

~~ Changes in xamarin-android ~~

We will need to make sure the `MonoRuntimeProvider.Bundled.20.java`
file is used in xamarin-android for older `targetSdkVersion`:

https://github.com/xamarin/xamarin-android/blob/5fed357fcfc7c59484fb1eb9b3748bc0545c8a25/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets#L1896-L1899

We also have this:

https://github.com/xamarin/xamarin-android/blob/b08240ee50e941cb326688cc0dcf5969ebfe5473/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs#L246

These two places may be doing the same work twice, so we should fix
that, too.

We also need to pass in `TargetSdkVersion` here:

https://github.com/xamarin/xamarin-android/blob/5fed357fcfc7c59484fb1eb9b3748bc0545c8a25/src/Xamarin.Android.Build.Tasks/Generator/Generator.cs#L26-L29
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Mar 19, 2019
Context: dotnet/java-interop#422
Context: dotnet#2841

In an effort to consolidate our Java source code, there are a couple
files that should be moved into xamarin-android:

* `MonoRuntimeProvider.Bundled.java`
* `MonoRuntimeProvider.Shared.java`

This gives us more flexibility in Xamarin.Android to change these
files, in cases such as:

* Is the shared runtime used?
* Is it a specific API level?
* Could we call into some pre-compiled code instead?

I moved the files, and left them as-is.

The only other changes are due to
`Java.Interop.Tools.JavaCallableWrappers.csproj` no longer containing
resources:

* The `<CopyResource/>` MSBuild task can just look in the executing
  assembly.
* The`<GenerateJavaStubs/>` MSBuild task can just look in the
  executing assembly.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Mar 20, 2019
Context: dotnet/java-interop#422
Context: dotnet#2841

Bump to xamarin/java.interop/master@7c7eae3a

Changes: dotnet/java-interop@f84c236...7c7eae3

In an effort to consolidate our Java source code, there are a couple
files that should be moved into xamarin-android:

* `MonoRuntimeProvider.Bundled.java`
* `MonoRuntimeProvider.Shared.java`

This gives us more flexibility in Xamarin.Android to change these
files, in cases such as:

* Is the shared runtime used?
* Is it a specific API level?
* Could we call into some pre-compiled code instead?

I moved the files, and left them as-is.

The only other changes are due to
`Java.Interop.Tools.JavaCallableWrappers.csproj` no longer containing
resources:

* The `<CopyResource/>` MSBuild task can just look in the executing
  assembly.
* The`<GenerateJavaStubs/>` MSBuild task can just look in the
  executing assembly.
jonpryor pushed a commit that referenced this pull request Mar 20, 2019
Context: dotnet/java-interop@7c7eae3
Context: #2841

`make prepare` creates `MonoInfo.props`.

Add `Xamarin.Android.Tools.Bytecode.ClassPath.Load(Stream,bool)`.

Support API-Q Java stub constructs in xamarin-android-docimporter-ng.

Upgrades to NUnit 3.11.0 and NUnit.ConsoleRunner 3.9.0

Reworks `MonoRuntimeProvider.*.java` usage in
`JavaCallableWrapperGenerator`.

In an effort to consolidate our Java source code, there are a couple
files that we're moving into xamarin-android:

  * `MonoRuntimeProvider.Bundled.java`
  * `MonoRuntimeProvider.Shared.java`

This gives us more flexibility in Xamarin.Android to change these
files, in cases such as:

  * Is the shared runtime used?
  * Is it a specific API level?
  * Could we call into some pre-compiled code instead?

Instead of `JavaCallableWrapperGenerator` containing the
`MonoRuntimeProvider.*.java` files as a resource, it will instead use
a new `JavaCallableWrapperGenerator.MonoRuntimeInitialization`
property, which must be set by `<GenerateJavaStubs/>` /
`Generator.CreateJavaSources()`.

The `MonoRuntimeInitialization.*.java` files have been moved from
Java.Interop into the xamarin-android repo, leaving them as-is.

Finally, as `Java.Interop.Tools.JavaCallableWrappers.dll` no longer
contains the `MonoRuntimeProvider.*.java` resources, there is no need
to extract them anymore:

  * The `<CopyResource/>` MSBuild task now looks in the executing
    assembly and not `Java.Interop.Tools.JavaCallableWrappers.dll`.
  * The`<GenerateJavaStubs/>` MSBuild task likewise just looks in
    the executing assembly.
@jonathanpeppers jonathanpeppers force-pushed the bundletool branch 2 times, most recently from cc2fba0 to f8857b8 Compare March 25, 2019 22:19
@jonathanpeppers jonathanpeppers marked this pull request as ready for review March 26, 2019 20:09
@jonathanpeppers jonathanpeppers removed the do-not-merge PR should not be merged. label Mar 26, 2019
@jonathanpeppers
Copy link
Member Author

@jonpryor @dellis1972 this is probably ready for first review.

The 1 test failure on Windows is another random failure.

Copy link
Contributor

@dellis1972 dellis1972 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be putting the BundleTool logic and targets in their own file like we are currently doing for Aapt and Aapt2? Is that even possible?

@jonathanpeppers
Copy link
Member Author

Restarting the macOS Release build. The Mono.Android-TestsMultiDex tests failed to run, so we didn't get test results for the new Mono.Android-TestsAppBundle tests either.

They seem to work for me locally, so trying again once.

Context: dotnet#2727

This is a prototype that gets us this far:

* We generate a `.aab` file
* We can generate a `.apks` file specific for an attached device
* We can *install* the `.apks` file
* The app starts successfully!

This workflow is achieved by:

* The `<Aapt2Link/>` MSBuild task needs to pass `--proto-format`.
* The `<BuildBaseAppBundle/>` and `<BuildAppBundle/>` MSBuild tasks
  run instead of `<BuildApk/>`.
* The `<BuildApkSet/>` and `<InstallApkSet/>` tasks run instead of
  `adb install`. These are somewhat odd, but they use the attached
  device to decide which format APK set is needed. Otherwise the APK
  set was 200MB!

Some notes about Android App Bundles:

* App bundles use `android:extractNativeLibs="false"`, unless the
  target device's API level is too low.
* `$(AndroidUseAapt2)` is required.
* `$(EmbedAssembliesIntoApk)` is required.
* `$(_EmbeddedDSOsEnabled)` is required, regardless of
  `android:extractNativeLibs` value in `AndroidManifest.xml`.
* `$(AndroidUseApkSigner)` is turned off.
* `$(AndroidUseSharedRuntime)` is turned off.

~~ MonoRuntimeProvider ~~

Some changes were needed in `MonoRuntimeProvider.Bundled.java` in
java.interop to move some Java source files to this repo:

dotnet/java-interop#422

We need the "split apks" to be in the list of APKs by calling
`ApplicationInfo.splitPublicSourceDirs`:

https://developer.android.com/reference/android/content/pm/ApplicationInfo.html#splitPublicSourceDirs

These are supported on API 21 and higher, so I had to create 4 `.java`
files:

* `MonoRuntimeProvider.Bundled.java`
* `MonoRuntimeProvider.Bundled.20.java`
* `MonoRuntimeProvider.Shared.java`
* `MonoRuntimeProvider.Shared.20.java`

I also extracted `MonoRuntimeProvider.Shared*.java` in
`_AddStaticResources`. We should remove the equivalent in monodroid,
to keep the code that *extracts* this file in the same repo.

I also removed `Xamarin.Android.Platform`, which is a dead package we
don't need code for anymore.
@jonathanpeppers
Copy link
Member Author

Test run is failing to install the AAB:

build-tools/scripts/TestApks.targets(177,5): error MSB6004: The specified task executable location "/bin/java" is invalid.

Something with the java path isn't right on macOS.

@jonathanpeppers
Copy link
Member Author

So the app bundle tests look good now:

<test-results name="Mono.Android_TestsAppBundle" total="147" errors="0" failures="0" not-run="2" inconclusive="0" ignored="2" skipped="0" invalid="0" date="2019-04-04" time="17:03:09">

We did get a crash in the JavaCallableWrappers-Tests, but I think it is unrelated:

[2019-04-04T21:24:12.306Z]   SIGQUIT: [libjvm.dylib+0x489759], sa_mask[0]=11111111011111110111111111111111, sa_flags=SA_RESTART|SA_SIGINFO
[2019-04-04T21:24:12.306Z] /Users/builder/jenkins/workspace/xamarin-android-pr-pipeline-release/xamarin-android/external/Java.Interop/build-tools/scripts/RunNUnitTests.targets(35,5): error MSB3073: The command "mono --debug packages/NUnit.ConsoleRunner.3.9.0/tools/nunit3-console.exe  /Users/builder/jenkins/workspace/xamarin-android-pr-pipeline-release/xamarin-android/external/Java.Interop/build-tools/scripts/../../bin/TestRelease/Java.Interop.Export-Tests.dll  --result="TestResult-Java.Interop.Export-Tests.xml;format=nunit2" --output="bin/TestRelease/TestOutput-Java.Interop.Export-Tests.txt"" exited with code -1.

ApkSet="$(_ApkSetIntermediate)"
/>
</Target>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dellis1972 it looks like I could put this target in a new file easily, but is that too little to warrant a new file?

BaseZip="$(_BaseZipIntermediate)"
Output="$(_AppBundleIntermediate)"
UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"
/>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could move these to the new file and use <CallTarget/>, but it wouldn't be able to use this:

<Output TaskParameter="NativeLibrariesReferences" ItemName="_AdditionalNativeLibraryReferences" />

@jonpryor jonpryor merged commit 4f08867 into dotnet:master Apr 9, 2019
@jonathanpeppers jonathanpeppers deleted the bundletool branch August 2, 2019 22:08
@github-actions github-actions bot locked and limited conversation to collaborators Jan 31, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants