Recompilación de Android frente a cambios

Introducción

La plataforma Android incluye un amplio abanico de tecnologías por lo que su compilación es un proceso largo y complejo. Vamos a intentar desvelando alguna de las claves del mismo.

Configuración inicial

Antes de comenzar a trabajar en la creación de la plataforma hay que ejecutar dentro del directorio con todas las fuentes:

android-2.2$ source build/envsetup.sh
Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- croot:   Changes directory to the top of the tree.
- m:       Makes from the top of the tree.
- mm:      Builds all of the modules in the current directory.
- mmm:     Builds all of the modules in the supplied directories.
- cgrep:   Greps on all local C/C++ files.
- jgrep:   Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir:   Go to the directory containing a file.

En realidad son funciones básicas de implementar pero útiles para moverte con agilidad por el entorno.

Compilación ante cambios

Aplicaciones Java

Si modificamos la aplicación de Settings en la parte de bluetooth (BluetoothSettings.java) para compilar y meter la nueva versión en el móvil:

android-2.2/packages/apps/Settings$ mm
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.2.1
...
make: se ingresa al directorio `/home/acs/devel/android/android-2.2'
target Java: Settings (out/target/common/obj/APPS/Settings_intermediates/classes)
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Copying: out/target/common/obj/APPS/Settings_intermediates/classes-full-debug.jar
Copying: out/target/common/obj/APPS/Settings_intermediates/classes-full-names.jar
Copying: out/target/common/obj/APPS/Settings_intermediates/classes.jar
target Dex: Settings
target Package: Settings (out/target/product/generic/obj/APPS/Settings_intermediates/package.apk)
 'out/target/common/obj/APPS/Settings_intermediates/classes.dex' as 'classes.dex'...
Install: out/target/product/generic/system/app/Settings.apk
target Java: SettingsTests (out/target/common/obj/APPS/SettingsTests_intermediates/classes)
Note: packages/apps/Settings/tests/src/com/android/settings/tests/BluetoothRequestPermissionTest.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Copying: out/target/common/obj/APPS/SettingsTests_intermediates/classes-full-debug.jar
Copying: out/target/common/obj/APPS/SettingsTests_intermediates/classes-full-names.jar
Copying: out/target/common/obj/APPS/SettingsTests_intermediates/classes.jar
target Dex: SettingsTests
target Package: SettingsTests (out/target/product/generic/obj/APPS/SettingsTests_intermediates/package.apk)
 'out/target/common/obj/APPS/SettingsTests_intermediates/classes.dex' as 'classes.dex'...
Install: out/target/product/generic/data/app/SettingsTests.apk
make: se sale del directorio `/home/acs/devel/android/android-2.2'

Vemos que esta aplicación se reconstruye en "out/target/product/generic/system/app/Settings.apk". Así que probar los cambios debería de ser tan sencillo como volver a instalar la aplicación en el móvil:

adb install android-2.2/out/target/product/generic/system/app/Settings.apk 
1289 KB/s (2514968 bytes in 1.904s)
        pkg: /data/local/tmp/Settings.apk
Failure [INSTALL_FAILED_ALREADY_EXISTS]

No lo podemos desinstalar con:

adb uninstall com.android.settings
Failure

y en los logs vemos que

I/PackageManager(  110): Removing system package:com.android.settings
W/PackageManager(  110): Attempt to delete system package com.android.settings

pero si lo podemos hacer desde dentro del móvil

# rm /system/app/Settings.apk
rm: remove '/system/app/Settings.apk'? y
rm: can't remove '/system/app/Settings.apk': Read-only file system
# mount -o remount r,w /system
# rm /system/app/Settings.apk
#

A partir de ahora la aplicación deja de ser de sistema y es una aplicación más que se puede instalar y desinstalar siguiendo los métodos tradicionales.

Ahora nos hemos quedado sin la aplicación de configuración en el móvil. Pero metemos la nueva versión:

adb install android-2.2/out/target/product/generic/system/app/Settings.apk
1263 KB/s (2514968 bytes in 1.943s)
        pkg: /data/local/tmp/Settings.apk
Success

y probamos el cambio que hemos hecho, que ha sido:

protected void onCreate(Bundle savedInstanceState) {
        
        if (true) {finish();}

es decir, que no haga nada al entrar en las opciones de bluetooth. Justo, le damos sobre este botón y no hace nada.

El ciclo de desarrollo aquí es muy rápido. Se modifica la aplicación, se compila, se desinstala y se instala de nuevo.

Cambios en código Java
android-2.2/packages/apps/Settings$ mm
adb uninstall com.android.settingsSuccess
adb install android-2.2/out/target/product/generic/system/app/Settings.apk
1263 KB/s (2514968 bytes in 1.943s)
        pkg: /data/local/tmp/Settings.apk
Success

Como vemos, los cambios en Android dentro del mundo Java son sencillos y rápidos de realizar.

Librerías C

Cuando cambiamos algo en una librería C, por ejemplo libc, nos podemos poner en el directorio y volver a compilar sólo esa librería. Por ejemplo, hemos modificado el fichero: android-2.2/bionic/libc$ vi stdlib/seed48.c

acs@lenovix:~/devel/android/android-2.2/bionic/libc$ mm
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.2.1
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=MASTER
============================================
make: se ingresa al directorio `/home/acs/devel/android/android-2.2'
target thumb C: libc_common <= bionic/libc/stdlib/seed48.c
target StaticLib: libc_common (out/target/product/generic/obj/STATIC_LIBRARIES/libc_common_intermediates/libc_common.a)
preparing StaticLib: libc_nomalloc [including out/target/product/generic/obj/STATIC_LIBRARIES/libc_common_intermediates/libc_common.a]
target StaticLib: libc_nomalloc (out/target/product/generic/obj/STATIC_LIBRARIES/libc_nomalloc_intermediates/libc_nomalloc.a)
preparing StaticLib: libc [including out/target/product/generic/obj/STATIC_LIBRARIES/libc_common_intermediates/libc_common.a]
target StaticLib: libc (out/target/product/generic/obj/STATIC_LIBRARIES/libc_intermediates/libc.a)
target SharedLib: libc (out/target/product/generic/obj/SHARED_LIBRARIES/libc_intermediates/LINKED/libc.so)
target Prelink: libc (out/target/product/generic/symbols/system/lib/libc.so)
libelfcopy: Warning: DW_FORM_data8 is unsupported when sizeof (unsigned long) != 8
libelfcopy: Warning: Range lists in .debug_info section aren't in ascending order!
target Strip: libc (out/target/product/generic/obj/lib/libc.so)
Install: out/target/product/generic/system/lib/libc.so
target SharedLib: libc_malloc_debug_leak (out/target/product/generic/obj/SHARED_LIBRARIES/libc_malloc_debug_leak_intermediates/LINKED/libc_malloc_debug_leak.so)
target Non-prelinked: libc_malloc_debug_leak (out/target/product/generic/symbols/system/lib/libc_malloc_debug_leak.so)
target Strip: libc_malloc_debug_leak (out/target/product/generic/obj/lib/libc_malloc_debug_leak.so)
Install: out/target/product/generic/system/lib/libc_malloc_debug_leak.so
target SharedLib: libc_malloc_debug_qemu (out/target/product/generic/obj/SHARED_LIBRARIES/libc_malloc_debug_qemu_intermediates/LINKED/libc_malloc_debug_qemu.so)
target Non-prelinked: libc_malloc_debug_qemu (out/target/product/generic/symbols/system/lib/libc_malloc_debug_qemu.so)
target Strip: libc_malloc_debug_qemu (out/target/product/generic/obj/lib/libc_malloc_debug_qemu.so)
Install: out/target/product/generic/system/lib/libc_malloc_debug_qemu.so
make: se sale del directorio `/home/acs/devel/android/android-2.2'

Vemos que se han creado un total de tres librerías (libc.so, libc_malloc_debug_leak.so, libc_malloc_debug_qemu.so). Estas librerías se podrían cambiar directamente en el móvil para tener disponibles los cambios. Bastaría con copiarlas en "/system/lib/libc.so". Eso sí, cuidando de no romper las APIs, tanto a nivel de fuentes como binarias, ya que en otro caso se romperían las aplicaciones que hicieran uso de libc, es decir, todas.

Librerías JNI

Una de las necesidades que tenemos es la de ampliar las funciones accesibles en BluetoothService.java para que las nuevas de HDP pasen a estar disponibles. Este cambio es más delicado ya que afecta tanto a las propias librerías que dan acceso a la API dbus, como a la parte Java que hace uso de ellas.

Lo primero es modificar la librería que implementa el acceso a DBUS de bluez. En nuestro caso está en "android-2.2/frameworks/base/core/jni/android_server_BluetoothService.cpp". Lo que hacemos para las pruebas es añadir una nueva función y hacerla visible desde Java. Lo único que vamos a hacer es que ponga una traza cuando se invoque esta función:

static jboolean startDiscoveryNativeTest(JNIEnv *env, jobject object) {
    LOGE("New function added to DBus bluez %s", __FUNCTION__);
}

Tras ello compilamos esta librería, que nos obliga por donde está a compilar todo "framework/base":

android-2.2/frameworks/base$ mm
make: se ingresa al directorio `/home/acs/devel/android/android-2.2'
Copying: out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes-full-debug.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-full-debug.jar
Install: out/target/product/generic/system/framework/framework.jar
Install: out/target/product/generic/system/framework/ext.jar
make: se sale del directorio `/home/acs/devel/android/android-2.2'

Vemos que no se da cuenta del cambio y no compila de nuevo. Este fichero genera los binarios intermedios:

./out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_server_BluetoothService.o
./out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_server_BluetoothService.P

que apuntan a que forman parte de:

./out/target/product/generic/obj/lib/libandroid_runtime.so

algo que poder comprobar con:

android-2.2$ strings ./out/target/product/generic/obj/lib/libandroid_runtime.so | grep BluetoothService
_ZN7android40register_android_server_BluetoothServiceEP7_JNIEnv
android/server/BluetoothService

así que estos cambios obligan a reconstruir esta librería. Si nos vamos al directorio principal de Android:

android-2.2$ mm
...
target thumb C++: libandroid_runtime <= frameworks/base/core/jni/android_server_BluetoothService.cpp
In file included from frameworks/base/core/jni/android_bluetooth_common.h:23,
                 from frameworks/base/core/jni/android_server_BluetoothService.cpp:21:
frameworks/base/core/jni/android_server_BluetoothService.cpp:201: error: no return statement in function returning non-void

Vemos que en este caso sí que intenta compilar el fichero que hemos tocado. Es una forma de volver a compilar lo que necesitamos aunque un poco lenta. Corregimos el error y compilamos de nuevo:

static jboolean startDiscoveryNativeTest(JNIEnv *env, jobject object) {
    LOGE("New function added to DBus bluez %s", __FUNCTION__);
    return JNI_TRUE;
}

android-2.2$ mm
...
target thumb C++: libandroid_runtime <= frameworks/base/core/jni/android_server_BluetoothService.cpp
In file included from frameworks/base/core/jni/android_bluetooth_common.h:23,
                 from frameworks/base/core/jni/android_server_BluetoothService.cpp:21:
dalvik/libnativehelper/include/nativehelper/jni.h:489: note: the mangling of 'va_list' has changed in GCC 4.4
target SharedLib: libandroid_runtime (out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/LINKED/libandroid_runtime.so)
target Prelink: libandroid_runtime (out/target/product/generic/symbols/system/lib/libandroid_runtime.so)
...
target Non-prelinked: sensors.trout (out/target/product/generic/symbols/system/lib/sensors.trout.so)
Target system fs image: out/target/product/generic/obj/PACKAGING/systemimage_unopt_intermediates/system.img
Install system fs image: out/target/product/generic/system.img
Installed file list: out/target/product/generic/installed-files.txt

Una vez que vamos conociendo las dependencias podemos ir directamente al target a cambiar: "target thumb C++: libandroid_runtime".

Ahora nos toca modificar la parte Java para que esta función este disponible. Hacemos que se llame a esta función siempre que se inicie el proceso de descubrimiento para ver que todo funciona.

...
public synchronized boolean startDiscovery() {
        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                "Need BLUETOOTH_ADMIN permission");
        if (!isEnabledInternal()) return false;
        
        startDiscoveryNativeTest();

        return startDiscoveryNative();
    }


// Test acs
    private native boolean startDiscoveryNativeTest();

En este caso sí que nos vale con entrar en "framework/base":

android-2.2/frameworks/base$ mm
...
make: se ingresa al directorio `/home/acs/devel/android/android-2.2'
Copying: out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes-full-debug.jar
target Java: framework (out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes)
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-full-debug.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-full-names.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
target Dex: framework
target Jar: framework (out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar)
 'out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.dex' as 'classes.dex'...
Install: out/target/product/generic/system/framework/framework.jar
Install: out/target/product/generic/system/framework/ext.jar
make: se sale del directorio `/home/acs/devel/android/android-2.2'

De todo esto parece que para cambiar esta parte bastaría con copiar al móvil:

android-2.2$ adb push out/target/product/generic/system/framework/framework.jar /sdcard/APIBluez/
1318 KB/s (2712818 bytes in 2.009s)
adb push out/target/product/generic/symbols/system/lib/libandroid_runtime.so /sdcard/APIBluez/
1326 KB/s (5608472 bytes in 4.129s)
acs@lenovix:~/devel/android/android-2.2$ adb push out/target/product/generic/system/framework/ext.jar /sdcard/APIBluez/
1297 KB/s (234748 bytes in 0.176s)

Y ahora le damos el cambiazo a las librerías ya instaladas:

