Accessing Android internal APIs from apps

Android SDK provides access to many great APIs and features but there are still many missing or incomplete. This is especially true about access to some slightly non-standard features. For example hardware video encoding API got added in Android 4.0 but it took four major releases before the usable API got exposed in Android 4.4 SDK.

Luckily for those of you who are not patient enough to wait two year for the private API to get publisher it’s possible to access such private APIs using couple tricks I’m going to describe in this post.

I’m going to use SCR Screen Recorder app as an example. The app captures the screen contents and encodes it to the mp4 video file.

SCR required access to two private APIs:

  • ScreenshotClient::capture() – to capture screen contents (this call requires root permission),
  • MediaRecorder::querySurfaceMediaSourceFromMediaServer() – to pass frames to hardware video encoder (BTW this API got exposed in Lollipup as MediaRecorder.getSurface())

as well as the number of C++ classes used by these APIs.

Build with AOSP rather than NDK

To access private APIs you need to build your code as part of Android Open Source Project (AOSP) build. It means you need to build whole Android before you can even start developing your binary! To checkout and build Android sources follow the instructions in the official documentation. Few tips regarding the build:

  • Don’t checkout master branch, instead use -b option with repo init and checkout the branch corresponding to the version of Android you’re running on your test device (see documentation).
  • Use one of the generic build targets (e.g. lunch full-userdebug or lunch full_x86-userdebug depending on your CPU architecture).
  • Be patient, it takes hours to build.
  • If you’re building an older version of Android (e.g. ICS) on recent build Environment like Ubuntu 14.04 you’re probably going to get some compilation errors. It takes just a few fixes/patches to resolve these issues. Unfortunately I don’t have the patches handy but you can surely find it on the Internet.

When the build is completed you’re ready to work on your app!
Create a new AOSP module e.g. under frameworks/base/cmds/my_module and configure Android.mk Have a look at frameworks/base/cmds/screencap for sample configuration.

To build your module you don’t have to run the whole AOSP build again. Simply initialize the build environment (. build/envsetup.sh and lunch full-userdebug) and then inside your directory (cd frameworks/base/cmds/my_module) run mm command. This will build contents of current directory and would take just couple seconds.
The build command will compile the binary to $OUT/system/bin/my_module

To test the compiled binary push it to your test device adb push $OUT/system/bin/my_module /data/local/tmp then open ADB shell and run the copied binary from command line

Separate binary per major release and CPU architecture

Unlike binaries compiled with NDK binaries compiled using the above instructions are generally not portable between Android versions. You’ll need to build a separate binary for each Android major version. If you’d like to support devices running Android 4.0 – 5.1 you’ll need to repeat the procedure described above 7 time by checking out 7 different Android versions to separate directories. To support Intel Atom devices you’d also need to repeat the build with full_x86-userdebug build target for each version (you may want to run export OUT_DIR=out_x86 before lunch full_x86-userdebug to have separate output directories for different architectures).

Install and run from your app

When you have a native binary ready, you need to package it and run it from your app.
If you followed the advice in previous paragraph, you’d end up with 14 different binaries, one for each Android version and CPU architecture.

There is a number of ways in which you can package your binaries. You can place the compiled binaries under res/raw-vXX directory and them it using InputStream inputStream = context.getResources().openRawResource(R.raw.my_module); or use AssetManager.

The extracted files can be put under application files directory Context.getFilesDir().
Remember to mark the file as executable file.setExecutable(true, false) (this call adds Unix x permission).

Use Runtime.getRuntime().exec() and Process to run the extracted files. Make sure to consume process error and output streams from some helper thread to avoid blocking on IO operations. If your app requires root you can use Chainfire’s helper library described at https://su.chainfire.eu/.

That’s it! Enjoy squeezing even more features out of Android!

Limitations

Of course there are some drawbacks of using undocumented and unofficial APIs.

Build environment size
You need a separate AOSP build for each supported version of Android and architecture. To support ICS and above you’d need around 1TB of disk space for AOSP builds.

Initial build time
It takes hours to build each version of AOSP. Using SSD can significantly reduce the build time but 1TB SSDs are still quite pricey.
Luckily this is only a problem with the initial build. Subsequent builds with mm are quite fast.

Compatibility
Unlike with public APIs, vendors (OEMs) are free to modify private APIs which results in incompatibilities and crashes (e.g. Segmentation Faults). Surprisingly, they modify framework APIs quite rarely so only a small percentage of devices should be affected. Driver APIs seems to be modified much more often but that’s another story.

Developer Program Policy
It’s unclear if, or to what extent, using private API is allowed by Google Play policies. According to the recent update “apps should not harm, interfere with, or improperly access Application Programming Interfaces (APIs)” and as always “improperly access” is not defined. There are many apps accessing private APIs on Google Play but remember, if you decide to join them, you’re doing it at your own risk.

Share on Google+Share on FacebookShare on LinkedInPin on PinterestTweet about this on Twitter

3 thoughts on “Accessing Android internal APIs from apps”

  1. hello dear friend.
    I m trying to use internal API but the problem is i can bind it to my application.
    I m using PreciseCallState but it doesnt fire up.
    What i wanna ask is how to use these API?Should I use it under root permission?(its not what I want)
    please send me a note or example if possible
    Thanks

  2. Hello,I have a question.How to use “Mediarecorder” to build soft encoding recorder?(SCR Settings — Encoder — Software MPEG-4)

  3. Sorry for late response, there is lots of spam here. The “Software MPEG-4” encoder option in SCR is using a FFmpeg insetead of Mediarecorder. It’s more reliable but slower than built in Android encoders.

Leave a Reply

Your email address will not be published. Required fields are marked *