Intel® Architecture Support Guide for Android* Middleware Providers

Download PDF

x86 support has been part of Android since 2011 and nowadays, as flagship products like the Dell Venue* 8 7840, Nokia* n1, Google Nexus* Player, and more than 200 other devices are based on Intel® architecture, it’s becoming more than important for middleware software providers to support x86 devices.

When choosing third-party middleware, companies look carefully at what CPU-architectures are supported, as the choice will have a direct impact on the compatibility of the final application, potentially making or breaking the deal.

In many cases supporting x86 is necessary. But note that it only makes sense for middleware that actually uses architecture-specific binaries (usually .so files packaged into the final APKs).  It’s possible to check for the presence of .so files in an APK using a Zip archive viewer, aapt dump badging command, or Native Libs Monitor:

Native Libs Monitor Details

As of NDK r10d, Android supports these CPU architectures (ABIs): armeabi, armeabi-v7a, x86, x86_64, arm64-v8a, mips, and mips64.

The ways of adding x86/x86_64 support entirely depends on what exactly is put into developer hands (Source code? Libraries? Toolchains? Services?). Generally, adding such support is done by following Android best development practices.

Since Android originally supported only ARM*, some providers didn’t pay much attention to supporting other platforms. To overcome this issue, Intel is providing the ability to translate ARM binaries at runtime on Intel devices, and it’s working well. However, middleware providers should offer native support for Intel® Architectures.

This document gives an overview of what has to be done at the library/engine level to better support Intel platforms.

Two main cases are identified:

  1. A component/library (OpenCV, ffmpeg, …) is provided. Developers can integrate it into their own Android project.
  2. A complete stack/service (like Unity, Adobe Air*, Corona*, and so on) is provided. The software or service handles the Android project creation and/or APK packaging.

In both cases, documentation and a sample project should be available to customers, which leads of course to the proper packaging and inclusion of the x86/x86_64 libs in the final applications.

Providing a library/engine to be reused or integrated by Android developers

Libraries and engines with native components can be distributed under different forms: several .jars, an .aar, a NDK project, an Android project, etc.

If the library consists of a Java* library and .so files, it should be distributed as an .aar instead of .jars accompanied by .so files. AARs are .jar files extended for Android that allow the inclusion of resources and native libraries. Android developers can easily include AARs once they have been uploaded to Maven Central Repository / jCenter.

You should place .so files in an .aar under ./jni/<ABI>.  For x86, place .so files under ./jni/x86/.

In case the library is distributed as NDK prebuilts (.so or .a files) to be reused from a NDK project, the NDK module declaration should be provided in an Android.mk file by using the dynamic variable $(TARGET_ARCH_ABI) in order to point to the right file depending on the various target architectures:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)				
LOCAL_MODULE := yourlib	
LOCAL_SRC_FILES := prebuilts/$(TARGET_ARCH_ABI)/libYourLib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/prebuilts/includes
include $(PREBUILT_SHARED_LIBRARY)

Also set all the supported architectures in the Application.mk file, like so:

APP_ABI := all # can also be: armeabi-v7a x86 x86_64 arm64-v8a…

If the library/engine is distributed through a complete Android project, .so files have to be included in the proper location:

  • In an Eclipse*/Apache Ant* project, place them under libs/<ABI>.
  • In an Android Studio/gradle project, place them under src/main/jniLibs/<ABI>.

To change the jniLibs location for a project managed by gradle, use the jniLibs.srcDir property from the build.gradle file. For example, you can set it back to the same location as Eclipse, to libs, where NDK libs are generated:  sourceSets.main { jniLibs.srcDir 'src/main/libs' }.

Application packaging

When providing a full service and/or software that handles APK packaging, you need to properly generate compatible APKs.

The recommended (and default) way is to package one APK and put all the .so files for all the supported architectures in the APK under lib/<ABI>/. Where <ABI> can be armeabi, armeabi-v7a, x86, x86_64, arm64-v8a, mips, mips64.

Of course, you should include x86 at a minimum to better support x86 platforms:

Native Libs Monitor  APK - x86 platforms

To get these .so files properly packaged under lib/<ABI>, by default they have to be placed under libs/<ABI> when using an Ant/Eclipse project, or under src/main/jniLibs/<ABI> when using a gradle/Android Studio project.

When having support for 3rd party plugins that can embed architecture-specific binaries, like Unity has, attention should be given to be sure these plugins are compatible with all the supported platforms. Before Android 5.0, it was possible to still load ARM libs from an x86 folder. But this isn’t possible anymore and leads to errors such has “dlopen failed: ‘libMyLib.so’ has unexpected e_machine: 40”. So plugins have to be upgraded to also include x86 binaries and the engine/service has to enforce that, in order to allow a smooth transition.

Reducing the size of the produced APKs

If the size of .so files is too large to make embedding several versions of these libs feasible, you can package one APK per architecture. The only requirements are to generate one APK with a different set of .so files placed in the right lib/<ABI> folder and to have different versionCodes, following this rule: x86_64 > x86 > arm64-v8a > armeabi-v7a > armeabi > mips64 > mips version code.

When using gradle, all this can be achieved seamlessly (all the proper APKs are generated in one build) using splits and dynamic version code, by adding the following code to build.gradle:

android {
...
splits {
   	abi {
       	enable true
       	reset()
       	include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
       	universalApk true
   	}
   }
   // map for the version code
   project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
   applicationVariants.all { variant ->
   	// assign different version code for each output
   	variant.outputs.each { output ->
       	output.versionCodeOverride =
               	project.ext.versionCodes.get(output.abiFilter, 0) * 1000000 + android.defaultConfig.versionCode
   	}
   }
...
}

To upload multiple APKs to the Google Play* Store for a single application, you are required to switch to Advanced mode before uploading the second APK:

Advanced mode to upload new APKs

Once all the APKs are uploaded, the summary of the available APKs should look like this:

The summary of the available APKs

Conclusion

Adding Intel architecture support when it’s possible is usually quite simple, and middleware suppliers should consider offering it in their software so their customers can use it. The Android Native Development Kit already has this capability embedded since 2011.

If you need help with recompilation issues, Intel® Developer Zone (software.intel.com) features other articles on this topic, such as NDK Apps porting methodologies   and NEON* to Intel® SSE instructions automatic porting solution.

To test your applications, Intel is offering free use of Intel® processor-based devices through well-known remote test services like AppThwack*, testdroid*, and Testin*.

For more complete information about compiler optimizations, see our Optimization Notice.