Skip to content

Conversation

@jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Jan 2, 2019

Context: #2154
Fixes: #2415

Currently, in order to activate the embedded DSO support, one has to
perform the following actions manually:

  1. Add the android:extractNativeLibs="false" attribute to the
    <application> element in the Properties/AndroidManifest.xml
    file.

  2. Add the following property to the project file:

<AndroidStoreUncompressedFileExtensions>.so</AndroidStoreUncompressedFileExtensions>
  1. Add an android environment file to the project with a line which
    says:
__XA_DSO_IN_APK=1

Changes

The presence of android:extractNativeLibs="false" in
AndroidManifest.xml should setup all the extra "stuff" so developers
don't have to manually do it.

android:extractNativeLibs="false" would automatically do the
following:

  1. .so will be appended to
    $(AndroidStoreUncompressedFileExtensions). Both the <Aapt/> and
    <BuildApk/> MSBuild tasks use this property.

  2. A new generated file in $(IntermediateOutputPath) will add
    __XA_DSO_IN_APK=1 as an @(AndroidEnvironment) build item.

I also added an MSBuild test to verify these changes are happening. I
was also able to update tests/EmbeddedDSOs to rely on the new
functionality.

@jonathanpeppers
Copy link
Member Author

The test failures I need to look into, it looks like this new feature isn't working during incremental builds.

It looks like if _GenerateJavaStubs gets skipped, then there is a problem.

@jonpryor
Copy link
Contributor

jonpryor commented Jan 3, 2019

What's worrying is this failure: https://jenkins.mono-project.com/job/xamarin-android-pr-builder-release/275/testReport/Xamarin.Android.EmbeddedDSO_Test/nunit/Possible_Crash___Release_Bundle/

Apparently there's no adb logcat output from that test run? That's quite odd. :-/

xa-build-status-v9.1.199.78_Darwin-x86_64_pr_886c6774-Release.zip doesn't contain any logcat files for DSO tests:

$ ls tests
logcat-Release-Aot-Mono.Android_Tests.txt                       logcat-Release-Bundle-Xamarin.Forms_Performance_Integration.txt
logcat-Release-Aot-Xamarin.Forms_Performance_Integration.txt    logcat-Release-Mono.Android_Tests.txt
logcat-Release-Bundle-Mono.Android_Tests.txt                    logcat-Release-Xamarin.Android.Bcl_Tests.txt
logcat-Release-Bundle-Xamarin.Android.Bcl_Tests.txt             logcat-Release-Xamarin.Android.JcwGen_Tests.txt
logcat-Release-Bundle-Xamarin.Android.JcwGen_Tests.txt          logcat-Release-Xamarin.Android.Locale_Tests.txt
logcat-Release-Bundle-Xamarin.Android.Locale_Tests.txt          logcat-Release-Xamarin.Forms_Performance_Integration.txt

Do we not capture adb logcat for EmbeddedDSO tests? Or do they not match the filename pattern logcat-$(CONFIGURATION)-*.txt? I don't know. :-(

@jonpryor
Copy link
Contributor

jonpryor commented Jan 3, 2019

This appears to be why the EmbeddedDSO tests are failing; from the build log:

  Executing: /Users/builder/android-toolchain/sdk/platform-tools/adb -s emulator-5570  install "/Users/builder/jenkins/workspace/xamarin-android-pr-builder-release/xamarin-android/tests/../bin/TestRelease/Xamarin.Android.EmbeddedDSO_Test-Signed.apk"
...
warning : adb: failed to install /Users/builder/jenkins/workspace/xamarin-android-pr-builder-release/xamarin-android/tests/../bin/TestRelease/Xamarin.Android.EmbeddedDSO_Test-Signed.apk: Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]

I'll need to try to find the relevant adb logcat snippet for that installation.

@jonpryor
Copy link
Contributor

jonpryor commented Jan 3, 2019

After lots of digging, I think (hope? pray?) that the places that INSTALL_FAILED_INVALID_APK is used within com_android_internal_content_NativeLibraryHelper.cpp will be relevant (because adb logcat certainly isn't helpful here).

Thus:

sumFiles():

    if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, NULL, NULL)) {
        return INSTALL_FAILED_INVALID_APK;
    }

copyFileIfChanged():

    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
        ALOGD("Couldn't read zip entry info\n");
        return INSTALL_FAILED_INVALID_APK;
    }
    if (!extractNativeLibs) {
        // check if library is uncompressed and page-aligned
        if (method != ZipFileRO::kCompressStored) {
            ALOGD("Library '%s' is compressed - will not be able to open it directly from apk.\n",
                fileName);
            return INSTALL_FAILED_INVALID_APK;
        }
        if (offset % PAGE_SIZE != 0) {
            ALOGD("Library '%s' is not page-aligned - will not be able to open it directly from"
                " apk.\n", fileName);
            return INSTALL_FAILED_INVALID_APK;
        }
        return INSTALL_SUCCEEDED;
    }