# cd /sdcard/APIBluez/
# cp /system/framework/ext.jar ext.jar.orig
# cp /system/framework/framework.jar /system/framework/framework.jar.orig
# cp /system/lib/libandroid_runtime.so libandroid_runtime.so.orig
# cp libandroid_runtime.so /system/lib/libandroid_runtime.so
# cp framework.jar /system/framework/framework.jar
# cp ext.jar /system/framework/ext.jar

y si todo va bien, en cuanto intentemos lanzar la exploración de dispositivos bluetooth deberemos de ver la llamada a nuestra nueva función.

Hemos podido realizar la exploración bien, pero no he visto la traza. Al intentar desinstalar el programa de Settings para volverlo a instalar el móvil se ha reiniciado. Le he tenido que quitar la batería y parece que ya no vuelve a arrancar. Vamos mejor a meter el "system.img" completo.

android-2.2# fastboot flash system ./out/target/product/generic/system.img
   sending 'system' (57578 KB)... OKAY [  8.263s]
              writing 'system'... OKAY [ 21.068s]
finished. total time: 29.331s

Estamos montando un sistema ya complicado ya que la base era cyanogen 2.2. Lo normal es que dejen de funcionar cosas del hardware. Efectivamente.

Lo que hacemos es integrar estos cambios dentro de cyanogen y crear una imagen de este. Tras instalarla las primeras pruebas resultan con que:

I/ActivityManager( 1254): Starting activity: Intent { act=android.intent.action.MAIN cmp=com.android.settings/.bluetooth.BluetoothSettings }
W/dalvikvm( 1254): No implementation found for native Landroid/server/BluetoothService;.startDiscoveryNativeTest ()Z
E/JavaBinder( 1254): *** Uncaught remote exception!  (Exceptions are not yet supported across processes.)
E/JavaBinder( 1254): java.lang.UnsatisfiedLinkError: startDiscoveryNativeTest
E/JavaBinder( 1254):    at android.server.BluetoothService.startDiscoveryNativeTest(Native Method)
E/JavaBinder( 1254):    at android.server.BluetoothService.startDiscovery(BluetoothService.java:1082)
E/JavaBinder( 1254):    at android.bluetooth.IBluetooth$Stub.onTransact(IBluetooth.java:151)
E/JavaBinder( 1254):    at android.os.Binder.execTransact(Binder.java:288)
E/JavaBinder( 1254):    at dalvik.system.NativeStart.run(Native Method)
W/dalvikvm( 1254): threadid=44: thread exiting with uncaught exception (group=0x4001d7e0)
E/AndroidRuntime( 1254): *** FATAL EXCEPTION IN SYSTEM PROCESS: Binder Thread #7
E/AndroidRuntime( 1254): java.lang.UnsatisfiedLinkError: startDiscoveryNativeTest
E/AndroidRuntime( 1254):        at android.server.BluetoothService.startDiscoveryNativeTest(Native Method)
E/AndroidRuntime( 1254):        at android.server.BluetoothService.startDiscovery(BluetoothService.java:1082)
E/AndroidRuntime( 1254):        at android.bluetooth.IBluetooth$Stub.onTransact(IBluetooth.java:151)
E/AndroidRuntime( 1254):        at android.os.Binder.execTransact(Binder.java:288)
E/AndroidRuntime( 1254):        at dalvik.system.NativeStart.run(Native Method)

Parece que es necesario hacer algo más para que el enganche funcione entre Java y C.

Nos faltaba por añadir la entrada en "android2.2/frameworks/base/core/jni/android_server_BluetoothService.cpp"

static JNINativeMethod sMethods[] = {
...
   {"startDiscoveryNativeTest", "()Z", (void*)startDiscoveryNativeTest},
...

Tras añadir esto ya funciona todo perfectamente. Cuando accedemos a la configuración de bluetooth, que hace una exploración de forma automática, podemos ver:

...
E/BluetoothService.cpp(  112): New function added to DBus bluez startDiscoveryNativeTest
...

con lo que queda resuelto ya el tema de como se implementan las nuevas funciones.

CompilarAndroidCambios (last edited 2010-10-06 10:18:40 by AlvaroDelCastillo)