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
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
masterbranch, instead use
repo initand 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_x86-userdebugdepending 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
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
The extracted files can be put under application
Remember to mark the file as executable
file.setExecutable(true, false) (this call adds Unix
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!
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.
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.