Android manifest file merge rules

Hi all,

Problem outline

Google recently changed their approach with respect to Android manifest files uploaded to Google Play, so that it is now no longer possible to upload a new version of an App which manifest file includes a duplicate permission request. This is a problem for our App, as we use two different libraries (NuGet-packages) that require the ACCESS_COARSE_LOCATION-permission - one being requested using the uses-permission-element, the other using the uses-permission-sdk-23-element (with API-level 23 lying midway our supported API-range). Although the Xamarin.Android manifest file merger is capable of reducing exact duplicates of nodes in manifest files, it does not see these two permission requests as duplicates. Hence, these elements are not reduced, leading Google to conclude that the same permission has been requested twice. Consequently, our App is rejected.

We first observed the issue just under a week ago (June 6th), when I tried to upload a new version of our App to Google Play - though judging from the rise in relevant StackOverflow-posts, it has been around since at least June 1st. As nothing had changed in our manifest file following our previous upload, we were rather surprised to run into this issue, moreover since the pertinent permission has been included in our manifest file twice for a quite some time now.

Yet, although the problem was easy enough to identify, the solution has been less so, since the more commonly proposed resolutions either 1) do not apply to our situation (i.e. simply removing the permission request from one of lower-priority manifest files in the merge, or setting a android:maxSdkVersion="22"-attribute on the former element) as we do not have access to the underlying manifest files; or 2) are not supported by msbuild, as is the case with merge rules (see here). Especially the latter is is unfortunate, as this would have so easily solved our problem. Yet, likely due to the ascribed difficulty of implementation (see here), this feature has long been attributed relatively minor priority (see here).

Notwithstanding, it would be difficult to imagine we're currently the only ones experiencing this problem with the interoperability of Xamarin.Android and Google Play, as a brief search across the forums already turned up a couple of other developers dealing with either this, or other manifest merging issues.

Situation

We're developing our Xamarin.Forms App using Visual Studio 2015 on Windows and Xamarin Studio on Mac (though currently migrating to VS on Mac). Our builds are performed through an on-premise Team Build 2015 server that uses a Mac-agent to build both the iOS and Android versions of our App in a single build operation (which allows re-use of results from some of the build steps and ensures equal build numbers on both platforms). As the Xamarin.Android build task is based on a PowerShell script and hence does not run on OS X, we use a custom shell script that performs exactly the same function as does the aforementioned build task. Once the build has completed, the App bundles are uploaded to iTunes Connect and the Google Play Developer Console through separate release configurations.

What we've tried so far

What we've tried so far in an attempt to resolve this issue is:
1. Apply a manifest merge rule to remove the duplicate permission during the merging process. This, unfortunately, does not work, as msbuild simply removes the tools:node="remove"-attribute, in effect causing the exact element we wish to remove to be inserted instead!
2. Extract/unzip AndroidManifest.xml from the final, but unsigned, APK; convert the binary XML back to XML using AXMLPrinter2.jar; apply an XSL-transform to remove the duplicate permissions; and re-insert the Android manifest back into the package. This approach unfortunately breaks (for us) on re-insertion of the manifest file, apparently due to issues with resource pointer translations originating with original package creation (e.g. @string/app_description becomes @ref/0x7f080077).
3. Include a post-build event in our Android project file to run on Unix (i.e. our Mac) to apply an XSL-transform once the build is complete. Though this approach seems to work in essence, the transformed file appears to be overridden prior to packaging, causing our changes to be lost.

Approaches not tried

What we haven't tried is to use the Apktool to first decompile our App's package and then recompile it again after transforming the AndroidManifest.xml, since that project 1) includes binary tools already included amongst the standard Android build tools; 2) incorporates compiled Java class files; 3) is too large a project to fully review; 4) requires the APK to be extracted entirely; and 5) is therefore judged to introduce security risks into our build process.

Questions

Thus, I guess my questions are the following in order of precedence:
1. May we expect Android manifest merge rules to become supported by Xamarin.Android and msbuild? If so, when?
2. Has anybody found a work-around yet to remove duplicate permissions (or any elements, for that matter) from the merged manifest file in an App's APK?

Any help would be greatly appreciated!