iterateOverNativeFiles():

    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
    if (zipFile == NULL) {
        return INSTALL_FAILED_INVALID_APK;
    }
    std::unique_ptr<NativeLibrariesIterator> it(
            NativeLibrariesIterator::create(zipFile, debuggable));
    if (it.get() == NULL) {
        return INSTALL_FAILED_INVALID_APK;
    }
    const ScopedUtfChars cpuAbi(env, javaCpuAbi);
    if (cpuAbi.c_str() == NULL) {
        // This would've thrown, so this return code isn't observable by
        // Java.
        return INSTALL_FAILED_INVALID_APK;
    }

findSupportedAbi():

    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
    if (zipFile == NULL) {
        return INSTALL_FAILED_INVALID_APK;
    }
    std::unique_ptr<NativeLibrariesIterator> it(
            NativeLibrariesIterator::create(zipFile, debuggable));
    if (it.get() == NULL) {
        return INSTALL_FAILED_INVALID_APK;
    }

All of which leads me to believe that something is "wrong" with the .apk we're producing, and that it would be useful to get the actual .apk file produced for manual examination.

@jonathanpeppers
Copy link
Member Author

@jonpryor I can reproduce locally. I think an incremental build can get the apk in a state where the .so files are compressed, but the env var is present.

Build + delete apk + build again. Looking into it.

Context: dotnet#2154
Fixes: dotnet#2415

Currently, in order to activate the embedded DSO support, one has to
perform the following actions manually:

1. Add the `android:extractNativeLibs="false"` attribute to the
   `<application>` element in the `Properties/AndroidManifest.xml`
   file.

2. Add the following property to the project file:

    <AndroidStoreUncompressedFileExtensions>.so</AndroidStoreUncompressedFileExtensions>

3. Add an android environment file to the project with a line which
   says:

    __XA_DSO_IN_APK=1

~~ Changes ~~

The presence of `android:extractNativeLibs="false"` in
`AndroidManifest.xml` should setup all the extra "stuff" so developers
don't have to manually do it.

`android:extractNativeLibs="false"` would automatically do the
following:

1. `.so` will be appended to
   `$(AndroidStoreUncompressedFileExtensions)`. Both the `<Aapt/>` and
   `<BuildApk/>` MSBuild tasks use this property.

3. A new generated file in `$(IntermediateOutputPath)` will add
   `__XA_DSO_IN_APK=1` as an `@(AndroidEnvironment)` build item.

The problem here is with incremental builds... If `_GenerateJavaStubs`
is skipped, we still need to know if `android:extractNativeLibs` is
set. To make this work, I added a `CacheFile` property on the
`GenerateJavaStubs` MSBuild task. It writes a simple XML document in
`$(IntermediateOutputPath)javastubs.cache` such as:

    <Properties>
      <EmbeddedDSOsEnabled>True</EmbeddedDSOsEnabled>
    </Properties>

The file will not exist at all unless the value is `True`, which will
prevent this feature from impacting build times.

I also added an MSBuild test to verify these changes are happening. I
was also able to update `tests/EmbeddedDSOs` to rely on the new
functionality.

~~ Other Changes ~~

I updated `_GenerateJavaStubs` to follow the "stamp" file convention
of using `$(_AndroidStampDirectory)_GenerateJavaStubs.stamp`.
@jonpryor
Copy link
Contributor

jonpryor commented Jan 4, 2019

Spitballing a documentation rewrite: https://gist.github.com/jonpryor/fc46ef8f7933cede130eadeeafc736b9

My problem with the existing documentation is twofold:

  1. Why should the developer care?
  2. WTF is a "DSO"?

(2) is closely related to (1): why should developers care about "DSO"s?

It doesn't help that "DSO" isn't even defined within that document. (How many people will know that it means "Dynamic Shared Objects"? How many will care to know?)

We "care" because we've riddled our codebase with the phrase "DSO", but that doesn't mean our users need to care.

@jonathanpeppers
Copy link
Member Author

The documentation changes seemed good to me, I think I may have fixed something minor, otherwise looked good.

@jonpryor
Copy link
Contributor

jonpryor commented Jan 4, 2019

The reported unit test failure is unrelated to this PR.

@jonpryor jonpryor merged commit 2e4a6ab into dotnet:master Jan 4, 2019
@jonathanpeppers jonathanpeppers deleted the msbuild-dsos branch January 4, 2019 22:05
@github-actions github-actions bot locked and limited conversation to collaborators Feb 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants