środa, 14 sierpnia 2013

Creating Android emulator v17 that support Google Maps v2

Few weeks ago we (my company) had been organizing a workshop for students. The topic of the workshop was Google Maps v2 for Android.

We usually work with devices, but for the workshop we could not assume  that everyone will posses the device which can run Google Maps.

When you run application in Android API 17 you will get the message that you need to Get Google Play services.

When running on the Google API 17 we get an empty map and in the logcat we can find.
W / EGL_emulation ( 1399): eglSurfaceAttrib not implemented
This was explained on some posts that for Google Maps OpenGL ES must be emulated.

Sometimes you get an information to update Google Play Services within an emulator.

I started researching the Internet/Stack Overflow for the solutions on the Google Map enabled emulator.
Seemed that several people tried this and there were contradicting reports.

Some blog posts say is impossible:

Some give some instructions to overcome the problem

What was in common in most of them is that you need to install some APKs in the emulator to make Google Services to work.

This post that was the most influential, was this one
it gave a nototion that you can modify the system.img.

I also found similar, a bit later

I cannot quote the sites now but I also tried downloading a complete image of an emulator. Of course it did not work.

From many posts in the internet I collected that Google Maps need:
  • intel x86 emulator to emulate OpenGL ES. This will not run on the Google Api emulator which has only arm binaries.
  • the Google Play Services is some Service that resides in apk(s) that are on the Google Api emulator but not in Andoid emulator.

The idea that sparked in my mind was simply to combine both emulators into one. First I started pulling apk(s) from one emulator and trying to install on another. But some of the apps did not want to install and even if they did I did not wanted to distribute the complete avd image for the emulator.

The post 38911bytes blog suggested we can make a yaffs2 image from the live system and put it in the avd directory. This happened to work and if you are on linux as I am you can use the mkfs.yaffs2.x86 directly in your system.

Similar can be also found in android source code under external/yaffs2 but it requires long compilation, unless you know how to compile single repository of yaffs2. I don't so I had to wait some hours before I could use it.

Running the creation on the regular PC would be better solution as mkfs.yaffs2.x86 is very slow on emulator. I thaugth there must be a way to unpack system.img and yes this was true.

You can get this tool from here (source code and binary)

This can be found compiled also with a tool to pack image back

Armed with those tools you can bind two system.img into one.
  1. Create the directory for the maps
    mkdir -p ~/tmp/maps/g17 ~/tmp/maps/a17
  2. Unpack the google api image into a g17
    unyaffs ~/android-sdk/add-ons/addon-google_apis-google-17/images/armeabi-v7a/system.img
  3. Unpack the android api image into a17
    unyaffs ~/android-sdk/system-images/android-17/x86/system.img
  4. Copy four apks from g17 directory to a17
    • GoogleLoginService.apk
    • GoogleServicesFramework.apk
    • Maps.apk
    • PrebuiltGmsCore.apk
  5. Make the system image again
    mkyaffs2image a17 ../system.img
  6. Create AVD
  7. The important bits are:

    • Target: Android API Level 17
    • CPU/ABI: Intel Atom (x86)
    • Use Host GPU
  8. Move system.img you created to the avd directory. In my case is
    mv system.img /home/made/.android/avd/new.avd/
    I have named the avd new. But the name is to your choice.
  9. You can start the emulator, now. If you need debugging I recommend to run this command
    emulator @new -verbose -show-kernel

Voila. The maps API seem to work!

I also had this error (but does not preven Maps API to be working).

emulator: Initializing hardware OpenGLES emulation support
emulator: ERROR: Could not load OpenGLES emulation library: lib64OpenglRender.so: cannot open shared object file: No such file or directory

You can do this
export LD_LIBRARY_PATH=/home/made/android-sdk/tools/lib:$LD_LIBRARY_PATH
of course substitute this with the correct path to the android sdk directory.

With that fix Maps work faster as OpenGL instead of software renderer is used.

Note that on the contrary to 38911bytes I do not copy maps.jar nor permissions file. I believe these step is necessary for Google Maps API v1. This does not hurt Google Maps API v2 but if you need Maps Api V1 you need copy com.google.android.map.xml to etc/permission and com.google.android.maps.jar to framework dirs from Google API system.img.