diff --git a/Directory.Build.props b/Directory.Build.props
index 3f855aae763..d01d6648f8e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -48,7 +48,7 @@
5.4.01.1.116.12.0.148
- 6.0.0
+ 8.0.06.0.02.13.12.14.1
diff --git a/Documentation/guides/AndroidAssetPacks.md b/Documentation/guides/AndroidAssetPacks.md
new file mode 100644
index 00000000000..920bc2d5959
--- /dev/null
+++ b/Documentation/guides/AndroidAssetPacks.md
@@ -0,0 +1,235 @@
+# Android Asset Packs
+
+Google Android began supporting splitting up the app package into multiple
+packs with the introduction of the `aab` package format. This format allows
+the developer to split the app up into multiple `packs`. Each `pack` can be
+downloaded to the device either at install time or on demand. This allows
+application developers to save space and install time by only installing
+the required parts of the app initially, then installing other `packs`
+as required.
+
+There are two types of `pack`: Feature packs and Asset packs.
+*Feature* pack contains *non-native* Java code and other resources.
+Code in these types of `pack` can be launched via the `Context.StartActivity()`
+API call. At this time due to various constraints .NET Android cannot support
+Feature packs.
+
+*Asset* packs contain *only*
+[`@(AndroidAsset)`](~/android/deploy-test/building-apps/build-items.md#androidasset) items.
+It *cannot* contain any code or other resources. This type of `pack` can be
+installed at install-time, fast-follow or ondemand. It is most useful for apps
+which contain a lot of Assets, such as Games or Multi Media applications.
+See the [Android Asset Delivery documentation](https://developer.android.com/guide/playcore/asset-delivery)
+for details on how this all works.
+
+## Asset Pack Specification
+
+We want to provide our users the ability to use `Asset` packs without relying
+on [Alternative Methods](#alternativemethods)
+
+Add support for new `%(AndroidAsset.AssetPack)` item metadata, which
+allows the build system to split up the assets into packs automatically:
+
+```xml
+
+
+
+
+
+```
+
+The default value for `%(AndroidAsset.AssetPack)` is `base`, which will
+cause the asset to be included in the main application package.
+
+As auto import of items is now common, we need a way for a user to add
+this additional attribute to auto included items. This can be done by
+using `Update`:
+
+```xml
+
+
+
+
+
+```
+
+`%(AndroidAsset.DeliveryType)` item metadata can be specified to control what
+*type* of asset pack is produced. Valid values are:
+
+ * `InstallTime`: Asset pack will be delivered when the app is installed.
+ This is the default value for assets not in the base package.
+ * `FastFollow`: Asset pack will be downloaded automatically as soon as the app is installed.
+ * `OnDemand`: Asset pack will be downloaded while the app is running.
+
+The `DeliveryType` for a given asset pack is based on the *first*
+`@(AndroidAsset.DeliveryType)` value encountered for a `%(AssetPack)` name.
+
+Consider the following example, in which `Asset/movie2.mp4` and `Asset/movie3.mp4`
+are both in the `assets1` pack, which will have a `%(DeliveryType)` of `InstallTime`
+(the first encountered value "wins"). `Asset/movie1.mp4` will be in the base package,
+while `Asset/movie4.mp4` will be in the "asset2" asset pack.
+
+```xml
+
+
+
+
+
+
+```
+
+See Google's [documentation](https://developer.android.com/guide/playcore/asset-delivery#asset-updates) for details on what each of the `DeliveryType` values do.
+
+If however you have a large number of assets it might be cleaner in the
+`.csproj` to make use of the `base` value for the `%(AssetPack)` attribute.
+In this scenario you update *all* assets to be in a single asset pack then use
+`AssetPack="base"` metadata to declare which specific assets end up in the base
+aab file. With this you can use wildcards to move most assets into the asset pack:
+
+```xml
+
+
+
+
+
+```
+
+In this example, `movie.mp4` and `some.png` will end up in the `base` aab file,
+but *all the other assets* will end up in the `assets1` asset pack.
+
+At this time the `@(AndroidAsset)` build action does not support `%(AssetPack)`
+or `%(DeliveryType)` Metadata in Library Projects.
+
+NOTE: `AssetPacks` are only used when the
+[`$(AndroidPackageFormat)`](~/android/deploy-test/building-apps/build-properties.md#debugsymbols)
+property is set to `aab` (the default for Release).
+When using the `apk` setting the assets will be placed inside the `apk`.
+
+## Release Configuration
+
+In order for the application to function correctly we need to inform the `R8`
+linker which Java classes we need to keep. To do this we need to add the
+following lines to a `ProGuard.cfg` file which is in the root of our project folder:
+
+```
+-keep com.google.android.play.*
+```
+
+Alternatively you can create a file called `ProGuard.cfg` and use the
+[@(ProguardConfiguration)](~/android/deploy-test/building-apps/build-items.md#proguardconfiguration)
+build action. Adding these lines will ensure that all the required Java components are not linked
+away during the Release build.
+
+## Testing and Debugging
+
+In order to test your asset packs in the `Debug` configuration, you will need to
+make some changes to your `.csproj`. Firstly we need to change the
+`$(AndroidPackageFormat)` to `aab`. It will be `aab` by default for `Release` builds,
+but will default to `apk` for `Debug` builds. Setting the `AndroidPackageFormat` to `aab`
+will disable fast deployment, so it is advised that you only do this when you need to test
+your `AssetPacks`.
+
+To test your asset packs add the following to the first `` in your `.csproj`.
+
+```xml
+aab
+--local-testing $(AndroidBundleToolExtraArgs)
+```
+
+The `--local-testing` argument tells the `bundletool` application to install all the asset packs
+in a local cache on the device. `InstallTime` packs will be installed during the app installation process.
+
+`FastFollow` packs behave like `OnDemand` packs. They will not automatically installed when the app
+is sideloaded. You will need to request them manually when the game starts.
+
+For more details see [https://developer.android.com/guide/playcore/asset-delivery/test](https://developer.android.com/guide/playcore/asset-delivery/test).
+
+## Implementation Details
+
+There are a few changes we need to make in order to support this feature.
+One of the issues we will hit is the build times when dealing with large assets.
+Current the assets which are to be included in the `aab` are ***copied***
+into the `$(IntermediateOutputPath)assets` directory. This folder is
+then passed to `aapt2` for the build process.
+
+The new system adds a new directory `$(IntermediateOutputPath)assetpacks`.
+This directory would contain a subdirectory for each `pack` that the
+user wants to include.
+
+```dotnetcli
+assetpacks/
+ assets1/
+ assets/
+ movie2.mp4
+ assets2/
+ assets/
+ movie3.mp4
+```
+
+All the building of the `pack` zip file would take place in these subfolders.
+The name of the pack will be based on the main "packagename" with the asset pack
+name appended to the end. e.g `com.microsoft.assetpacksample.assets1`.
+
+During the build process we identify all the `AndroidAsset` items which
+have an `AssetPack` attribute. These files are then copied to the
+new `$(IntermediateOutputPath)assetpacks` directory rather than the
+existing `$(IntermediateOutputPath)assets` directory. This allows us to
+continue to support the normal `AndroidAsset` behavior while adding the
+new system.
+
+Once we have collected and copied all the assets we then use the new
+`` Task to figure out which asset packs we need to create.
+We then call the `` task to create a required
+`AndroidManifest.xml` file for the asset pack. This file will end
+up in the same `$(IntermediateOutputPath)assetpacks` directory.
+We call this Task `` because it can be used
+to create any feature pack if and when we get to implement full feature
+packs.
+
+```dotnetcli
+assetpacks/
+ assets1/
+ AndroidManifest.xml
+ assets/
+ movie2.mp4
+ assets2/
+ AndroidManifest.xml
+ assets/
+ movie3.mp4
+```
+
+We can then call `aapt2` to build these packs into `.zip` files. A new
+task `` task takes care of this. This is a special version
+of `Aapt2Link` which implements linking for asset packs only.
+It also takes care of a few problems which `aapt2` introduces. For some
+reason the zip file that is created has the `AndroidManifest.xml` file
+in the wrong place. It creates it in the root of the zip file, but the
+`bundletool` expects it to be in a `manifest` directory.
+`bundletool` will error out if its not in the right place.
+So `` takes care of this for us. It also removes a
+`resources.pb` which gets added. Again, `bundletool` will error if this
+file is in the zip file.
+
+Once the zip files have been created they are then added to the
+`@(AndroidAppBundleModules)` ItemGroup. This will ensure that when the
+final `.aab` file is generated they are included as asset packs.
+
+## Alternative Methods
+
+An alternative method is available on [github](https://github.com/infinitespace-studios/MauiAndroidAssetPackExample).
+This method allows developers to place additional assets in a special
+[NoTargets](https://github.com/microsoft/MSBuildSdks/blob/main/src/NoTargets/README.md) project.
+This project is built just after the final `aab` is produced. It builds a zip
+file which is then added to the `@(Modules)` ItemGroup in the main application.
+This zip is then included into the
+final app as an additional feature.
+
+Using a separate project like in the hack is one way to go. It does have some
+issues though.
+
+ 1. It is a `special` type of project. It requires a `global.json` which imports
+ the `NoTargets` sdk.
+ 2. There is no IDE support for building this type of project.
+
+Having the user go through a number of hoops to implement this for
+.NET Android or .NET MAUI is not ideal.
diff --git a/Documentation/guides/building-apps/build-items.md b/Documentation/guides/building-apps/build-items.md
index cd78fd4fa02..176e875aa3a 100644
--- a/Documentation/guides/building-apps/build-items.md
+++ b/Documentation/guides/building-apps/build-items.md
@@ -19,6 +19,37 @@ or library project is built.
Supports [Android Assets](https://developer.android.com/guide/topics/resources/providing-resources#OriginalFiles),
files that would be included in the `assets` folder in a Java Android project.
+Starting with .NET 9 the `@(AndroidAsset)` build action also supports additional metadata for generating [Asset Packs](https://developer.android.com/guide/playcore/asset-delivery). The `%(AndroidAsset.AssetPack)` metadata can be used to automatically generate an asset pack of that name. This feature is only supported when the [`$(AndroidPackageFormat)`](#androidpackageformat) is set to `.aab`. The following example will place `movie2.mp4` and `movie3.mp4` in separate asset packs.
+
+```xml
+
+
+
+
+
+```
+
+This feature can be used to include large files in your application which would normally exceed the max
+package size limits of Google Play.
+
+If you have a large number of assets it might be more efficient to make use of the `base` asset pack.
+In this scenario you update ALL assets to be in a single asset pack then use the `AssetPack="base"` metadata
+to declare which specific assets end up in the base aab file. With this you can use wildcards to move most
+assets into the asset pack.
+
+```xml
+
+
+
+
+
+```
+
+In this example, `movie.mp4` and `some.png` will end up in the `base` aab file, while all the other assets
+will end up in the `assets1` asset pack.
+
+The additional metadata is only supported on .NET Android 9 and above.
+
## AndroidAarLibrary
The Build action of `AndroidAarLibrary` should be used to directly
diff --git a/Documentation/guides/building-apps/build-process.md b/Documentation/guides/building-apps/build-process.md
index 42e4837ed9a..0ec934e1610 100644
--- a/Documentation/guides/building-apps/build-process.md
+++ b/Documentation/guides/building-apps/build-process.md
@@ -195,6 +195,7 @@ Extension points include:
- [`$(AfterGenerateAndroidManifest)](~/android/deploy-test/building-apps/build-properties.md#aftergenerateandroidmanifest)
- [`$(BeforeGenerateAndroidManifest)](~/android/deploy-test/building-apps/build-properties.md#beforegenerateandroidmanifest)
+- [`$(BeforeBuildAndroidAssetPacks)`](~/android/deploy-test/building-apps/build-properties.md#beforebuildandroidassetpacks)
A word of caution about extending the build process: If not
written correctly, build extensions can affect your build
diff --git a/Documentation/guides/building-apps/build-properties.md b/Documentation/guides/building-apps/build-properties.md
index cd29dca973e..5e3468f8a55 100644
--- a/Documentation/guides/building-apps/build-properties.md
+++ b/Documentation/guides/building-apps/build-properties.md
@@ -832,6 +832,18 @@ APK root directory. The format of the path is `lib\ARCH\wrap.sh` where
+ `x86_64`
+ `x86`
+## AndroidIncludeAssetPacksInPackage
+
+This property controls if an Asset Packs build automatically are auto
+included in the final `.aab` file. It will default to `true`.
+
+In certain cases the user might want to release an interim release. In
+these cases the user does not need to update the asset pack. Especially
+if the contents of the asset pack have not changed. This property allows
+the user to skip the asset packs if they are not required.
+
+Added in .NET 9
+
## AndroidInstallJavaDependencies
The default value is `true` for command line builds. When set to `true`, enables
@@ -1674,6 +1686,13 @@ as support for `$(AotAssemblies)` will be removed in a future release.
Extra options to pass to `aprofutil`.
+## BeforeBuildAndroidAssetPacks
+
+MSBuild Targets listed in this
+property will run directly before the `AssetPack` items are built.
+
+Added in .NET 9
+
## BeforeGenerateAndroidManifest
MSBuild Targets listed in this
diff --git a/Documentation/guides/messages/README.md b/Documentation/guides/messages/README.md
index bab9d5dc5d0..df2c6915f35 100644
--- a/Documentation/guides/messages/README.md
+++ b/Documentation/guides/messages/README.md
@@ -100,6 +100,9 @@ package from all the users on device and try again. If that does not work you ca
Fast Deployment is not currently supported on this device.
Please file an issue with the exact error message using the 'Help->Send Feedback->Report a Problem' menu item in Visual Studio
or 'Help->Report a Problem' in Visual Studio for Mac.
++ [XA0138](xa0138.md): %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
++ [XA0139](xa0139.md): `@(AndroidAsset)` `{0}` has invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`
++ [XA0140](xa0140.md):
## XA1xxx: Project related
diff --git a/Documentation/guides/messages/xa0138.md b/Documentation/guides/messages/xa0138.md
new file mode 100644
index 00000000000..6dc8a4341ae
--- /dev/null
+++ b/Documentation/guides/messages/xa0138.md
@@ -0,0 +1,13 @@
+title: Xamarin.Android error XA0138
+description: XA0138 error code
+ms.date: 02/05/2024
+---
+# Xamarin.Android error XA0138
+
+## Issue
+
+%(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+## Solution
+
+Remove the 'AssetPack' or 'DeliveryType' Metadata from your `AndroidAsset` build Items in the project the error was raised for.
diff --git a/Documentation/guides/messages/xa0139.md b/Documentation/guides/messages/xa0139.md
new file mode 100644
index 00000000000..1ba498b9736
--- /dev/null
+++ b/Documentation/guides/messages/xa0139.md
@@ -0,0 +1,13 @@
+title: Xamarin.Android error XA0138
+description: XA0138 error code
+ms.date: 02/05/2024
+---
+# Xamarin.Android error XA0138
+
+## Issue
+
+`@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+
+## Solution
+
+Make sure that all `DeliveryType` attributes are one of the following valid values, `installtime`, `ondemand` or `fastfollow`.
diff --git a/Documentation/guides/messages/xa0140.md b/Documentation/guides/messages/xa0140.md
new file mode 100644
index 00000000000..3d25181c887
--- /dev/null
+++ b/Documentation/guides/messages/xa0140.md
@@ -0,0 +1,13 @@
+title: Xamarin.Android error XA0138
+description: XA0140 error code
+ms.date: 02/05/2024
+---
+# Xamarin.Android error XA0140
+
+## Issue
+
+The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+
+## Solution
+
+Make sure that all `AssetPack` attributes only contain valid characters.
diff --git a/Localize/loc/cs/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/cs/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index f6586705089..f66376d8a5e 100644
--- a/Localize/loc/cs/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/cs/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/cs/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/cs/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index 1d128039578..5690c0fe4f4 100644
--- a/Localize/loc/cs/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/cs/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/cs/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/cs/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 879d8f782f1..43d40aa37eb 100644
--- a/Localize/loc/cs/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/cs/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/es/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/es/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index d97d95502ad..ca19d71e1d1 100644
--- a/Localize/loc/es/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/es/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/es/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/es/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index f7542997362..9901b50122d 100644
--- a/Localize/loc/es/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/es/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/es/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/es/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 747c1a58ae6..53997d48643 100644
--- a/Localize/loc/es/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/es/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/fr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/fr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 571b9b17ced..26dc8e9af6d 100644
--- a/Localize/loc/fr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/fr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/fr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/fr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index 9bb670674bf..c663b66d3ee 100644
--- a/Localize/loc/fr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/fr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/fr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/fr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 0f36b36d415..d47387f7731 100644
--- a/Localize/loc/fr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/fr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/it/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/it/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index b3052669481..494b261bfa5 100644
--- a/Localize/loc/it/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/it/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/it/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/it/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index c9969e1ba3c..a771abc1123 100644
--- a/Localize/loc/it/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/it/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/it/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/it/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 7a030c2ac04..5f304f66a4b 100644
--- a/Localize/loc/it/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/it/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/ja/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ja/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 86369bde99e..b6cb0693504 100644
--- a/Localize/loc/ja/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ja/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ja/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ja/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index 4339d218eaf..ee1049bd906 100644
--- a/Localize/loc/ja/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ja/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ja/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/ja/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 448c22fcbe1..58ca38745bf 100644
--- a/Localize/loc/ja/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/ja/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/ko/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ko/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 437763db39a..fb105652392 100644
--- a/Localize/loc/ko/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ko/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ko/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ko/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index 055c047f213..e503106959d 100644
--- a/Localize/loc/ko/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ko/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ko/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/ko/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 297684eb109..980a46dc037 100644
--- a/Localize/loc/ko/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/ko/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/pl/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/pl/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 37c8bf71751..ddb99fe63c9 100644
--- a/Localize/loc/pl/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/pl/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/pl/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/pl/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index 0166133ee4b..66ddf9283ea 100644
--- a/Localize/loc/pl/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/pl/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/pl/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/pl/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 65f6e7b8e97..0e30a224437 100644
--- a/Localize/loc/pl/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/pl/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index bbe1854eb90..3037b7a8881 100644
--- a/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index c45b5bcf109..f2d5db18180 100644
--- a/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/pt-BR/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/pt-BR/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/pt-BR/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 511983be568..e824d4bb83a 100644
--- a/Localize/loc/pt-BR/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/pt-BR/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/ru/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ru/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 88cac291c43..68341d49e90 100644
--- a/Localize/loc/ru/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ru/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ru/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/ru/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index a5a70383648..ddcafde7c9f 100644
--- a/Localize/loc/ru/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/ru/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/ru/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/ru/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index c2a436866f0..2be4b4745cf 100644
--- a/Localize/loc/ru/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/ru/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/tr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/tr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 0a170e08624..1a3ed5a4307 100644
--- a/Localize/loc/tr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/tr/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/tr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/tr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index fffa1f923b0..40ee516fb54 100644
--- a/Localize/loc/tr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/tr/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/tr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/tr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 3b0aa216000..5d3f24be10b 100644
--- a/Localize/loc/tr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/tr/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 2f5d90b3342..c6260032ffc 100644
--- a/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index aba4d641e40..1aadf671ef0 100644
--- a/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/zh-Hans/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/zh-Hans/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/zh-Hans/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index 1c1ccaf757d..530821af33c 100644
--- a/Localize/loc/zh-Hans/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/zh-Hans/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl b/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
index 93e4d41a1a7..19598290aeb 100644
--- a/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl b/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
index e4a16c9bc7b..b697d81f8bc 100644
--- a/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
+++ b/Localize/loc/zh-Hant/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.json.lcl
@@ -29,10 +29,13 @@
-
+
-
+
+
+
+
diff --git a/Localize/loc/zh-Hant/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl b/Localize/loc/zh-Hant/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
index fcdd2362bc5..c1b10dc9ade 100644
--- a/Localize/loc/zh-Hant/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
+++ b/Localize/loc/zh-Hant/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx.lcl
@@ -417,6 +417,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln
index 50918d1e4e3..c85d197a5aa 100644
--- a/Xamarin.Android.sln
+++ b/Xamarin.Android.sln
@@ -109,7 +109,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "decompress-assemblies", "to
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tmt", "tools\tmt\tmt.csproj", "{1A273ED2-AE84-48E9-9C23-E978C2D0CB34}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "assembly-store-reader", "tools\assembly-store-reader\assembly-store-reader.csproj", "{DA50FC92-7FE7-48B5-BDB6-CDA57B37BB51}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "assembly-store-reader", "tools\assembly-store-reader-mk2\assembly-store-reader.csproj", "{DA50FC92-7FE7-48B5-BDB6-CDA57B37BB51}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.JavaTypeSystem", "external\Java.Interop\src\Java.Interop.Tools.JavaTypeSystem\Java.Interop.Tools.JavaTypeSystem.csproj", "{4EFCED6E-9A6B-453A-94E4-CE4B736EC684}"
EndProject
diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets
index dbd3982b5bf..85c8b990475 100644
--- a/build-tools/installers/create-installers.targets
+++ b/build-tools/installers/create-installers.targets
@@ -136,6 +136,7 @@
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Aapt2.targets" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Analysis.targets" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Application.targets" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Assets.targets" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.ClassParse.targets" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.Core.targets" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)Xamarin.Android.Bindings.Maven.targets" />
diff --git a/build-tools/scripts/TestApks.targets b/build-tools/scripts/TestApks.targets
index 95ba391d170..1110f32453c 100644
--- a/build-tools/scripts/TestApks.targets
+++ b/build-tools/scripts/TestApks.targets
@@ -111,7 +111,14 @@
Timeout="60000"
/>
+
-
+ https://github.com/dotnet/installer
- e911f5c82cc02aea96e227596e16c830d54cf03a
+ b40c44502deca1e7f51674b97b2d6ca2d5e0abac
-
+ https://github.com/dotnet/runtime
- 3eb8c7f1086b79b28a27b57a935f97be3b7fcccb
+ 596a1f7b6429fc06cf71465238cb349cab4edc35
-
+ https://github.com/dotnet/runtime
- 3eb8c7f1086b79b28a27b57a935f97be3b7fcccb
+ 596a1f7b6429fc06cf71465238cb349cab4edc35https://github.com/dotnet/emsdk
diff --git a/eng/Versions.props b/eng/Versions.props
index f16fd164c98..7c14218b5df 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -1,9 +1,9 @@
- 9.0.100-preview.3.24161.2
- 9.0.0-preview.3.24160.3
- 9.0.0-preview.3.24160.3
+ 9.0.100-preview.3.24165.20
+ 9.0.0-preview.3.24162.31
+ 9.0.0-preview.3.24162.317.0.0-beta.22103.17.0.0-beta.22103.19.0.0-preview.3.24156.3
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.cs.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.cs.json
index 209e3191423..2c7a5fb488e 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.cs.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.cs.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Šablona aktivity Androidu",
+ "name": "Android Activity",
"description": "Třída aktivity Androidu",
"symbols/namespace/description": "obor názvů pro vygenerovaný kód",
"postActions/openInEditor/description": "Otevře soubor Activity1.cs v editoru"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.de.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.de.json
index 054d3423f73..d8c9c828c09 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.de.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.de.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android-Aktivitätsvorlage",
+ "name": "Android Activity",
"description": "Eine Android-Aktivitätsklasse",
"symbols/namespace/description": "Namespace für den generierten Code",
"postActions/openInEditor/description": "Öffnet Activity1.cs im Editor"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.es.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.es.json
index a2b0fdd88b3..0b5889febf4 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.es.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.es.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Plantilla de actividad de Android",
+ "name": "Android Activity",
"description": "Una clase de actividad de Android",
"symbols/namespace/description": "espacio de nombres para el código generado",
"postActions/openInEditor/description": "Abre Activity1.cs en el editor"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.fr.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.fr.json
index 7384adafecb..9ef7a13d917 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.fr.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.fr.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modèle d’activité Android",
+ "name": "Android Activity",
"description": "Une classe d’activité Android",
"symbols/namespace/description": "espace de noms pour le code généré",
"postActions/openInEditor/description": "Ouvre Activity1.cs dans l’éditeur"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.it.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.it.json
index 6f449315d1f..7c156f6c459 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.it.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.it.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modello di attività Android",
+ "name": "Android Activity",
"description": "Classe di attività Android",
"symbols/namespace/description": "spazio dei nomi per il codice generato",
"postActions/openInEditor/description": "Apre Activity1.cs nell'editor"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ja.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ja.json
index 58eca70a69b..d25bf9506c0 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ja.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ja.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android アクティビティ テンプレート",
+ "name": "Android Activity",
"description": "Android アクティビティ クラス",
"symbols/namespace/description": "生成されたコードの名前空間",
"postActions/openInEditor/description": "エディターで Activity1.cs を開きます"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ko.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ko.json
index 3fb98e6fa19..23314be7f1c 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ko.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ko.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 활동 템플릿",
+ "name": "Android Activity",
"description": "Android 활동 클래스",
"symbols/namespace/description": "생성된 코드의 네임스페이스",
"postActions/openInEditor/description": "편집기에서 Activity1.cs를 엽니다."
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pl.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pl.json
index b79c2185192..c08d08cd96f 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pl.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pl.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Szablon Aktywność systemu Android",
+ "name": "Android Activity",
"description": "Klasa Aktywność systemu Android",
"symbols/namespace/description": "przestrzeń nazw wygenerowanego kodu.",
"postActions/openInEditor/description": "Otwiera plik Activity1.cs w edytorze"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pt-BR.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pt-BR.json
index a3050741c5a..661ea64b3fc 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pt-BR.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.pt-BR.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modelo de Atividade do Android",
+ "name": "Android Activity",
"description": "Uma classe de Atividade do Android",
"symbols/namespace/description": "namespace do código gerado",
"postActions/openInEditor/description": "Abre Activity1.cs no editor"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ru.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ru.json
index feacd4ee7f1..b14a5c3819f 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ru.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.ru.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Шаблон действий Android",
+ "name": "Android Activity",
"description": "Класс активности Android",
"symbols/namespace/description": "пространство имен для созданного кода",
"postActions/openInEditor/description": "Открывает Activity1.cs в редакторе"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.tr.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.tr.json
index ccbe7a76c6d..11e6fca1286 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.tr.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.tr.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android Etkinlik şablonu",
+ "name": "Android Activity",
"description": "Android Etkinlik sınıfı",
"symbols/namespace/description": "oluşturulan kod için ad alanı",
"postActions/openInEditor/description": "Activity1.cs dosyasını düzenleyicide açar"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hans.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hans.json
index 7e9e2fb2073..aae1495fad4 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hans.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hans.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 活动模板",
+ "name": "Android Activity",
"description": "Android 活动类",
"symbols/namespace/description": "生成的代码的命名空间",
"postActions/openInEditor/description": "在编辑器中打开 Activity1.cs"
diff --git a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hant.json b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hant.json
index a7501e3e863..93e88a5b136 100644
--- a/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hant.json
+++ b/src/Microsoft.Android.Templates/android-activity/.template.config/localize/templatestrings.zh-Hant.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 活動範本",
+ "name": "Android Activity",
"description": "Android 活動類別",
"symbols/namespace/description": "適用於產生之程式碼的命名空間",
"postActions/openInEditor/description": "在編輯器中開啟 Activity1.cs"
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.cs.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.cs.json
index 98469f9b720..a7d8196424b 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.cs.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.cs.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Šablona rozložení pro Android",
+ "name": "Android Layout",
"description": "Soubor rozložení Androidu (XML)",
"postActions/openInEditor/description": "Otevře soubor Layout1.xml v editoru"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.de.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.de.json
index 7082b64b9ef..e5bf2c4a327 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.de.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.de.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android-Layoutvorlage",
+ "name": "Android Layout",
"description": "Eine Android-Layoutdatei (XML)",
"postActions/openInEditor/description": "Öffnet Layout1.xml im Editor"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.es.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.es.json
index 6f69c4da7a9..12eaf398ce4 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.es.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.es.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Plantilla de diseño de Android",
+ "name": "Android Layout",
"description": "Un archivo de diseño de Android (XML)",
"postActions/openInEditor/description": "Abre Layout1.xml en el editor"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.fr.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.fr.json
index d4f92089e84..febaf069f42 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.fr.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.fr.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modèle de disposition Android",
+ "name": "Android Layout",
"description": "Fichier de disposition Android (XML)",
"postActions/openInEditor/description": "Ouvre Layout1.xml dans l’éditeur"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.it.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.it.json
index 40c52301a2b..509976aca0d 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.it.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.it.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modello di layout Android",
+ "name": "Android Layout",
"description": "File di layout Android (XML)",
"postActions/openInEditor/description": "Apre Layout1.xml nell'editor"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ja.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ja.json
index 4f0ab4f5fbc..2271bc56c75 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ja.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ja.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android レイアウト テンプレート",
+ "name": "Android Layout",
"description": "Android レイアウト (XML) ファイル",
"postActions/openInEditor/description": "エディターで Layout1.xml を開きます"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ko.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ko.json
index 3766f60e2fc..32164416b0f 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ko.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ko.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 레이아웃 템플릿",
+ "name": "Android Layout",
"description": "Android 레이아웃(XML) 파일",
"postActions/openInEditor/description": "편집기에서 Layout1.xml 엽니다."
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pl.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pl.json
index 0421f11b109..1d07b020667 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pl.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pl.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Szablon Układ systemu Android",
+ "name": "Android Layout",
"description": "Plik układu systemu Android (XML)",
"postActions/openInEditor/description": "Otwiera plik Layout1.xml w edytorze"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pt-BR.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pt-BR.json
index 43039dbe9a4..b0b529cfe55 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pt-BR.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.pt-BR.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Modelo de Layout do Android",
+ "name": "Android Layout",
"description": "Um arquivo de layout do Android (XML)",
"postActions/openInEditor/description": "Abre Layout1.xml no editor"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ru.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ru.json
index 46f7d0395a3..9ef322a8c76 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ru.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.ru.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Шаблон макета Android",
+ "name": "Android Layout",
"description": "Файл макета Android (XML)",
"postActions/openInEditor/description": "Открывает Layout1.xml в редакторе"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.tr.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.tr.json
index d42375f3fe5..05823701f09 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.tr.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.tr.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android Düzeni şablonu",
+ "name": "Android Layout",
"description": "Android düzeni (XML) dosyası",
"postActions/openInEditor/description": "Layout1.xml dosyasını düzenleyicide açar"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hans.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hans.json
index f1e39eb23c9..93b7e549967 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hans.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hans.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 布局模板",
+ "name": "Android Layout",
"description": "Android 布局 (XML) 文件",
"postActions/openInEditor/description": "在编辑器中打开 Layout1.xml"
}
\ No newline at end of file
diff --git a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hant.json b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hant.json
index 54370db861f..0adcbc216da 100644
--- a/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hant.json
+++ b/src/Microsoft.Android.Templates/android-layout/.template.config/localize/templatestrings.zh-Hant.json
@@ -1,6 +1,6 @@
{
"author": "Microsoft",
- "name": "Android 版面配置範本",
+ "name": "Android Layout",
"description": "Android 配置 (XML) 檔案",
"postActions/openInEditor/description": "在編輯器中開啟 Layout1.xml"
}
\ No newline at end of file
diff --git a/src/Mono.Android/Android.Runtime/JavaProxyThrowable.cs b/src/Mono.Android/Android.Runtime/JavaProxyThrowable.cs
index 5755f705f30..d221bf48054 100644
--- a/src/Mono.Android/Android.Runtime/JavaProxyThrowable.cs
+++ b/src/Mono.Android/Android.Runtime/JavaProxyThrowable.cs
@@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Reflection;
+using System.Text;
using StackTraceElement = Java.Lang.StackTraceElement;
@@ -37,6 +39,81 @@ public static JavaProxyThrowable Create (Exception innerException)
return proxy;
}
+ (int lineNumber, string? methodName, string? className) GetFrameInfo (StackFrame? managedFrame, MethodBase? managedMethod)
+ {
+ string? methodName = null;
+ string? className = null;
+
+ if (managedFrame == null) {
+ if (managedMethod != null) {
+ methodName = managedMethod.Name;
+ className = managedMethod.DeclaringType?.FullName;
+ }
+
+ return (-1, methodName, className);
+ }
+
+ int lineNumber = -1;
+ lineNumber = managedFrame.GetFileLineNumber ();
+ if (lineNumber == 0) {
+ // -2 means it's a native frame
+ lineNumber = managedFrame.HasNativeImage () ? -2 : -1;
+ }
+
+ if (managedMethod != null) {
+ // If we have no line number information and if it's a managed frame, add the
+ // IL offset.
+ if (lineNumber == -1 && managedFrame.HasILOffset ()) {
+ methodName = $"{managedMethod.Name} + 0x{managedFrame.GetILOffset():x}";
+ } else {
+ methodName = managedMethod.Name;
+ }
+
+ return (lineNumber, methodName, managedMethod.DeclaringType?.FullName);
+ }
+
+ string frameString = managedFrame.ToString ();
+ var sb = new StringBuilder ();
+
+ // We take the part of the returned string that stretches from the beginning to the first space character
+ // and treat it as the method name.
+ // https://github.com/dotnet/runtime/blob/18c3ad05c3fc127c3b7f37c49bc350bf7f8264a0/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs#L15-L55
+ int pos = frameString.IndexOf (' ');
+ string? fullName = null;
+ if (pos > 1) {
+ fullName = frameString.Substring (0, pos);
+ }
+
+ if (!String.IsNullOrEmpty (fullName) && (pos = fullName.LastIndexOf ('.')) >= 1) {
+ className = pos + 1 < fullName.Length ? fullName.Substring (pos + 1) : null;
+ fullName = fullName.Substring (0, pos);
+ }
+
+ if (!String.IsNullOrEmpty (fullName)) {
+ sb.Append (fullName);
+ } else if (managedFrame.HasNativeImage ()) {
+ // We have no name, so we'll put the native IP
+ nint nativeIP = managedFrame.GetNativeIP ();
+ sb.Append (CultureInfo.InvariantCulture, $"Native 0x{nativeIP:x}");
+ }
+
+ if (sb.Length > 0) {
+ // We will also append information native offset information, if available and only if we
+ // have recorded any previous information, since the offset without context is useless.
+ int nativeOffset = managedFrame.GetNativeOffset ();
+ if (nativeOffset != StackFrame.OFFSET_UNKNOWN) {
+ sb.Append (" + ");
+ sb.Append (CultureInfo.InvariantCulture, $"0x{nativeOffset:x}");
+ }
+ }
+
+ if (sb.Length > 0) {
+ methodName = sb.ToString ();
+ }
+
+ return (lineNumber, methodName, className);
+ }
+
void TranslateStackTrace ()
{
// FIXME: https://github.com/xamarin/xamarin-android/issues/8724
@@ -61,20 +138,22 @@ void TranslateStackTrace ()
// ..but ignore
}
-
StackFrame[] frames = trace.GetFrames ();
int nElements = frames.Length + (javaTrace?.Length ?? 0);
StackTraceElement[] elements = new StackTraceElement[nElements];
+ const string Unknown = "Unknown";
for (int i = 0; i < frames.Length; i++) {
StackFrame managedFrame = frames[i];
MethodBase? managedMethod = StackFrameGetMethod (managedFrame);
+ // https://developer.android.com/reference/java/lang/StackTraceElement?hl=en#StackTraceElement(java.lang.String,%20java.lang.String,%20java.lang.String,%20int)
+ (int lineNumber, string? methodName, string? declaringClass) = GetFrameInfo (managedFrame, managedMethod);
var throwableFrame = new StackTraceElement (
- declaringClass: managedMethod?.DeclaringType?.FullName,
- methodName: managedMethod?.Name,
+ declaringClass: declaringClass ?? Unknown,
+ methodName: methodName ?? Unknown,
fileName: managedFrame?.GetFileName (),
- lineNumber: managedFrame?.GetFileLineNumber () ?? -1
+ lineNumber: lineNumber
);
elements[i] = throwableFrame;
diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Assets.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Assets.targets
new file mode 100644
index 00000000000..00dc628acaa
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Assets.targets
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ $(IntermediateOutputPath)assets\
+ $(IntermediateOutputPath)assetpacks
+ Assets
+ true
+
+
+
+
+ UpdateAndroidAssets
+ ;_CalculateAssetsWithAssetPackMetaData
+ ;_CalculateAssetPacks
+ ;$(BeforeBuildAndroidAssetPacks)
+ ;_CreateAssetPackManifests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_AssetDirectories Include="$(MonoAndroidAssetsDirIntermediate)" />
+ <_AssetDirectories Include="@(_AssetPacks->'%(AssetPackDirectory)')" />
+
+
+
+
+
+
+
+
+
+
+
+
+ <_AssetsWithAssetPackMetaData Include="@(AndroidAsset)" Condition=" '%(AndroidAsset.AssetPack)' != '' " />
+
+
+
+
+
+
+
+
+
+
+ <_AssetPacks Include="@(_AndroidAsset)">
+ $(MonoAndroidAssetPacksDirIntermediate)\%(_AndroidAsset.AssetPack)\assets
+ $(MonoAndroidAssetPacksDirIntermediate)\%(_AndroidAsset.AssetPack).zip
+ $(MonoAndroidAssetPacksDirIntermediate)\%(_AndroidAsset.AssetPack)\AndroidManifest.xml
+ InstallTime
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
index ae35d401aee..d4aae16f7ee 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
@@ -120,11 +120,14 @@ _ResolveAssemblies MSBuild target.
<_ResolvedSymbolFiles Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Extension)' == '.pdb' " />
<_ResolvedJavaLibraries Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Extension)' == '.jar' " />
+
+
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
index 19d0c698d1f..31c9442e8e9 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
@@ -76,6 +76,7 @@ properties that determine build ordering.
_LintChecks;
_IncludeNativeSystemLibraries;
_CheckGoogleSdkRequirements;
+ BuildAndroidAssetPacks;
@@ -91,6 +92,7 @@ properties that determine build ordering.
_AddAndroidDefines;
_CheckForContent;
_CheckForObsoleteFrameworkAssemblies;
+ _CalculateAssetsWithAssetPackMetaData;
_RemoveLegacyDesigner;
_ValidateAndroidPackageProperties;
AddLibraryJarsToBind;
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs
index e46244bf171..c5df25bdcc2 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs
@@ -1600,5 +1600,15 @@ public static string XA1036 {
return ResourceManager.GetString("XA1036", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{0}' does not match the $(SupportedOSPlatformVersion) value '{1}' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed).
+ ///Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist)..
+ ///
+ public static string XA1039 {
+ get {
+ return ResourceManager.GetString("XA1039", resourceCulture);
+ }
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx
index 9fd5f97a2ed..7b3a141387c 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx
@@ -328,6 +328,20 @@ Pokud tento soubor pochází z balíčku NuGet, aktualizujte na novější verzi
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Při parsování {0} došlo k problému. Pravděpodobnou příčinou je neúplný nebo neplatný kód XML. Výjimka: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx
index 1ce0eb5ff2b..8abd3bc8bd8 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx
@@ -328,6 +328,20 @@ Wenn diese Datei aus einem NuGet-Paket stammt, führen Sie ein Update auf eine n
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Problem beim Analysieren von "{0}". Dies ist wahrscheinlich auf eine unvollständige oder ungültige XML-Datei zurückzuführen. Ausnahme: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx
index d25bdbd85e6..5468dc4a071 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx
@@ -328,6 +328,20 @@ Si este archivo procede de un paquete NuGet, actualice a una versión más recie
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Hubo un problema al analizar {0}. Probablemente se deba a un elemento XML incompleto o no válido. Excepción: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx
index 98e26fb061b..5887bd7c892 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx
@@ -328,6 +328,20 @@ Si ce fichier provient d'un package NuGet, effectuez une mise à jour vers une v
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Un problème s'est produit durant l'analyse de {0}. Cela est probablement dû à du code XML incomplet ou non valide. Exception : {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx
index a39f1641c71..626403495a0 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx
@@ -328,6 +328,20 @@ Se questo file proviene da un pacchetto NuGet, eseguire l'aggiornamento a una ve
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Si è verificato un problema durante l'analisi di {0}, probabilmente a causa di codice XML incompleto o non valido. Eccezione: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx
index 7f772edefa1..d739e788d1a 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx
@@ -328,6 +328,20 @@ Visual Studio プロジェクトのプロパティ ページでデバッグ情
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.{0} の解析で問題が発生しました。不完全または無効な XML が原因である可能性があります。例外: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx
index 1fd598fecb9..e9ff8e5cb5a 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx
@@ -328,6 +328,20 @@ Visual Studio 프로젝트 속성 페이지에서 디버깅 정보를 이식 가
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.{0}을(를) 구문 분석하는 중 문제가 발생했습니다. XML이 불완전하거나 잘못된 것 같습니다. 예외: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx
index 15d1cb362ce..95e41b7f1b3 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx
@@ -328,6 +328,20 @@ Jeśli ten plik pochodzi z pakietu NuGet, zaktualizuj do nowszej wersji pakietu
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Wystąpił problem podczas analizowania pliku {0}. Prawdopodobnie jest to spowodowane niekompletnym lub nieprawidłowym kodem XML. Wyjątek: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx
index d216340f3fa..5cf638fb51c 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx
@@ -328,6 +328,20 @@ Se esse arquivo provém de um pacote NuGet, faça a atualização para uma vers
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Ocorreu um problema ao analisar {0}. Isso provavelmente se deve a XML incompleto ou inválido. Exceção: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
index a1a45e18ff3..f473cd0f265 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
@@ -328,6 +328,20 @@ If this file comes from a NuGet package, update to a newer version of the NuGet
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.There was a problem parsing {0}. This is likely due to incomplete or invalid XML. Exception: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx
index 1392685268b..1dcfa59c35b 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx
@@ -328,6 +328,20 @@ When it appears in the middle of a sentence, "lint" is not capitalized.
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.Возникла проблема при синтаксическом анализе {0}. Возможная причина: неполный или недопустимый код XML. Исключение: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx
index 8161ee088a3..5a937c38be2 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx
@@ -328,6 +328,20 @@ Bu dosya bir NuGet paketinden geliyorsa NuGet paketinin daha yeni bir sürümün
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.{0} ayrıştırılırken bir sorun oluştu. Bunun nedeni büyük olasılıkla tamamlanmamış veya geçersiz XML olabilir. Özel durum: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx
index 3be36be35a2..c65728ddaea 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx
@@ -328,6 +328,20 @@ When it appears in the middle of a sentence, "lint" is not capitalized.
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.分析 {0} 时出现问题。这可能是由于 XML 不完整或无效。异常: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx
index f4715d17ac2..43503b28e39 100644
--- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx
+++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx
@@ -328,6 +328,20 @@ When it appears in the middle of a sentence, "lint" is not capitalized.
The following are literal names and should not be translated: 'DebugType', 'portable'
The capitalized word "Portable" that appears earlier in the message is plain text and should be translated, but the lowercase word "portable" later in the message is a literal value and should not be translated.
{0} - The file name of a deprecated symbol file
+
+
+ %(AndroidAsset.AssetPack) and %(AndroidAsset.AssetPack) item metadata are only supported when `$(AndroidApplication)` is `true`.
+
+
+
+ `@(AndroidAsset)` `{0}` has an invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow`.
+ {0} - The file name
+{1} - The value of the attribute in the project file.
+
+
+ The AssetPack value defined for `{0}` has invalid characters. `{1}` should only contain A-z, a-z, 0-9 or an underscore.
+ {0} - The file name
+{1} - The value of the attribute in the metadata.剖析 {0} 時發生問題。此情況可能是 XML 不完整或無效所致。例外狀況: {1}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2LinkAssetPack.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2LinkAssetPack.cs
new file mode 100644
index 00000000000..7c9a1f16a5a
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2LinkAssetPack.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+using Xamarin.Android.Tools;
+using Microsoft.Android.Build.Tasks;
+
+namespace Xamarin.Android.Tasks {
+
+ public class Aapt2LinkAssetPack : Aapt2 {
+ public override string TaskPrefix => "A2LAP";
+
+ [Required]
+ public ITaskItem Manifest { get; set; }
+
+ [Required]
+ public ITaskItem[] AssetDirectories { get; set; }
+
+ [Required]
+ public string PackageName { get; set; }
+
+ [Required]
+ public ITaskItem OutputArchive { get; set; }
+
+ protected override int GetRequiredDaemonInstances ()
+ {
+ return Math.Min (1, DaemonMaxInstanceCount);
+ }
+
+ public async override System.Threading.Tasks.Task RunTaskAsync ()
+ {
+ RunAapt (GenerateCommandLineCommands (Manifest, OutputArchive), OutputArchive.ItemSpec);
+ ProcessOutput ();
+ if (File.Exists (OutputArchive.ItemSpec)) {
+ // move the manifest to the right place.
+ using (var zip = new ZipArchiveEx (OutputArchive.ItemSpec, File.Exists (OutputArchive.ItemSpec) ? FileMode.Open : FileMode.Create)) {
+ zip.MoveEntry ("AndroidManifest.xml", "manifest/AndroidManifest.xml");
+ zip.Archive.DeleteEntry ("resources.pb");
+ // Fix up aapt2 not dealing with '\' in subdirectories for assets.
+ zip.FixupWindowsPathSeparators ((a, b) => Log.LogDebugMessage ($"Fixing up malformed entry `{a}` -> `{b}`"));
+ }
+ }
+ await System.Threading.Tasks.Task.CompletedTask;
+ }
+
+ protected string[] GenerateCommandLineCommands (ITaskItem manifest, ITaskItem output)
+ {
+ //link --manifest AndroidManifest.xml --proto-format --custom-package $(Package) -A $(AssetsDirectory) -o $(_TempOutputFile)
+ List cmd = new List ();
+ cmd.Add ("link");
+ if (MonoAndroidHelper.LogInternalExceptions)
+ cmd.Add ("-v");
+ cmd.Add ("--manifest");
+ cmd.Add (GetFullPath (manifest.ItemSpec));
+ cmd.Add ("--proto-format");
+ cmd.Add ("--custom-package");
+ cmd.Add (PackageName);
+ foreach (var assetDirectory in AssetDirectories) {
+ cmd.Add ("-A");
+ cmd.Add (GetFullPath (assetDirectory.ItemSpec));
+ }
+ cmd.Add ($"-o");
+ cmd.Add (GetFullPath (output.ItemSpec));
+ return cmd.ToArray ();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidComputeResPaths.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidComputeResPaths.cs
index 96a3ccadbe9..4115073151c 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidComputeResPaths.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidComputeResPaths.cs
@@ -45,6 +45,8 @@ public class AndroidComputeResPaths : AndroidTask
[Required]
public string IntermediateDir { get; set; }
+ public string AssetPackIntermediateDir { get; set; }
+
public string Prefixes { get; set; }
public bool LowercaseFilenames { get; set; }
@@ -83,6 +85,7 @@ public override bool RunTask ()
continue;
//compute the target path
string rel;
+ var assetPack = item.GetMetadata ("AssetPack");
var logicalName = item.GetMetadata ("LogicalName").Replace ('\\', Path.DirectorySeparatorChar);
if (item.GetMetadata ("IsWearApplicationResource") == "True") {
rel = item.ItemSpec.Substring (IntermediateDir.Length);
@@ -126,6 +129,13 @@ public override bool RunTask ()
}
string dest = Path.GetFullPath (Path.Combine (IntermediateDir, baseFileName));
string intermediateDirFullPath = Path.GetFullPath (IntermediateDir);
+ if (!string.IsNullOrEmpty (assetPack) &&
+ (string.Compare (assetPack, "base", StringComparison.OrdinalIgnoreCase) != 0) &&
+ !string.IsNullOrEmpty (AssetPackIntermediateDir)) {
+ dest = Path.GetFullPath (Path.Combine (AssetPackIntermediateDir, assetPack, "assets", baseFileName));
+ intermediateDirFullPath = Path.GetFullPath (AssetPackIntermediateDir);
+ }
+
// if the path ends up "outside" of our target intermediate directory, just use the filename
if (String.Compare (intermediateDirFullPath, 0, dest, 0, intermediateDirFullPath.Length, StringComparison.OrdinalIgnoreCase) != 0) {
dest = Path.GetFullPath (Path.Combine (IntermediateDir, Path.GetFileName (baseFileName)));
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
index c99ce0db5c5..45778d60204 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
@@ -23,6 +23,9 @@ namespace Xamarin.Android.Tasks
{
public class BuildApk : AndroidTask
{
+ const string ArchiveAssembliesPath = "lib";
+ const string ArchiveLibPath = "lib";
+
public override string TaskPrefix => "BLD";
public string AndroidNdkDirectory { get; set; }
@@ -33,6 +36,7 @@ public class BuildApk : AndroidTask
[Required]
public string ApkOutputPath { get; set; }
+ [Required]
public string AppSharedLibrariesDir { get; set; }
[Required]
@@ -118,12 +122,11 @@ bool _Debug {
SequencePointsMode sequencePointsMode = SequencePointsMode.None;
public ITaskItem[] LibraryProjectJars { get; set; }
- string [] uncompressedFileExtensions;
+ HashSet uncompressedFileExtensions;
+ // Do not use trailing / in the path
protected virtual string RootPath => "";
- protected virtual string AssembliesPath => RootPath + "assemblies/";
-
protected virtual string DalvikPath => "";
protected virtual CompressionMethod UncompressedMethod => CompressionMethod.Store;
@@ -136,7 +139,7 @@ protected virtual void FixupArchive (ZipArchiveEx zip) { }
List includePatterns = new List ();
- void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary compressedAssembliesInfo, string assemblyStoreApkName)
+ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary> compressedAssembliesInfo, string assemblyStoreApkName)
{
ArchiveFileList files = new ArchiveFileList ();
bool refresh = true;
@@ -207,6 +210,7 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
apk.Flush ();
}
+ AddRuntimeConfigBlob (apk);
AddRuntimeLibraries (apk, supportedAbis);
apk.Flush();
AddNativeLibraries (files, supportedAbis);
@@ -218,10 +222,6 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
}
}
- if (!String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) {
- AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, $"{AssembliesPath}rc.bin", compressionMethod: UncompressedMethod);
- }
-
foreach (var file in files) {
var item = Path.Combine (file.archivePath.Replace (Path.DirectorySeparatorChar, '/'));
existingEntries.Remove (item);
@@ -318,7 +318,18 @@ public override bool RunTask ()
Aot.TryGetSequencePointsMode (AndroidSequencePointsMode, out sequencePointsMode);
var outputFiles = new List ();
- uncompressedFileExtensions = UncompressedFileExtensions?.Split (new char [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty ();
+ uncompressedFileExtensions = new HashSet (StringComparer.OrdinalIgnoreCase);
+ foreach (string? e in UncompressedFileExtensions?.Split (new char [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty ()) {
+ string? ext = e?.Trim ();
+ if (String.IsNullOrEmpty (ext)) {
+ continue;
+ }
+
+ if (ext[0] != '.') {
+ ext = $".{ext}";
+ }
+ uncompressedFileExtensions.Add (ext);
+ }
existingEntries.Clear ();
@@ -331,12 +342,12 @@ public override bool RunTask ()
bool debug = _Debug;
bool compress = !debug && EnableCompression;
- IDictionary compressedAssembliesInfo = null;
+ IDictionary> compressedAssembliesInfo = null;
if (compress) {
string key = CompressedAssemblyInfo.GetKey (ProjectFullPath);
Log.LogDebugMessage ($"Retrieving assembly compression info with key '{key}'");
- compressedAssembliesInfo = BuildEngine4.UnregisterTaskObjectAssemblyLocal> (key, RegisteredTaskObjectLifetime.Build);
+ compressedAssembliesInfo = BuildEngine4.UnregisterTaskObjectAssemblyLocal>> (key, RegisteredTaskObjectLifetime.Build);
if (compressedAssembliesInfo == null)
throw new InvalidOperationException ($"Assembly compression info not found for key '{key}'. Compression will not be performed.");
}
@@ -380,34 +391,36 @@ static Regex FileGlobToRegEx (string fileGlob, RegexOptions options)
return new Regex (sb.ToString (), options);
}
- void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary compressedAssembliesInfo, string assemblyStoreApkName)
+ void AddRuntimeConfigBlob (ZipArchiveEx apk)
+ {
+ // We will place rc.bin in the `lib` directory next to the blob, to make startup slightly faster, as we will find the config file right after we encounter
+ // our assembly store. Not only that, but also we'll be able to skip scanning the `base.apk` archive when split configs are enabled (which they are in 99%
+ // of cases these days, since AAB enforces that split). `base.apk` contains only ABI-agnostic file, while one of the split config files contains only
+ // ABI-specific data+code.
+ if (!String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) {
+ foreach (string abi in SupportedAbis) {
+ // Prefix it with `a` because bundletool sorts entries alphabetically, and this will place it right next to `assemblies.*.blob.so`, which is what we
+ // like since we can finish scanning the zip central directory earlier at startup.
+ string inArchivePath = MakeArchiveLibPath (abi, "libarc.bin.so");
+ AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, inArchivePath, compressionMethod: GetCompressionMethod (inArchivePath));
+ }
+ }
+ }
+
+ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary> compressedAssembliesInfo, string assemblyStoreApkName)
{
string sourcePath;
AssemblyCompression.AssemblyData compressedAssembly = null;
string compressedOutputDir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4"));
- AssemblyStoreGenerator storeGenerator;
+ AssemblyStoreGenerator? storeGenerator;
if (UseAssemblyStore) {
- storeGenerator = new AssemblyStoreGenerator (AssembliesPath, Log);
+ storeGenerator = new AssemblyStoreGenerator (Log);
} else {
storeGenerator = null;
}
- AssemblyStoreAssemblyInfo storeAssembly = null;
-
- //
- // NOTE
- //
- // The very first store (ID 0) **must** contain an index of all the assemblies included in the application, even if they
- // are included in other APKs than the base one. The ID 0 store **must** be placed in the base assembly
- //
-
- // Currently, all the assembly stores end up in the "base" apk (the APK name is the key in the dictionary below) but the code is ready for the time when we
- // partition assemblies into "feature" APKs
- const string DefaultBaseApkName = "base";
- if (String.IsNullOrEmpty (assemblyStoreApkName)) {
- assemblyStoreApkName = DefaultBaseApkName;
- }
+ AssemblyStoreAssemblyInfo? storeAssemblyInfo = null;
// Add user assemblies
AddAssembliesFromCollection (ResolvedUserAssemblies);
@@ -419,41 +432,53 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary> assemblyStorePaths = storeGenerator.Generate (Path.GetDirectoryName (ApkOutputPath));
- if (assemblyStorePaths == null) {
+ Dictionary assemblyStorePaths = storeGenerator.Generate (AppSharedLibrariesDir);
+
+ if (assemblyStorePaths.Count == 0) {
throw new InvalidOperationException ("Assembly store generator did not generate any stores");
}
- if (!assemblyStorePaths.TryGetValue (assemblyStoreApkName, out List baseAssemblyStores) || baseAssemblyStores == null || baseAssemblyStores.Count == 0) {
- throw new InvalidOperationException ("Assembly store generator didn't generate the required base stores");
+ if (assemblyStorePaths.Count != SupportedAbis.Length) {
+ throw new InvalidOperationException ("Internal error: assembly store did not generate store for each supported ABI");
}
- string assemblyStorePrefix = $"{assemblyStoreApkName}_";
- foreach (string assemblyStorePath in baseAssemblyStores) {
- string inArchiveName = Path.GetFileName (assemblyStorePath);
-
- if (inArchiveName.StartsWith (assemblyStorePrefix, StringComparison.Ordinal)) {
- inArchiveName = inArchiveName.Substring (assemblyStorePrefix.Length);
- }
-
- CompressionMethod compressionMethod;
- if (inArchiveName.EndsWith (".manifest", StringComparison.Ordinal)) {
- compressionMethod = CompressionMethod.Default;
- } else {
- compressionMethod = UncompressedMethod;
- }
-
- AddFileToArchiveIfNewer (apk, assemblyStorePath, AssembliesPath + inArchiveName, compressionMethod);
+ string inArchivePath;
+ foreach (var kvp in assemblyStorePaths) {
+ string abi = MonoAndroidHelper.ArchToAbi (kvp.Key);
+ inArchivePath = MakeArchiveLibPath (abi, "lib" + Path.GetFileName (kvp.Value));
+ AddFileToArchiveIfNewer (apk, kvp.Value, inArchivePath, GetCompressionMethod (inArchivePath));
}
void AddAssembliesFromCollection (ITaskItem[] assemblies)
{
- foreach (ITaskItem assembly in assemblies) {
- if (bool.TryParse (assembly.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
- Log.LogDebugMessage ($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
- continue;
+ Dictionary> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (
+ assemblies,
+ SupportedAbis,
+ validate: true,
+ shouldSkip: (ITaskItem asm) => {
+ if (bool.TryParse (asm.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
+ Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
+ return true;
+ }
+
+ return false;
}
+ );
+
+ foreach (var kvp in perArchAssemblies) {
+ Log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'");
+ DoAddAssembliesFromArchCollection (kvp.Value);
+ }
+ }
+ void DoAddAssembliesFromArchCollection (Dictionary assemblies)
+ {
+ // In the "all assemblies are per-RID" world, assemblies, pdb and config are disguised as shared libraries (that is,
+ // their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory.
+ // For this reason, they have to be treated just like other .so files, as far as compression rules are concerned.
+ // Thus, we no longer just store them in the apk but we call the `GetCompressionMethod` method to find out whether
+ // or not we're supposed to compress .so files.
+ foreach (ITaskItem assembly in assemblies.Values) {
if (MonoAndroidHelper.IsReferenceAssembly (assembly.ItemSpec)) {
Log.LogCodedWarning ("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec);
}
@@ -461,25 +486,27 @@ void AddAssembliesFromCollection (ITaskItem[] assemblies)
sourcePath = CompressAssembly (assembly);
// Add assembly
- var assemblyPath = GetAssemblyPath (assembly, frameworkAssembly: false);
+ (string assemblyPath, string assemblyDirectory) = GetInArchiveAssemblyPath (assembly);
if (UseAssemblyStore) {
- storeAssembly = new AssemblyStoreAssemblyInfo (sourcePath, assemblyPath, assembly.GetMetadata ("Abi"));
+ storeAssemblyInfo = new AssemblyStoreAssemblyInfo (sourcePath, assembly);
} else {
- AddFileToArchiveIfNewer (apk, sourcePath, assemblyPath + Path.GetFileName (assembly.ItemSpec), compressionMethod: UncompressedMethod);
+ AddFileToArchiveIfNewer (apk, sourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath));
}
// Try to add config if exists
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
if (UseAssemblyStore) {
- storeAssembly.SetConfigPath (config);
+ if (File.Exists (config)) {
+ storeAssemblyInfo.ConfigFile = new FileInfo (config);
+ }
} else {
- AddAssemblyConfigEntry (apk, assemblyPath, config);
+ AddAssemblyConfigEntry (apk, assemblyDirectory, config);
}
// Try to add symbols if Debug
if (debug) {
var symbols = Path.ChangeExtension (assembly.ItemSpec, "pdb");
- string symbolsPath = null;
+ string? symbolsPath = null;
if (File.Exists (symbols)) {
symbolsPath = symbols;
@@ -487,15 +514,21 @@ void AddAssembliesFromCollection (ITaskItem[] assemblies)
if (!String.IsNullOrEmpty (symbolsPath)) {
if (UseAssemblyStore) {
- storeAssembly.SetDebugInfoPath (symbolsPath);
+ storeAssemblyInfo.SymbolsFile = new FileInfo (symbolsPath);
} else {
- AddFileToArchiveIfNewer (apk, symbolsPath, assemblyPath + Path.GetFileName (symbols), compressionMethod: UncompressedMethod);
+ string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols));
+ AddFileToArchiveIfNewer (
+ apk,
+ symbolsPath,
+ archiveSymbolsPath,
+ compressionMethod: GetCompressionMethod (archiveSymbolsPath)
+ );
}
}
}
if (UseAssemblyStore) {
- storeGenerator.Add (assemblyStoreApkName, storeAssembly);
+ storeGenerator.Add (storeAssemblyInfo);
}
}
}
@@ -519,38 +552,44 @@ string CompressAssembly (ITaskItem assembly)
return assembly.ItemSpec;
}
- var key = CompressedAssemblyInfo.GetDictionaryKey (assembly);
- if (compressedAssembliesInfo.TryGetValue (key, out CompressedAssemblyInfo info) && info != null) {
- EnsureCompressedAssemblyData (assembly.ItemSpec, info.DescriptorIndex);
- string assemblyOutputDir;
- string subDirectory = assembly.GetMetadata ("DestinationSubDirectory");
- if (!String.IsNullOrEmpty (subDirectory))
- assemblyOutputDir = Path.Combine (compressedOutputDir, subDirectory);
- else
- assemblyOutputDir = compressedOutputDir;
- AssemblyCompression.CompressionResult result = AssemblyCompression.Compress (compressedAssembly, assemblyOutputDir);
- if (result != AssemblyCompression.CompressionResult.Success) {
- switch (result) {
- case AssemblyCompression.CompressionResult.EncodingFailed:
- Log.LogMessage ($"Failed to compress {assembly.ItemSpec}");
- break;
-
- case AssemblyCompression.CompressionResult.InputTooBig:
- Log.LogMessage ($"Input assembly {assembly.ItemSpec} exceeds maximum input size");
- break;
-
- default:
- Log.LogMessage ($"Unknown error compressing {assembly.ItemSpec}");
- break;
- }
- return assembly.ItemSpec;
- }
- return compressedAssembly.DestinationPath;
- } else {
+ string key = CompressedAssemblyInfo.GetDictionaryKey (assembly);
+ AndroidTargetArch arch = MonoAndroidHelper.GetTargetArch (assembly);
+ if (!compressedAssembliesInfo.TryGetValue (arch, out Dictionary assembliesInfo)) {
+ throw new InvalidOperationException ($"Internal error: compression assembly info for architecture {arch} not available");
+ }
+
+ if (!assembliesInfo.TryGetValue (key, out CompressedAssemblyInfo info) || info == null) {
Log.LogDebugMessage ($"Assembly missing from {nameof (CompressedAssemblyInfo)}: {key}");
+ return assembly.ItemSpec;
}
- return assembly.ItemSpec;
+ EnsureCompressedAssemblyData (assembly.ItemSpec, info.DescriptorIndex);
+ string assemblyOutputDir;
+ string subDirectory = assembly.GetMetadata ("DestinationSubDirectory");
+ string abi = MonoAndroidHelper.GetAssemblyAbi (assembly);
+ if (!String.IsNullOrEmpty (subDirectory)) {
+ assemblyOutputDir = Path.Combine (compressedOutputDir, abi, subDirectory);
+ } else {
+ assemblyOutputDir = Path.Combine (compressedOutputDir, abi);
+ }
+ AssemblyCompression.CompressionResult result = AssemblyCompression.Compress (compressedAssembly, assemblyOutputDir);
+ if (result != AssemblyCompression.CompressionResult.Success) {
+ switch (result) {
+ case AssemblyCompression.CompressionResult.EncodingFailed:
+ Log.LogMessage ($"Failed to compress {assembly.ItemSpec}");
+ break;
+
+ case AssemblyCompression.CompressionResult.InputTooBig:
+ Log.LogMessage ($"Input assembly {assembly.ItemSpec} exceeds maximum input size");
+ break;
+
+ default:
+ Log.LogMessage ($"Unknown error compressing {assembly.ItemSpec}");
+ break;
+ }
+ return assembly.ItemSpec;
+ }
+ return compressedAssembly.DestinationPath;
}
}
@@ -568,13 +607,14 @@ bool AddFileToArchiveIfNewer (ZipArchiveEx apk, string file, string inArchivePat
void AddAssemblyConfigEntry (ZipArchiveEx apk, string assemblyPath, string configFile)
{
- string inArchivePath = assemblyPath + Path.GetFileName (configFile);
+ string inArchivePath = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyPath + Path.GetFileName (configFile));
existingEntries.Remove (inArchivePath);
- if (!File.Exists (configFile))
+ if (!File.Exists (configFile)) {
return;
+ }
- CompressionMethod compressionMethod = UncompressedMethod;
+ CompressionMethod compressionMethod = GetCompressionMethod (inArchivePath);
if (apk.SkipExistingFile (configFile, inArchivePath, compressionMethod)) {
Log.LogDebugMessage ($"Skipping {configFile} as the archive file is up to date.");
return;
@@ -593,19 +633,48 @@ void AddAssemblyConfigEntry (ZipArchiveEx apk, string assemblyPath, string confi
///
/// Returns the in-archive path for an assembly
///
- string GetAssemblyPath (ITaskItem assembly, bool frameworkAssembly)
+ (string assemblyFilePath, string assemblyDirectoryPath) GetInArchiveAssemblyPath (ITaskItem assembly)
{
- var assembliesPath = AssembliesPath;
- var subDirectory = assembly.GetMetadata ("DestinationSubDirectory");
- if (!string.IsNullOrEmpty (subDirectory)) {
- assembliesPath += subDirectory.Replace ('\\', '/');
- if (!assembliesPath.EndsWith ("/", StringComparison.Ordinal)) {
- assembliesPath += "/";
+ var parts = new List ();
+
+ // The PrepareSatelliteAssemblies task takes care of properly setting `DestinationSubDirectory`, so we can just use it here.
+ string? subDirectory = assembly.GetMetadata ("DestinationSubDirectory")?.Replace ('\\', '/');
+ if (string.IsNullOrEmpty (subDirectory)) {
+ throw new InvalidOperationException ($"Internal error: assembly '{assembly}' lacks the required `DestinationSubDirectory` metadata");
+ }
+
+ string assemblyName = Path.GetFileName (assembly.ItemSpec);
+ if (UseAssemblyStore) {
+ parts.Add (subDirectory);
+ parts.Add (assemblyName);
+ } else {
+ // For discrete assembly entries we need to treat assemblies specially.
+ // All of the assemblies have their names mangled so that the possibility to clash with "real" shared
+ // library names is minimized. All of the assembly entries will start with a special character:
+ //
+ // `_` - for regular assemblies (e.g. `_Mono.Android.dll.so`)
+ // `-` - for satellite assemblies (e.g. `-es-Mono.Android.dll.so`)
+ //
+ // Second of all, we need to treat satellite assemblies with even more care.
+ // If we encounter one of them, we will return the culture as part of the path transformed
+ // so that it forms a `-culture-` assembly file name prefix, not a `culture/` subdirectory.
+ // This is necessary because Android doesn't allow subdirectories in `lib/{ABI}/`
+ //
+ string[] subdirParts = subDirectory.TrimEnd ('/').Split ('/');
+ if (subdirParts.Length == 1) {
+ // Not a satellite assembly
+ parts.Add (subDirectory);
+ parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName));
+ } else if (subdirParts.Length == 2) {
+ parts.Add (subdirParts[0]);
+ parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName, subdirParts[1]));
+ } else {
+ throw new InvalidOperationException ($"Internal error: '{assembly}' `DestinationSubDirectory` metadata has too many components ({parts.Count} instead of 1 or 2)");
}
- } else if (!frameworkAssembly && SatelliteAssembly.TryGetSatelliteCultureAndFileName (assembly.ItemSpec, out var culture, out _)) {
- assembliesPath += culture + "/";
}
- return assembliesPath;
+
+ string assemblyFilePath = MonoAndroidHelper.MakeZipArchivePath (ArchiveAssembliesPath, parts);
+ return (assemblyFilePath, Path.GetDirectoryName (assemblyFilePath) + "/");
}
sealed class LibInfo
@@ -618,14 +687,12 @@ sealed class LibInfo
CompressionMethod GetCompressionMethod (string fileName)
{
- if (uncompressedFileExtensions.Any (x => string.Compare (x.StartsWith (".", StringComparison.OrdinalIgnoreCase) ? x : $".{x}", Path.GetExtension (fileName), StringComparison.OrdinalIgnoreCase) == 0))
- return UncompressedMethod;
- return CompressionMethod.Default;
+ return uncompressedFileExtensions.Contains (Path.GetExtension (fileName)) ? UncompressedMethod : CompressionMethod.Default;
}
void AddNativeLibraryToArchive (ZipArchiveEx apk, string abi, string filesystemPath, string inArchiveFileName)
{
- string archivePath = $"lib/{abi}/{inArchiveFileName}";
+ string archivePath = MakeArchiveLibPath (abi, inArchiveFileName);
existingEntries.Remove (archivePath);
CompressionMethod compressionMethod = GetCompressionMethod (archivePath);
if (apk.SkipExistingFile (filesystemPath, archivePath, compressionMethod)) {
@@ -809,7 +876,7 @@ private void AddAdditionalNativeLibraries (ArchiveFileList files, string [] supp
void AddNativeLibrary (ArchiveFileList files, string path, string abi, string archiveFileName)
{
string fileName = string.IsNullOrEmpty (archiveFileName) ? Path.GetFileName (path) : archiveFileName;
- var item = (filePath: path, archivePath: $"lib/{abi}/{fileName}");
+ var item = (filePath: path, archivePath: MakeArchiveLibPath (abi, fileName));
if (files.Any (x => x.archivePath == item.archivePath)) {
Log.LogCodedWarning ("XA4301", path, 0, Properties.Resources.XA4301, item.archivePath);
return;
@@ -833,5 +900,7 @@ void LogSanitizerError (string message)
{
Log.LogError (message);
}
+
+ static string MakeArchiveLibPath (string abi, string fileName) => MonoAndroidHelper.MakeZipArchivePath (ArchiveLibPath, abi, fileName);
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateDynamicFeatureManifest.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateDynamicFeatureManifest.cs
new file mode 100644
index 00000000000..48e6fbb9bca
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateDynamicFeatureManifest.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+
+using Xamarin.Android.Tools;
+using Microsoft.Android.Build.Tasks;
+
+namespace Xamarin.Android.Tasks
+{
+ public class CreateDynamicFeatureManifest : AndroidTask
+ {
+ readonly XNamespace androidNS = "http://schemas.android.com/apk/res/android";
+ readonly XNamespace distNS = "http://schemas.android.com/apk/distribution";
+ readonly XNamespace toolsNS = "http://schemas.android.com/tools";
+
+ public override string TaskPrefix => "CDFM";
+
+ [Required]
+ public string FeatureSplitName { get; set; }
+
+ [Required]
+ public string FeatureDeliveryType { get; set; }
+
+ [Required]
+ public string FeatureType { get; set; }
+
+ [Required]
+ public string PackageName { get; set; }
+
+ [Required]
+ public ITaskItem OutputFile { get; set; }
+
+ public string FeatureTitleResource { get; set; }
+
+ public string MinSdkVersion { get; set; }
+
+ public string TargetSdkVersion { get; set; }
+
+ public bool IsFeatureSplit { get; set; } = false;
+ public bool IsInstant { get; set; } = false;
+ public bool HasCode { get; set; } = false;
+
+ public override bool RunTask ()
+ {
+ XDocument doc = new XDocument ();
+ switch (FeatureType.ToLowerInvariant ()) {
+ case "assetpack":
+ GenerateAssetPackManifest (doc);
+ break;
+ default:
+ GenerateFeatureManifest (doc);
+ break;
+ }
+ doc.SaveIfChanged (OutputFile.ItemSpec);
+ return !Log.HasLoggedErrors;
+ }
+
+ void GenerateFeatureManifest (XDocument doc)
+ {
+ XAttribute featureTitleResource = null;
+ if (!string.IsNullOrEmpty (FeatureTitleResource))
+ featureTitleResource = new XAttribute (distNS + "title", FeatureTitleResource);
+ XElement usesSdk = new XElement ("uses-sdk");
+ if (!string.IsNullOrEmpty (MinSdkVersion))
+ usesSdk.Add (new XAttribute (androidNS + "minSdkVersion", MinSdkVersion));
+ if (!string.IsNullOrEmpty (MinSdkVersion))
+ usesSdk.Add (new XAttribute (androidNS + "targetSdkVersion", TargetSdkVersion));
+ doc.Add (new XElement ("manifest",
+ new XAttribute (XNamespace.Xmlns + "android", androidNS),
+ new XAttribute (XNamespace.Xmlns + "tools", toolsNS),
+ new XAttribute (XNamespace.Xmlns + "dist", distNS),
+ new XAttribute (androidNS + "versionCode", "1"),
+ new XAttribute (androidNS + "versionName", "1.0"),
+ new XAttribute ("package", PackageName),
+ new XAttribute ("featureSplit", FeatureSplitName),
+ new XAttribute (androidNS + "isFeatureSplit", IsFeatureSplit),
+ new XElement (distNS + "module",
+ featureTitleResource,
+ new XAttribute (distNS + "instant", IsInstant),
+ new XElement (distNS + "delivery",
+ GetDistribution ()
+ ),
+ new XElement (distNS + "fusing",
+ new XAttribute (distNS + "include", true)
+ )
+ ),
+ usesSdk,
+ new XElement ("application",
+ new XAttribute (androidNS + "hasCode", HasCode),
+ new XAttribute (toolsNS + "replace", "android:hasCode")
+ )
+ )
+ );
+ }
+
+ void GenerateAssetPackManifest (XDocument doc)
+ {
+ doc.Add (new XElement ("manifest",
+ new XAttribute (XNamespace.Xmlns + "android", androidNS),
+ new XAttribute (XNamespace.Xmlns + "tools", toolsNS),
+ new XAttribute (XNamespace.Xmlns + "dist", distNS),
+ new XAttribute ("package", PackageName),
+ new XAttribute ("split", FeatureSplitName),
+ new XElement (distNS + "module",
+ new XAttribute (distNS + "type", "asset-pack"),
+ new XElement (distNS + "delivery",
+ GetDistribution ()
+ ),
+ new XElement (distNS + "fusing",
+ new XAttribute (distNS + "include", true)
+ )
+ )
+ )
+ );
+ }
+
+ XElement GetDistribution ()
+ {
+ XElement distribution;
+ switch (FeatureDeliveryType.ToLowerInvariant ())
+ {
+ case "ondemand":
+ distribution = new XElement (distNS + "on-demand");
+ break;
+ case "fastfollow":
+ distribution = new XElement (distNS + "fast-follow");
+ break;
+ case "installtime":
+ default:
+ distribution = new XElement (distNS + "install-time");
+ break;
+ }
+ return distribution;
+ }
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
index 441299c527d..81a11497da4 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
@@ -4,6 +4,8 @@
using Microsoft.Build.Framework;
using Microsoft.Android.Build.Tasks;
+using Xamarin.Android.Tools;
+
namespace Xamarin.Android.Tasks
{
public class GenerateCompressedAssembliesNativeSourceFiles : AndroidTask
@@ -41,38 +43,54 @@ void GenerateCompressedAssemblySources ()
return;
}
- var assemblies = new SortedDictionary (StringComparer.Ordinal);
- foreach (ITaskItem assembly in ResolvedAssemblies) {
- if (bool.TryParse (assembly.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
- continue;
- }
+ Dictionary> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (
+ ResolvedAssemblies,
+ SupportedAbis,
+ validate: true,
+ shouldSkip: (ITaskItem asm) => bool.TryParse (asm.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value
+ );
+ var archAssemblies = new Dictionary> ();
+ var counters = new Dictionary ();
+
+ foreach (var kvpPerArch in perArchAssemblies) {
+ AndroidTargetArch arch = kvpPerArch.Key;
+ Dictionary resolvedArchAssemblies = kvpPerArch.Value;
+
+ foreach (var kvp in resolvedArchAssemblies) {
+ ITaskItem assembly = kvp.Value;
+
+ if (!archAssemblies.TryGetValue (arch, out Dictionary assemblies)) {
+ assemblies = new Dictionary (StringComparer.OrdinalIgnoreCase);
+ archAssemblies.Add (arch, assemblies);
+ }
- var assemblyKey = CompressedAssemblyInfo.GetDictionaryKey (assembly);
- if (assemblies.ContainsKey (assemblyKey)) {
- Log.LogDebugMessage ($"Skipping duplicate assembly: {assembly.ItemSpec}");
- continue;
- }
+ var assemblyKey = CompressedAssemblyInfo.GetDictionaryKey (assembly);
+ if (assemblies.ContainsKey (assemblyKey)) {
+ Log.LogDebugMessage ($"Skipping duplicate assembly: {assembly.ItemSpec} (arch {MonoAndroidHelper.GetAssemblyAbi(assembly)})");
+ continue;
+ }
- var fi = new FileInfo (assembly.ItemSpec);
- if (!fi.Exists) {
- Log.LogError ($"Assembly {assembly.ItemSpec} does not exist");
- continue;
- }
+ var fi = new FileInfo (assembly.ItemSpec);
+ if (!fi.Exists) {
+ Log.LogError ($"Assembly {assembly.ItemSpec} does not exist");
+ continue;
+ }
- assemblies.Add (assemblyKey, new CompressedAssemblyInfo (checked((uint)fi.Length)));
- }
- uint index = 0;
- foreach (var kvp in assemblies) {
- kvp.Value.DescriptorIndex = index++;
+ if (!counters.TryGetValue (arch, out uint counter)) {
+ counter = 0;
+ }
+ assemblies.Add (assemblyKey, new CompressedAssemblyInfo (checked((uint)fi.Length), counter++, arch, Path.GetFileNameWithoutExtension (assembly.ItemSpec)));
+ counters[arch] = counter;
+ }
}
string key = CompressedAssemblyInfo.GetKey (ProjectFullPath);
Log.LogDebugMessage ($"Storing compression assemblies info with key '{key}'");
- BuildEngine4.RegisterTaskObjectAssemblyLocal (key, assemblies, RegisteredTaskObjectLifetime.Build);
- Generate (assemblies);
+ BuildEngine4.RegisterTaskObjectAssemblyLocal (key, archAssemblies, RegisteredTaskObjectLifetime.Build);
+ Generate (archAssemblies);
- void Generate (IDictionary dict)
+ void Generate (Dictionary> dict)
{
var composer = new CompressedAssembliesNativeAssemblyGenerator (Log, dict);
LLVMIR.LlvmIrModule compressedAssemblies = composer.Construct ();
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
index 453885d1758..8bd49bfcd99 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
@@ -2,16 +2,11 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
-using System.IO.MemoryMappedFiles;
using System.Linq;
-using System.Reflection;
-using System.Text;
using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
using Mono.Cecil;
-
+using Microsoft.Build.Utilities;
using Java.Interop.Tools.Cecil;
using Java.Interop.Tools.Diagnostics;
@@ -28,7 +23,7 @@ namespace Xamarin.Android.Tasks
public class GenerateJavaStubs : AndroidTask
{
- public const string MarshalMethodsRegisterTaskKey = ".:!MarshalMethods!:.";
+ public const string NativeCodeGenStateRegisterTaskKey = ".:!MarshalMethods!:.";
public override string TaskPrefix => "GJS";
@@ -95,7 +90,7 @@ public class GenerateJavaStubs : AndroidTask
public ITaskItem[] Environments { get; set; }
[Output]
- public string [] GeneratedBinaryTypeMaps { get; set; }
+ public ITaskItem[] GeneratedBinaryTypeMaps { get; set; }
internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration";
@@ -103,11 +98,7 @@ public override bool RunTask ()
{
try {
bool useMarshalMethods = !Debug && EnableMarshalMethods;
- // We're going to do 3 steps here instead of separate tasks so
- // we can share the list of JLO TypeDefinitions between them
- using (XAAssemblyResolver res = MakeResolver (useMarshalMethods)) {
- Run (res, useMarshalMethods);
- }
+ Run (useMarshalMethods);
} catch (XamarinAndroidException e) {
Log.LogCodedError (string.Format ("XA{0:0000}", e.Code), e.MessageWithoutCode);
if (MonoAndroidHelper.LogInternalExceptions)
@@ -124,210 +115,207 @@ public override bool RunTask ()
return !Log.HasLoggedErrors;
}
- XAAssemblyResolver MakeResolver (bool useMarshalMethods)
+ XAAssemblyResolver MakeResolver (bool useMarshalMethods, AndroidTargetArch targetArch, Dictionary assemblies)
{
- var readerParams = new ReaderParameters();
+ var readerParams = new ReaderParameters ();
if (useMarshalMethods) {
readerParams.ReadWrite = true;
readerParams.InMemory = true;
}
- var res = new XAAssemblyResolver (Log, loadDebugSymbols: true, loadReaderParameters: readerParams);
- foreach (var dir in FrameworkDirectories) {
- if (Directory.Exists (dir.ItemSpec)) {
- res.FrameworkSearchDirectories.Add (dir.ItemSpec);
+ var res = new XAAssemblyResolver (targetArch, Log, loadDebugSymbols: true, loadReaderParameters: readerParams);
+ var uniqueDirs = new HashSet (StringComparer.OrdinalIgnoreCase);
+
+ Log.LogDebugMessage ($"Adding search directories to new architecture {targetArch} resolver:");
+ foreach (var kvp in assemblies) {
+ string assemblyDir = Path.GetDirectoryName (kvp.Value.ItemSpec);
+ if (uniqueDirs.Contains (assemblyDir)) {
+ continue;
}
+
+ uniqueDirs.Add (assemblyDir);
+ res.SearchDirectories.Add (assemblyDir);
+ Log.LogDebugMessage ($" {assemblyDir}");
}
return res;
}
- void Run (XAAssemblyResolver res, bool useMarshalMethods)
+ void Run (bool useMarshalMethods)
{
PackageNamingPolicy pnp;
JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse (PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseCrc64;
- Dictionary>? abiSpecificAssembliesByPath = null;
- if (useMarshalMethods) {
- abiSpecificAssembliesByPath = new Dictionary> (StringComparer.Ordinal);
+ // We will process each architecture completely separately as both type maps and marshal methods are strictly per-architecture and
+ // the assemblies should be processed strictly per architecture. Generation of JCWs, and the manifest are ABI-agnostic.
+ // We will generate them only for the first architecture, whichever it is.
+ Dictionary> allAssembliesPerArch = MonoAndroidHelper.GetPerArchAssemblies (ResolvedAssemblies, SupportedAbis, validate: true);
+
+ // Should "never" happen...
+ if (allAssembliesPerArch.Count != SupportedAbis.Length) {
+ // ...but it happens at least in our `BuildAMassiveApp` test, where `SupportedAbis` mentions only the `x86` and `armeabi-v7a` ABIs, but `ResolvedAssemblies` contains
+ // entries for all the ABIs we support, so let's be flexible and ignore the extra architectures but still error out if there are less architectures than supported ABIs.
+ if (allAssembliesPerArch.Count < SupportedAbis.Length) {
+ throw new InvalidOperationException ($"Internal error: number of architectures ({allAssembliesPerArch.Count}) must equal the number of target ABIs ({SupportedAbis.Length})");
+ }
}
- // Put every assembly we'll need in the resolver
- bool hasExportReference = false;
- bool haveMonoAndroid = false;
- var allTypemapAssemblies = new Dictionary (StringComparer.OrdinalIgnoreCase);
- var userAssemblies = new Dictionary (StringComparer.OrdinalIgnoreCase);
-
- foreach (var assembly in ResolvedAssemblies) {
- bool value;
- if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) && value) {
- Log.LogDebugMessage ($"Skipping Java Stub Generation for {assembly.ItemSpec}");
- continue;
+ // ...or this...
+ foreach (string abi in SupportedAbis) {
+ AndroidTargetArch arch = MonoAndroidHelper.AbiToTargetArch (abi);
+ if (!allAssembliesPerArch.ContainsKey (arch)) {
+ throw new InvalidOperationException ($"Internal error: no assemblies for architecture '{arch}', which corresponds to target abi '{abi}'");
}
+ }
- bool addAssembly = false;
- string fileName = Path.GetFileName (assembly.ItemSpec);
- if (!hasExportReference && String.Compare ("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
- hasExportReference = true;
- addAssembly = true;
- } else if (!haveMonoAndroid && String.Compare ("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
- haveMonoAndroid = true;
- addAssembly = true;
- } else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains (fileName)) {
- if (!bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) || !value) {
- string name = Path.GetFileNameWithoutExtension (fileName);
- if (!userAssemblies.ContainsKey (name))
- userAssemblies.Add (name, assembly.ItemSpec);
- addAssembly = true;
- }
+ // ...as well as this
+ Dictionary> userAssembliesPerArch = MonoAndroidHelper.GetPerArchAssemblies (ResolvedUserAssemblies, SupportedAbis, validate: true);
+ foreach (var kvp in userAssembliesPerArch) {
+ if (!allAssembliesPerArch.TryGetValue (kvp.Key, out Dictionary allAssemblies)) {
+ throw new InvalidOperationException ($"Internal error: found user assemblies for architecture '{kvp.Key}' which isn't found in ResolvedAssemblies");
}
- if (addAssembly) {
- MaybeAddAbiSpecifcAssembly (assembly, fileName);
- if (!allTypemapAssemblies.ContainsKey (assembly.ItemSpec)) {
- allTypemapAssemblies.Add (assembly.ItemSpec, assembly);
+ foreach (var asmKvp in kvp.Value) {
+ if (!allAssemblies.ContainsKey (asmKvp.Key)) {
+ throw new InvalidOperationException ($"Internal error: user assembly '{asmKvp.Value}' not found in ResolvedAssemblies");
}
}
-
- res.Load (MonoAndroidHelper.GetTargetArch (assembly), assembly.ItemSpec);
}
- // However we only want to look for JLO types in user code for Java stub code generation
- foreach (var asm in ResolvedUserAssemblies) {
- if (bool.TryParse (asm.GetMetadata (AndroidSkipJavaStubGeneration), out bool value) && value) {
- Log.LogDebugMessage ($"Skipping Java Stub Generation for {asm.ItemSpec}");
- continue;
- }
- res.Load (MonoAndroidHelper.GetTargetArch (asm), asm.ItemSpec);
- MaybeAddAbiSpecifcAssembly (asm, Path.GetFileName (asm.ItemSpec));
- if (!allTypemapAssemblies.ContainsKey (asm.ItemSpec)) {
- allTypemapAssemblies.Add (asm.ItemSpec, asm);
- }
+ // Now that "never" never happened, we can proceed knowing that at least the assembly sets are the same for each architecture
+ var nativeCodeGenStates = new Dictionary ();
+ bool generateJavaCode = true;
+ NativeCodeGenState? templateCodeGenState = null;
- string name = Path.GetFileNameWithoutExtension (asm.ItemSpec);
- if (!userAssemblies.ContainsKey (name))
- userAssemblies.Add (name, asm.ItemSpec);
- }
+ foreach (var kvp in allAssembliesPerArch) {
+ AndroidTargetArch arch = kvp.Key;
+ Dictionary archAssemblies = kvp.Value;
+ (bool success, NativeCodeGenState? state) = GenerateJavaSourcesAndMaybeClassifyMarshalMethods (arch, archAssemblies, MaybeGetArchAssemblies (userAssembliesPerArch, arch), useMarshalMethods, generateJavaCode);
- // Step 1 - Find all the JLO types
- var cache = new TypeDefinitionCache ();
- var scanner = new XAJavaTypeScanner (Log, cache) {
- ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
- };
- List allJavaTypes = scanner.GetJavaTypes (allTypemapAssemblies.Values, res);
- var javaTypes = new List ();
+ if (!success) {
+ return;
+ }
- foreach (JavaType jt in allJavaTypes) {
- // Whem marshal methods are in use we do not want to skip non-user assemblies (such as Mono.Android) - we need to generate JCWs for them during
- // application build, unlike in Debug configuration or when marshal methods are disabled, in which case we use JCWs generated during Xamarin.Android
- // build and stored in a jar file.
- if ((!useMarshalMethods && !userAssemblies.ContainsKey (jt.Type.Module.Assembly.Name.Name)) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (jt.Type, cache)) {
- continue;
+ if (generateJavaCode) {
+ templateCodeGenState = state;
+ generateJavaCode = false;
}
- javaTypes.Add (jt);
+
+ nativeCodeGenStates.Add (arch, state);
}
- MarshalMethodsClassifier classifier = null;
- if (useMarshalMethods) {
- classifier = new MarshalMethodsClassifier (cache, res, Log);
+ if (templateCodeGenState == null) {
+ throw new InvalidOperationException ($"Internal error: no native code generator state defined");
}
+ JCWGenerator.EnsureAllArchitecturesAreIdentical (Log, nativeCodeGenStates);
- // Step 2 - Generate Java stub code
- var success = CreateJavaSources (javaTypes, cache, classifier, useMarshalMethods);
- if (!success)
- return;
+ NativeCodeGenState.Template = templateCodeGenState;
+ BuildEngine4.RegisterTaskObjectAssemblyLocal (ProjectSpecificTaskObjectKey (NativeCodeGenStateRegisterTaskKey), nativeCodeGenStates, RegisteredTaskObjectLifetime.Build);
if (useMarshalMethods) {
// We need to parse the environment files supplied by the user to see if they want to use broken exception transitions. This information is needed
// in order to properly generate wrapper methods in the marshal methods assembly rewriter.
// We don't care about those generated by us, since they won't contain the `XA_BROKEN_EXCEPTION_TRANSITIONS` variable we look for.
var environmentParser = new EnvironmentFilesParser ();
+ bool brokenExceptionTransitionsEnabled = environmentParser.AreBrokenExceptionTransitionsEnabled (Environments);
- Dictionary assemblyPaths = AddMethodsFromAbiSpecificAssemblies (classifier, res, abiSpecificAssembliesByPath);
+ foreach (var kvp in nativeCodeGenStates) {
+ NativeCodeGenState state = kvp.Value;
+ RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled);
+ state.Classifier.AddSpecialCaseMethods ();
- var rewriter = new MarshalMethodsAssemblyRewriter (classifier.MarshalMethods, classifier.Assemblies, assemblyPaths, Log);
- rewriter.Rewrite (res, environmentParser.AreBrokenExceptionTransitionsEnabled (Environments));
- }
-
- // Step 3 - Generate type maps
- // Type mappings need to use all the assemblies, always.
- WriteTypeMappings (allJavaTypes, cache);
-
- // We need to save a map of .NET type -> ACW type for resource file fixups
- var managed = new Dictionary (javaTypes.Count, StringComparer.Ordinal);
- var java = new Dictionary (javaTypes.Count, StringComparer.Ordinal);
-
- var managedConflicts = new Dictionary> (0, StringComparer.Ordinal);
- var javaConflicts = new Dictionary> (0, StringComparer.Ordinal);
-
- using (var acw_map = MemoryStreamPool.Shared.CreateStreamWriter ()) {
- foreach (JavaType jt in javaTypes) {
- TypeDefinition type = jt.Type;
- string managedKey = type.FullName.Replace ('/', '.');
- string javaKey = JavaNativeTypeManager.ToJniName (type, cache).Replace ('/', '.');
-
- acw_map.Write (type.GetPartialAssemblyQualifiedName (cache));
- acw_map.Write (';');
- acw_map.Write (javaKey);
- acw_map.WriteLine ();
-
- TypeDefinition conflict;
- bool hasConflict = false;
- if (managed.TryGetValue (managedKey, out conflict)) {
- if (!conflict.Module.Name.Equals (type.Module.Name)) {
- if (!managedConflicts.TryGetValue (managedKey, out var list))
- managedConflicts.Add (managedKey, list = new List { conflict.GetPartialAssemblyName (cache) });
- list.Add (type.GetPartialAssemblyName (cache));
- }
- hasConflict = true;
- }
- if (java.TryGetValue (javaKey, out conflict)) {
- if (!conflict.Module.Name.Equals (type.Module.Name)) {
- if (!javaConflicts.TryGetValue (javaKey, out var list))
- javaConflicts.Add (javaKey, list = new List { conflict.GetAssemblyQualifiedName (cache) });
- list.Add (type.GetAssemblyQualifiedName (cache));
- success = false;
- }
- hasConflict = true;
+ Log.LogDebugMessage ($"[{state.TargetArch}] Number of generated marshal methods: {state.Classifier.MarshalMethods.Count}");
+ if (state.Classifier.RejectedMethodCount > 0) {
+ Log.LogWarning ($"[{state.TargetArch}] Number of methods in the project that will be registered dynamically: {state.Classifier.RejectedMethodCount}");
}
- if (!hasConflict) {
- managed.Add (managedKey, type);
- java.Add (javaKey, type);
-
- acw_map.Write (managedKey);
- acw_map.Write (';');
- acw_map.Write (javaKey);
- acw_map.WriteLine ();
-
- acw_map.Write (JavaNativeTypeManager.ToCompatJniName (type, cache).Replace ('/', '.'));
- acw_map.Write (';');
- acw_map.Write (javaKey);
- acw_map.WriteLine ();
+
+ if (state.Classifier.WrappedMethodCount > 0) {
+ // TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
+ Log.LogDebugMessage ($"[{state.TargetArch}] Number of methods in the project that need marshal method wrappers: {state.Classifier.WrappedMethodCount}");
}
}
+ }
+
+ bool typemapsAreAbiAgnostic = Debug && !GenerateNativeAssembly;
+ bool first = true;
+ foreach (var kvp in nativeCodeGenStates) {
+ if (!first && typemapsAreAbiAgnostic) {
+ Log.LogDebugMessage ("Typemaps: it's a debug build and type maps are ABI-agnostic, not processing more ABIs");
+ break;
+ }
+
+ NativeCodeGenState state = kvp.Value;
+ first = false;
+ WriteTypeMappings (state);
+ }
- acw_map.Flush ();
- Files.CopyIfStreamChanged (acw_map.BaseStream, AcwMapFile);
+ var acwMapGen = new ACWMapGenerator (Log);
+ if (!acwMapGen.Generate (templateCodeGenState, AcwMapFile)) {
+ Log.LogDebugMessage ("ACW map generation failed");
}
- foreach (var kvp in managedConflicts) {
- Log.LogCodedWarning ("XA4214", Properties.Resources.XA4214, kvp.Key, string.Join (", ", kvp.Value));
- Log.LogCodedWarning ("XA4214", Properties.Resources.XA4214_Result, kvp.Key, kvp.Value [0]);
+ IList additionalProviders = MergeManifest (templateCodeGenState, MaybeGetArchAssemblies (userAssembliesPerArch, templateCodeGenState.TargetArch));
+ GenerateAdditionalProviderSources (templateCodeGenState, additionalProviders);
+
+ Dictionary MaybeGetArchAssemblies (Dictionary> dict, AndroidTargetArch arch)
+ {
+ if (!dict.TryGetValue (arch, out Dictionary archDict)) {
+ return new Dictionary (StringComparer.OrdinalIgnoreCase);
+ }
+
+ return archDict;
+ }
+ }
+
+ void GenerateAdditionalProviderSources (NativeCodeGenState codeGenState, IList additionalProviders)
+ {
+ // Create additional runtime provider java sources.
+ string providerTemplateFile = "MonoRuntimeProvider.Bundled.java";
+ string providerTemplate = GetResource (providerTemplateFile);
+
+ foreach (var provider in additionalProviders) {
+ var contents = providerTemplate.Replace ("MonoRuntimeProvider", provider);
+ var real_provider = Path.Combine (OutputDirectory, "src", "mono", provider + ".java");
+ Files.CopyIfStringChanged (contents, real_provider);
}
- foreach (var kvp in javaConflicts) {
- Log.LogCodedError ("XA4215", Properties.Resources.XA4215, kvp.Key);
- foreach (var typeName in kvp.Value)
- Log.LogCodedError ("XA4215", Properties.Resources.XA4215_Details, kvp.Key, typeName);
+ // Create additional application java sources.
+ StringWriter regCallsWriter = new StringWriter ();
+ regCallsWriter.WriteLine ("\t\t// Application and Instrumentation ACWs must be registered first.");
+ foreach (TypeDefinition type in codeGenState.JavaTypesForJCW) {
+ if (JavaNativeTypeManager.IsApplication (type, codeGenState.TypeCache) || JavaNativeTypeManager.IsInstrumentation (type, codeGenState.TypeCache)) {
+ if (codeGenState.Classifier != null && !codeGenState.Classifier.FoundDynamicallyRegisteredMethods (type)) {
+ continue;
+ }
+
+ string javaKey = JavaNativeTypeManager.ToJniName (type, codeGenState.TypeCache).Replace ('/', '.');
+ regCallsWriter.WriteLine (
+ "\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);",
+ type.GetAssemblyQualifiedName (codeGenState.TypeCache),
+ javaKey
+ );
+ }
}
+ regCallsWriter.Close ();
- // Step 3 - Merge [Activity] and friends into AndroidManifest.xml
+ var real_app_dir = Path.Combine (OutputDirectory, "src", "mono", "android", "app");
+ string applicationTemplateFile = "ApplicationRegistration.java";
+ SaveResource (
+ applicationTemplateFile,
+ applicationTemplateFile,
+ real_app_dir,
+ template => template.Replace ("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString ())
+ );
+ }
+
+ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary userAssemblies)
+ {
var manifest = new ManifestDocument (ManifestTemplate) {
PackageName = PackageName,
VersionName = VersionName,
ApplicationLabel = ApplicationLabel ?? PackageName,
Placeholders = ManifestPlaceholders,
- Resolver = res,
+ Resolver = codeGenState.Resolver,
SdkDir = AndroidSdkDir,
TargetSdkVersion = AndroidSdkPlatform,
MinSdkVersion = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (SupportedOSPlatformVersion).ToString (),
@@ -342,7 +330,7 @@ void Run (XAAssemblyResolver res, bool useMarshalMethods)
} else if (!string.IsNullOrEmpty (VersionCode)) {
manifest.VersionCode = VersionCode;
}
- manifest.Assemblies.AddRange (userAssemblies.Values);
+ manifest.Assemblies.AddRange (userAssemblies.Values.Select (item => item.ItemSpec));
if (!String.IsNullOrWhiteSpace (CheckedBuild)) {
// We don't validate CheckedBuild value here, this will be done in BuildApk. We just know that if it's
@@ -351,216 +339,67 @@ void Run (XAAssemblyResolver res, bool useMarshalMethods)
manifest.ForceExtractNativeLibs = true;
}
- var additionalProviders = manifest.Merge (Log, cache, allJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);
+ IList additionalProviders = manifest.Merge (Log, codeGenState.TypeCache, codeGenState.AllJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments);
// Only write the new manifest if it actually changed
if (manifest.SaveIfChanged (Log, MergedAndroidManifestOutput)) {
Log.LogDebugMessage ($"Saving: {MergedAndroidManifestOutput}");
}
- // Create additional runtime provider java sources.
- string providerTemplateFile = "MonoRuntimeProvider.Bundled.java";
- string providerTemplate = GetResource (providerTemplateFile);
-
- foreach (var provider in additionalProviders) {
- var contents = providerTemplate.Replace ("MonoRuntimeProvider", provider);
- var real_provider = Path.Combine (OutputDirectory, "src", "mono", provider + ".java");
- Files.CopyIfStringChanged (contents, real_provider);
- }
-
- // Create additional application java sources.
- StringWriter regCallsWriter = new StringWriter ();
- regCallsWriter.WriteLine ("\t\t// Application and Instrumentation ACWs must be registered first.");
- foreach (JavaType jt in javaTypes) {
- TypeDefinition type = jt.Type;
- if (JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache)) {
- if (classifier != null && !classifier.FoundDynamicallyRegisteredMethods (type)) {
- continue;
- }
-
- string javaKey = JavaNativeTypeManager.ToJniName (type, cache).Replace ('/', '.');
- regCallsWriter.WriteLine ("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);",
- type.GetAssemblyQualifiedName (cache), javaKey);
- }
- }
- regCallsWriter.Close ();
-
- var real_app_dir = Path.Combine (OutputDirectory, "src", "mono", "android", "app");
- string applicationTemplateFile = "ApplicationRegistration.java";
- SaveResource (applicationTemplateFile, applicationTemplateFile, real_app_dir,
- template => template.Replace ("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString ()));
-
- if (useMarshalMethods) {
- classifier.AddSpecialCaseMethods ();
-
- Log.LogDebugMessage ($"Number of generated marshal methods: {classifier.MarshalMethods.Count}");
-
- if (classifier.RejectedMethodCount > 0) {
- Log.LogWarning ($"Number of methods in the project that will be registered dynamically: {classifier.RejectedMethodCount}");
- }
-
- if (classifier.WrappedMethodCount > 0) {
- // TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
- Log.LogDebugMessage ($"Number of methods in the project that need marshal method wrappers: {classifier.WrappedMethodCount}");
- }
- }
-
- void MaybeAddAbiSpecifcAssembly (ITaskItem assembly, string fileName)
- {
- if (abiSpecificAssembliesByPath == null) {
- return;
- }
-
- string? abi = assembly.GetMetadata ("Abi");
- if (!String.IsNullOrEmpty (abi)) {
- if (!abiSpecificAssembliesByPath.TryGetValue (fileName, out List? items)) {
- items = new List ();
- abiSpecificAssembliesByPath.Add (fileName, items);
- }
-
- items.Add (assembly);
- }
- }
+ return additionalProviders;
}
- AssemblyDefinition LoadAssembly (string path, XAAssemblyResolver? resolver = null)
+ (bool success, NativeCodeGenState? stubsState) GenerateJavaSourcesAndMaybeClassifyMarshalMethods (AndroidTargetArch arch, Dictionary assemblies, Dictionary userAssemblies, bool useMarshalMethods, bool generateJavaCode)
{
- string pdbPath = Path.ChangeExtension (path, ".pdb");
- var readerParameters = new ReaderParameters {
- AssemblyResolver = resolver,
- InMemory = false,
- ReadingMode = ReadingMode.Immediate,
- ReadSymbols = File.Exists (pdbPath),
- ReadWrite = false,
- };
-
- MemoryMappedViewStream? viewStream = null;
- try {
- // Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
- using var fileStream = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
- using var mappedFile = MemoryMappedFile.CreateFromFile (
- fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
- viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read);
+ XAAssemblyResolver resolver = MakeResolver (useMarshalMethods, arch, assemblies);
+ var tdCache = new TypeDefinitionCache ();
+ (List allJavaTypes, List javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods);
+ var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods);
+ var jcwGenerator = new JCWGenerator (Log, jcwContext);
+ bool success;
- AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, readerParameters).Assembly;
-
- // We transferred the ownership of the viewStream to the collection.
- viewStream = null;
-
- return result;
- } finally {
- viewStream?.Dispose ();
+ if (generateJavaCode) {
+ success = jcwGenerator.GenerateAndClassify (AndroidSdkPlatform, outputPath: Path.Combine (OutputDirectory, "src"), ApplicationJavaClass);
+ } else {
+ success = jcwGenerator.Classify (AndroidSdkPlatform);
}
- }
- bool CreateJavaSources (IEnumerable newJavaTypes, TypeDefinitionCache cache, MarshalMethodsClassifier classifier, bool useMarshalMethods)
- {
- if (useMarshalMethods && classifier == null) {
- throw new ArgumentNullException (nameof (classifier));
+ if (!success) {
+ return (false, null);
}
- string outputPath = Path.Combine (OutputDirectory, "src");
- string monoInit = GetMonoInitSource (AndroidSdkPlatform);
- bool hasExportReference = ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll");
- bool generateOnCreateOverrides = int.Parse (AndroidSdkPlatform) <= 10;
+ return (true, new NativeCodeGenState (arch, tdCache, resolver, allJavaTypes, javaTypesForJCW, jcwGenerator.Classifier));
+ }
- var reader_options = new CallableWrapperReaderOptions {
- DefaultApplicationJavaClass = ApplicationJavaClass,
- DefaultGenerateOnCreateOverrides = generateOnCreateOverrides,
- DefaultMonoRuntimeInitialization = monoInit,
- MethodClassifier = classifier,
- };
- var writer_options = new CallableWrapperWriterOptions {
- CodeGenerationTarget = JavaPeerStyle.XAJavaInterop1
+ (List allJavaTypes, List javaTypesForJCW) ScanForJavaTypes (XAAssemblyResolver res, TypeDefinitionCache cache, Dictionary assemblies, Dictionary userAssemblies, bool useMarshalMethods)
+ {
+ var scanner = new XAJavaTypeScanner (res.TargetArch, Log, cache) {
+ ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
};
+ List allJavaTypes = scanner.GetJavaTypes (assemblies.Values, res);
+ var javaTypesForJCW = new List ();
- bool ok = true;
- foreach (JavaType jt in newJavaTypes) {
- TypeDefinition t = jt.Type; // JCW generator doesn't care about ABI-specific types or token ids
- if (t.IsInterface) {
- // Interfaces are in typemap but they shouldn't have JCW generated for them
+ foreach (TypeDefinition type in allJavaTypes) {
+ // When marshal methods are in use we do not want to skip non-user assemblies (such as Mono.Android) - we need to generate JCWs for them during
+ // application build, unlike in Debug configuration or when marshal methods are disabled, in which case we use JCWs generated during Xamarin.Android
+ // build and stored in a jar file.
+ if ((!useMarshalMethods && !userAssemblies.ContainsKey (type.Module.Assembly.Name.Name)) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (type, cache)) {
continue;
}
-
- using (var writer = MemoryStreamPool.Shared.CreateStreamWriter ()) {
- try {
- var jcw_type = CecilImporter.CreateType (t, cache, reader_options);
-
- jcw_type.Generate (writer, writer_options);
-
- if (useMarshalMethods) {
- if (classifier.FoundDynamicallyRegisteredMethods (t)) {
- Log.LogWarning ($"Type '{t.GetAssemblyQualifiedName (cache)}' will register some of its Java override methods dynamically. This may adversely affect runtime performance. See preceding warnings for names of dynamically registered methods.");
- }
- }
-
- writer.Flush ();
-
- var path = jcw_type.GetDestinationPath (outputPath);
- Files.CopyIfStreamChanged (writer.BaseStream, path);
- if (jcw_type.HasExport && !hasExportReference)
- Diagnostic.Error (4210, Properties.Resources.XA4210);
- } catch (XamarinAndroidException xae) {
- ok = false;
- Log.LogError (
- subcategory: "",
- errorCode: "XA" + xae.Code,
- helpKeyword: string.Empty,
- file: xae.SourceFile,
- lineNumber: xae.SourceLine,
- columnNumber: 0,
- endLineNumber: 0,
- endColumnNumber: 0,
- message: xae.MessageWithoutCode,
- messageArgs: Array.Empty