Recompilación de Android frente a cambios
Contents
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:
- Modificamos android-2.2/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSettings.java
- Compilamos desde android-2.2/packages/apps/Settings
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
Successy 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
SuccessComo 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-voidVemos 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.txtUna 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:
- out/target/product/generic/symbols/system/lib/libandroid_runtime.so: Acceso a DBUS vía JNI
- out/target/product/generic/system/framework/framework.jar: Servicio de Bluetooth
- out/target/product/generic/system/framework/ext.jar
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.331sEstamos 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.