Best regards,
Alexander.

Best Answer

  • AlexanderMelchersAlexanderMelchers NL ✭✭
    Accepted Answer

    Managed to find the solution to my problem (albeit a work-around rather than a stable fix) thanks to a suggestion by @xakpc on another thread. For those similarly searching for an answer while an actual fix is being developed, here's what I did:

    1. Get a copy of the AndroidManifest.xml from your App's APK by using unzip: unzip my.package.apk AndroidManifest.xml
    2. Run AXMLPrinter2 on the extracted binary AndroidManifest.xml to get plaintext version that will provide insight into the transformation needed to resolve the issue: java -jar AXMLPrinter2.jar AndroidManifest.xml > AndroidManifest.plaintext.xml
    3. Develop an XSL-transform to clean up the plaintext AndroidManifest.xml and resolve the issue on your test file (in my case I used key-matching to group permission requests of both the regular and sdk-23 type and flatten them using a template while copying/preserving all other elements unaltered).
    4. Create a bash/shell script to find your unpackaged AndroidManifest.xml during the build process and apply your XSL-transform. The correct AndroidManifest.xml to clean is located in obj/$configuration/android/AndroidManifest.xml.
    5. Extend your project file with a custom build target to run before the _CreateBaseApk-target and which will execute your shell script:

      <Target Name="_BeforeCreateBaseApk" BeforeTargets="_CreateBaseApk">
      <Exec Command="../../Deployment/RemoveDuplicateAndroidPermissions &quot;$(Configuration)&quot;" Condition=" '$(OS)' == 'Unix' " />
      </Target>

      Note that the OS-check in the Exec-task is needed because the executed script is platform-specific, i.e. will not run on Windows. I created this for OS X, which is where we run our build server. But something similar can be done on Windows as well.

    Hope this helps!

Answers

  • AlexanderMelchersAlexanderMelchers NLMember ✭✭
    Accepted Answer

    Managed to find the solution to my problem (albeit a work-around rather than a stable fix) thanks to a suggestion by @xakpc on another thread. For those similarly searching for an answer while an actual fix is being developed, here's what I did:

    1. Get a copy of the AndroidManifest.xml from your App's APK by using unzip: unzip my.package.apk AndroidManifest.xml
    2. Run AXMLPrinter2 on the extracted binary AndroidManifest.xml to get plaintext version that will provide insight into the transformation needed to resolve the issue: java -jar AXMLPrinter2.jar AndroidManifest.xml > AndroidManifest.plaintext.xml
    3. Develop an XSL-transform to clean up the plaintext AndroidManifest.xml and resolve the issue on your test file (in my case I used key-matching to group permission requests of both the regular and sdk-23 type and flatten them using a template while copying/preserving all other elements unaltered).
    4. Create a bash/shell script to find your unpackaged AndroidManifest.xml during the build process and apply your XSL-transform. The correct AndroidManifest.xml to clean is located in obj/$configuration/android/AndroidManifest.xml.
    5. Extend your project file with a custom build target to run before the _CreateBaseApk-target and which will execute your shell script:

      <Target Name="_BeforeCreateBaseApk" BeforeTargets="_CreateBaseApk">
      <Exec Command="../../Deployment/RemoveDuplicateAndroidPermissions &quot;$(Configuration)&quot;" Condition=" '$(OS)' == 'Unix' " />
      </Target>

      Note that the OS-check in the Exec-task is needed because the executed script is platform-specific, i.e. will not run on Windows. I created this for OS X, which is where we run our build server. But something similar can be done on Windows as well.

    Hope this helps!

  • DooksDooks ZAMember ✭✭✭
    edited July 22

    Hi @AlexanderMelchers
    Will it be possible to give a few code examples or provide a sample project?

    I (and a lot of other people) have this issue where there are duplicate entries added to the AndroidManifest.xml
    android:authorities="dollar_openBracket_applicationId_closeBracket"

    This line needs to be removed, and your solution is the only solution I think will work at this moment.

    Check this link https://github.com/xamarin/XamarinComponents/issues/590

    I just need an example snippet of your *.csproj file where you added the <Target... lines mentioned, and an example of your XSL-transform file.

Sign In or Register to comment.