1
2
Índice general
1 INTRODUCCIÓN
1.1 Motivación
1.2 Explicación del proyecto
1.3 Objetivos
2 CONTEXTUALIZACIÓN
2.1 Tecnologías disponibles
2.2 Aplicaciones similares
3 ANÁLISIS Y DISEÑO
3.1 El antes y el después en el emulador de Android: QEMU
3.2 Estructura del emulador de Android
3.3 Especificación de requisitos
3.4 Puesta en marcha: El entorno
3.5 Cómo compilar
3.6 AVD
4. EJECUTANDO EL EMULADOR
4.1 HAXM
4.1.1 Requisitos
4.2 Parámetros del emulador
3
5 IMPLEMENTACIÓN
5.1 Modificaciones
5.2 Estructura de las modificaciones
5.3 Lista de propiedades del hardware virtual
6 LINEAS FUTURAS
7 CONCLUSIONES
8 AGRADECIMIENTOS
9 BIBLIOGRAFÍA
4
Índice de Figuras
Figura 1: Arranque de métodos del emulador 10
Figura 2: Muestra del fichero que define el hardware soportado por defecto 12
Figura 3: Lista de opciones del fichero config.ini 13
Figura 4: Comunicación entre las distintas partes de QEMU 18
Figura 5: Conociendo el puerto RIL 23
Figura 6: Solicitud del operador de red por parte de la capa RIL 24
Figura 7: Opciones del comando radiooptions de la capa RIL 25
5
1 Introducción
1.1 Motivación
El proyecto surge de la necesidad de validar mediante la realización de pruebas la
calidad de las mediciones y de los productos realizados en Swiss Mobility Solutions,
una compañía de Gemalto.
Estos productos se basan en recoger información, bien sea en segundo plano de
forma pasiva o mediante pruebas activas, permitiendo determinar el QoE y QoS, esto
es, la Calidad de Experiencia y Calidad de Servicio respectivamente.
Para realizar los test de calidad surgieron dos alternativas: Una era portar el código
actual a java puro dejando ciertas partes acopladas al Api de Android como es lógico;
la otra en probar en un dispositivo real la aplicación, como si de una gran caja negra
se tratase. Sabemos lo que entra y el resultado que debería de arrojar. Finalmente se
optó por esta vía dado que independientemente de estas pruebas de calidad, muchas
otras se apoyarían en dispositivos reales para, por ejemplo, testear las interfaces.
Teniendo en cuenta el ámbito del proyecto no hay nada en el mercado que realice
estas funciones y además los parámetros que Google ofrece para modificar su
emulador son a todas luces insuficientes así como la creciente necesidad de testeo
nace este proyecto con la ilusión de modificar el emulador de Android para permitir
introducir los valores necesarios para comprobar el funcionamiento de las
aplicaciones que se desarrollan en la empresa.
Por todo esto me entusiasmó la idea de tener la oportunidad de crear este proyecto,
aprendiendo Android desde lo más profundo y desarrollando una herramienta que
diera un nuevo rumbo al emulador.
6
1.2 Explicación del proyecto
La idea principal consiste en permitir que ciertos valores que son asignados de forma
fija en el código del emulador puedan ser modificados externamente y con el
emulador funcionando mediante la introducción parámetros.
De esta forma, podemos tener la aplicación funcionando en el emulador, e ir
modificando los parámetros de entrada para comprobar el comportamiento de la
aplicación emulando así condiciones reales que difícilmente podrían ser probadas,
como por ejemplo, una captura de un valor invalido para el MCC/MNC del operador
debido a malfuncionamientos de la red.
1.3 Objetivos
Con lo dicho anteriormente los objetivos del proyecto son los siguientes:
• Conseguir modificar el valor de ciertos parámetros del emulador relativos a la clase
Telephony.
• Aprender el funcionamiento y arquitectura del emulador de Android
• Testear aplicaciones mediante test de caja negra haciendo uso del emulador
• Ampliar el parque de pruebas disponibles para las aplicaciones mediante test antes
difíciles o imposibles de realizar por requerir condiciones físicas extremas de la red.
7
2 CONTEXTUALIZACIÓN
Existen actualmente dentro del mercado móvil muchos dispositivos para poder elegir
el que mejor se ajuste a las exigencias y necesidades de cada usuario. Pero hay un
líder indiscutible desde hace unos años, el sistema operativo Android. A lo largo de
los años hemos podido ver cómo el mercado evolucionaba y ampliaba encontrando
actualmente al sistema operativo Android en casi cualquier dispositivo y con
características muy diferentes entre sí: desde tablets y smartphones hasta relojes,
televisiones y coches.
Debido a esto y a pesar de ser una necesidad obvia cada vez es más imperativo
asegurar que nuestras aplicaciones funcionan de la manera que esperamos que
funcionen por lo tanto una herramienta como la que se va a desarrollar en este
proyecto es esencial puesto que ofrece un medio seguro, controlado y muy próximo
a la realidad en el que testear las aplicaciones.
2.1 Tecnologías disponibles
En el ámbito actual no se encuentra disponible ninguna aplicación de características
similares ni si quiera de manera profesional. Podemos sin embargo buscar ciertas
referencias a funcionamientos parecidos en otros programas como pueden ser
VMWare.
Dicho programa permite crear máquinas virtuales y configurar las características de
estas para posteriormente ejecutarlas en una maquina anfitriona. Este es el
comportamiento que sigue el emulador de Android que además hace uso de QEMU,
un emulador Open Source escrito en C que ha servido como base para multitud de
proyectos.
En nuestro caso el emulador de Android tan solo permite la ejecución de máquinas
con el sistema operativo Android pero permitiendo de igual manera que VMWare la
configuración de estas máquinas para emular de forma más fideligna el
comportamiento de los dispositivos reales. Tales configuraciones van desde la
cantidad de memoria RAM, hasta el tamaño y densidad de pantalla, espacio en disco,
emulación de la GPU, etc.
8
2.2 Aplicaciones similares
Actualmente no hay ninguna aplicación que realice los dichos cambios en el
emulador. Sin embargo hay aplicaciones que interactúan con el emulador ampliando
con dispositivos físicos la entrada de éste, permitiendo así simular ciertos
comportamientos de una manera más próxima a la realidad. En este caso se trata de
una aplicación que parchea el código fuente del emulador para permitir que éste lea
tarjetas SIM reales a través de un dispositivo físico destinado a tal uso.
3 ANALISIS Y DISEÑO
El sino de este apartado es el de exponer el estudio previo realizado para la
comprensión, diseño e implementación de las modificaciones que queremos sobre el
emulador de Android. Así como vislumbrar los cambios futuros que se sucederán
sobre el emulador y el porqué de la disrupción en código que ha sufrido y que ha
cambiado drásticamente la manera de trabajar con éste.
3.1 El antes y el después en el emulador de Android con QEMU
Como se ha mencionado anteriormente el emulador de Android está basado en el
emulador Open Source QEMU, tras unas drásticas modificaciones sobre éste se
consiguió emular las capas necesarias para hacer correr el sistema operativo.
Históricamente y con pocos dispositivos en el mercado el emulador era lo más
parecido a un dispositivo real, funcionaba prácticamente igual y permitía una
herramienta para probar las aplicaciones de manera que los desarrolladores podían
comprobar si sus aplicaciones funcionaban correctamente en los dispositivos antes
de salir al mercado.
La forma de trabajar con el emulador, desde el punto de vista del desarrollador,
pasaba por descargar todo el árbol de fuentes del repositorio AOSP de Google y
usando una maquina Linux/Mac compilar todo el sistema operativo pues el emulador,
era una parte integrada en éste.
Posteriormente Google se dio cuenta de que el emulador se estaba convirtiendo en
una aplicación monolítica difícilmente mantenible y usable puesto que cada vez había
más dispositivos que emular con unas especificaciones muy diversas.
Cabe destacar que la herramienta, emula físicamente los dispositivos.
9
Por esta razón se decidió crear una pequeña rama aparte donde las herramientas
fuesen utilizadas por su IDE principal, Android Studio, y en la que Google aprovecho
para romper con el diseño antiguo de su emulador para intentar paliar los problemas
mencionados.
Actualmente el emulador sigue siendo una pieza monolítica clave en las
herramientas de Google.
10
3.2 Estructura del emulador de Android
El emulador se divide en tres grandes bloques: Frontend, comprobación y
configuración del hardware y arranque e inicialización del hardware y Kernel.
1. Frontend
En esta parte el emulador se encarga de parsear y
verificar los parámetros de entrada así como dar a
QEMU una lista limpia de parámetros para
comenzar la emulación.
Como mencionábamos anteriormente, hay un gran
chequeo de parámetros debido a los diferentes
entornos que ofrecen los dispositivos y las
necesidades de que el emulador sea compatible.
Algunas de las principales funcionalidades son las
siguientes:
Ejecución de main.c: Comienza la ejecución
parseando los parámetros mediante
Android_parse_options en una estructura que
contiene punteros listas de parámetros. Muchas de
estas opciones están definidas en cmdline-
options.h
Posteriormente se sanean estas opciones
mediante main-common.c/sanitizeOptions que
maneja todas las combinaciones de parametros
disponibles.
Tras esto se llama a main-common.c/createAVD y
se gestiona las máquinas virtuales. Estas
máquinas virtuales pueden funcionar en base a
dos casos diferentes:
Figura 1: Arranque de métodos del emulador
11
1. El emulador es ejecutado bajo el contexto del Android Build System, esto ocurre
en las compilaciones para ramas antiguas, cuando el emulador estaba integrado
con el sistema operativo. Aquí se obtienen configuraciones como la arquitectura
elegida, directorio de salida, directorio de compilación, etc.
2. El emulador es ejecutado en modo standalone, este es el caso de la nueva rama.
En esta ejecución el emulador es encarga de comprobar los archivos requeridos,
nombre del dispositivo a emular, path hacia el sdk necesario, etc.
2. Configuración Hardware
Este será uno de los puntos de entrada para la modificación de las variables
internas del emulador. Se trata de una variable global llamada Android_hw,
internamente es del tipo AndroidHwConfig y es una estructura que está definida
bajo Android/avd/hw-config.h y sus definiciones en hw-config-defs.h. Viendo estos
ficheros podemos hacernos una idea de las características de hardware
soportadas. Aquí incluiremos nuestras variables de forma que el emulador las
considere como parte del hardware soportado.
Dentro de esta configuración también se suceden los siguientes eventos que
resultan de crucial importancia para el arranque de QEMU:
- Arranque del reloj: se usan 3 relojes en qemu-timer.c uno de tiempo real, otro
para el reloj virtual y otro reloj para el host.
- Chequeo de opciones de QEMU: se contrastan todas las opciones filtradas
anteriormente con las opciones en qemu-options.h además se realiza el
parseo de la configuración hardware dada en Android-hw y se inicializan los
sistemas de imágenes tales como system.img, data.img, sdcard.img, etc.
12
- Entre el resto de opciones parseadas se encuentran el arranque del modem
GSM y GPS, la velocidad de red y su latencia, densidad de pantalla, etc. Para
la configuración del modem incidiré en el fichero modem.c así como en la
configuración de la tarjeta SIM mediante el fichero sim.c
Figura 2. Muestra del fichero que define el hardware soportado por defecto
13
3. Inicialización del hardware y del Kernel
Se comienza con el arranque de los puertos ttyS0, destinado al Kernel, el ttyS1,
destinado a QEMU y los bucles principales del emulador. El bucle principal y sobre
el que habrá que incidir posteriormente está en las funciones
qemu_init_main_loop() y qemu_event_init() además en este proceso se inicializan
los dispositivos. Respecto al Kernel se trata de una CPU virtual llamada Goldfish
que ejecuta instrucciones ARM926T y que se encarga de la entrada/salida, por
ejemplo para la entrada de teclado y salida de video, que se muestra en el
emulador.
Figura 3. Comunicación entre las distintas partes de QEMU
14
3.3 Especificación de requisitos
Aunque hay una obvia diferenciación entre los procesos de compilación según elijamos
compilar la antigua rama o la nueva, los requisitos mínimos necesarios se basan
principalmente en el espacio en disco disponible y la cantidad de RAM. Las plataformas
disponibles para realizar la compilación son Linux y Mac OS y se necesita al menos 16Gb
de RAM y un mínimo de 100Gb para compilar el código fuente. Además, si se realizan
múltiples compilaciones se recomienda unos 200Gb adicionales y entre 50-100Gb más
si usamos CCache, lo cual es harto recomendado. Como herramientas para la
compilación es obligatorio tener Python 2.6-2.7, GMake 3.82, Git y Java 7.
En lo personal recomendaría como mínimo un i7 con 16Gb de RAM combinados con
CCache, que es un compilador de caché para C/C++, una herramienta que aunque no
en la primera compilación, si en las posteriores, puede reducir los tiempos hasta dejarlo
en una hora o incluso 15minutos dependiendo de los cambios que hagamos en el código
fuente. También es recomendable usar un disco SSD.
3.4 Puesta en marcha: El entorno
Para compilar debemos seguir los siguientes pasos dependiendo de la plataforma en la
que estemos y teniendo en cuenta que estamos en la rama antigua:
1. Linux (Debian)
1. $ sudo apt-get install openjdk-7-jdk
2. $ sudo update-alternatives --config javac
3. $ sudo apt-get install bison g++-multilib git gperf libxml2-utils make python-network
zlib1g-dev:i386 zip
1. Mac
Se necesita tener el Sistema operativo instalado con la opción case-
sensitive habilitada o en su defecto crear una partición donde descargar el
código AOSP
Con el siguiente código se creará y montará una partición de 60Gb.
Además se hará uso de Brew para la instalación de paquetes externos. Por
último se aumentará el número de file descriptors simultáneos que puede
abrir el sistema operativo.
15
1. # hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+'
-size 60g ~/android.dmg
2. # function mountAndroid { hdiutil attach ~/android.dmg -mountpoint /Volumes/android; }
3. # brew install gmake libsdl git gnupg
4. # ulimit –S –n 1024
Tanto si utilizamos la versión antigua como la nueva, es recomendable el
uso de la herramienta de compilación CCache. Para usarla, edita .bashrc:
1. $ export USE_CCACHE = 1
3.5 Cómo compilar
Una vez descargado el código fuente y teniendo en cuenta que estamos compilando bajo
el método antiguo debemos usar los siguientes pasos para compilar. Empezamos por
acceder al directorio donde están las fuentes y posteriormente ejecutamos:
1. $ source build/envsetup.sh 2. lunch 3. Seleccionamos la combinación deseada 4. make –j4
Destacar los siguientes sufijos a la hora de seleccionar la
plataforma/dispositivo:
1. user – Acceso limitado, versión de producción 2. userdebug – Como la anterior pero con acceso root 3. eng – Versión de desarrollo con herramientas para debug
A pesar de todo lo habitual será que compilemos la nueva rama para
disponer de las últimas versiones. Esta rama facilita y reduce enormemente
los tiempos de compilación. Dejándolos en alrededor de minuto y medio
16
con el equipo anteriormente mencionado. Cabe destacar que deberemos
descargar dos proyectos, a saber: qemu y qemu-android.
En el proyecto qemu esta una versión antigua y fuertemente parcheada
para hacer funcionar el emulador de Android mientras que en qemu-android
están las nuevas características de QEMU 2.0+ así como las
refactorizaciones y mejoras que se van portando del proyecto antiguo.
Actualmente el proyecto qemu tiene dependencias con qemu-android.
Tanto qemu como qemu-android deben colgar del directorio external del
árbol de AOSP. Una vez hayamos clonado ambos repositorios podremos
empezar a compilar.
Como nota añadida y aunque se hablará posteriormente, si queremos
modificar la capa RIL deberemos clonarla también bajo external.
1. $ cd qemu 2. $ ./android-rebuild.sh –build-qemu-android –debug
De esta forma, compilaremos ambos proyectos desde un único comando.
Otra forma sería compilar cada proyecto por separado. Hay que tener en
cuenta que si solo vamos a modificar el antiguo QEMU solo necesitamos
compilar qemu-android la primera vez, lo cual como es lógico supone una
reducción de tiempo.
Si necesitamos compilar los proyectos por separado estos son los
comandos:
1. $ cd qemu-android 2. $ ./android/scripts/build-qemu-android.sh 3. $ cd qemu 4. $ ./android-rebuild.sh -debug
Una vez hayamos compilado los ejecutables del emulador son generados bajo
la carpeta qemu/objs/
17
3.6 AVD (Android Virtual Device)
Las AVD son las máquinas virtuales que podemos y debemos crear y
configurar para emular los distintos dispositivos. Por ejemplo, podemos crear
una máquina virtual que emule un Nexus 5 o una tablet Samsung. Por defecto
disponemos de varios modelos por defecto, prácticamente en su totalidad de
Google salvo algunos modelos de Samsung. Por supuesto tenemos un gran
abanico de opciones para ajustarnos de la mejor forma posible a los
dispositivos que no estén en la lista por defecto. Para lanzar el emulador es
requisito indispensable utilizar una avd. He aquí los pasos a seguir para crear
una.
Lo primero que debemos hacer es obtener la lista de sistemas disponibles
instalados. De no tener ninguno, deberemos descargar el sdk correspondiente
a la versión deseada.
1. $ android list targets
Seguidamente lanzamos el siguiente comando, habiendo considerado
previamente el id deseado del listado de plataformas arrojado por el
comando anterior.
2. $ android create –n <name> -t <targetID> [-<option> <value>]
Si queremos que la máquina virtual sea compatible con HAXM deberemos
asegurarnos de que la CPU/ABI esté establecida en x86.
18
El resto de opciones podemos ajustarlas cómodamente desde el fichero de
configuración de la máquina virtual. Para acceder a dicha configuración tan
solo tenemos que ir a la carpeta .android en los sistemas Mac/Linux. Dentro,
observaremos un listado de carpetas con el nombre de nuestras máquinas
virtuales creadas, dentro de cada una de estas carpetas hay un config.ini en el
que podremos asignar sus propiedades mediante la pareja clave=valor.
Recomiendo al menos las siguientes opciones:
Figura 4: Lista de opciones del fichero config.ini
19
En caso de querer eliminar una máquina virtual podremos hacerlo de la
siguiente manera:
1. $ android delete avd -n <name>
Una vez creada y configurada la máquina virtual solo queda lanzarla.
4. EJECUTANDO EL EMULADOR
Si estamos con la rama con la rama antigua debemos tener en cuenta que los
ejecutables son añadidos al path automáticamente pero este es temporal. Una vez
cerremos la terminal el path se borrará y no podremos acceder a los ejecutables teniendo
que volver a compilar de nuevo. Aunque se pueden derivar a otra carpeta para
mantenerlos a salvo, las referencias que utilizan los ejecutables se perderán con lo que
no arreglaremos nada. Con la nueva rama este problema fue resuelto y los ejecutables
permanecen en el directorio aunque no son añadidos al path.
4.1 HAXM
Como se ha explicado, el emulador de Android emula un teléfono completo, con su
CPU, GPU, memoria, etc. Esto unido a las pocas mejoras dedicadas da como resultado
una lentitud considerable. Debido a las quejas recurrentes sobre este problema, Intel
decidió lanzar una herramienta que acelerase los tiempos de ejecución y en general de
respuesta del emulador. De esta forma nació HAXM.
HAXM es un driver/módulo de kernel/extensión de kernel según el sistema operativo de
turno, que concede a QEMU acceso directo al hardware en el que se ejecuta.
20
4.1.1 Requisitos
HAXM son las siglas de Hardware Accelerated Execution Manager, una tecnología de
Intel que permite acelerar de forma notable la ejecución del emulador. Solo funciona en
combinación con las imágenes x86 del procesador Intel Atom. Teniendo esto en cuenta
los únicos requisitos son disponer de un hardware que soporte Virtualization Technology,
es decir, debe ser compatible con Intel VT-x y EM64T. Para Linux en lugar de HAXM
debe utilizarse KVM.
Puede ser necesario tener que habilitar la funcionalidad Virtualization Technology y
Execute Disable Bit en la BIOS.
Además es recomendable tener al menos 1GB de RAM.
4.1.2 Instalación
Para cualquiera dos de las tres plataformas Mac/Windows debemos abrir el Android
SDK Manager y bajo la carpeta Extras buscar: Intel x86 Emulator Acclerator (HAXM).
Tanto en Windows como en Mac deberemos buscar el ejecutable intelHAXM dmg o .exe
según proceda y seguir la instalación. Se trata de un proceso sencillo y muy guiado. Lo
único que debemos tener en cuenta es el valor de la cantidad de RAM que vamos a dejar
disponible para HAXM. Se aconseja como mínimo 1GB.
Para Linux el proceso es un poco diferente pero igualmente sencillo. Primero
instalaremos los paquetes necesarios para KVM:
Distribuciones basadas en Debian:
1. # apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-
utils
Posteriormente solo hay que añadir el usuario al grupo KVM y libvirtd:
1. # adduser usuario kvm 2. # adduser usuario libvirtd
21
Para comprobar el funcionamiento:
1. # virsh -c qemu:///system list
Según los datos de performance de Intel las mejoras observables con Intel HAXM
o KVM son de aproximadamente entre 5x y 10x.
4.2 Parámetros del emulador
Con el fin de acelerar la ejecución y rendimiento del emulador todavía más disponemos
de las siguientes opciones al lanzar el comando:
- -gpu-on: Activa la GPU de la maquina anfitriona permitiendo al
emulador hacer uso de ella. Las mejoras visuales y el arranque
mejoran notablemente.
- -no-boot-anim: Desactiva la animación de arranque de Android. Dado
que nuestros propósitos son puramente de desarrollo esto es un
detalle estético innecesario.
- -no-skin: Aunque puede ser útil dado que algunos skins ofrecen
teclado numérico el no cargar uno se consigue aligerar la carga del
emulador.
Así pues tras las mejoras instaladas, configuraciones y parámetros elegidos, la
ejecución del emulador sería así:
1. $ ./objs/emulator –avd NOMBRE –gpu on –no-boot-anim –no-skin
En la máquina mencionada los tiempos de cargar se han reducido a unos 20
segundos.
22
5. IMPLEMENTACIÓN
Con lo visto hasta ahora somos capaces de compilar y ejecutar el resultado de nuestra
compilación con unos tiempos mínimos. Así pues vamos a dar paso a la implementación
de los cambios. Los cambios podrían dividirse en cuatro bloques: Escritura de
configuraciones, lectura de configuraciones, lectura de un fichero de estados y respuesta
basada dicho fichero.
5.1 Modificaciones
El primer cambio se basa en la ampliación del fichero de configuración que se crea para
ofrecer todas las configuraciones hardware disponible para las máquinas virtuales.
Teniendo este fichero en cuenta debemos configurar nuestra máquina virtual,
modificando el config.ini mencionado en los puntos anteriores añadiendo las opciones
que consideremos oportunas.
Una vez que el emulador arranca, carga y analiza los parámetros de este fichero. Con
esta primera modificación se abren las puertas a nuevas características del emulador de
Android permitiendo por ejemplo configurar el roaming, el estado de la conexión con el
operador y algunos datos de éste como el MCC/MNC.
Posteriormente disponemos de un fichero de texto donde con valores separados por
comas con un orden en concreto podemos asignar a estas variables, disponibles en el
fichero de configuración, diversos valores para que cambien de forma fluida sin
necesidad de reiniciar el emulador.
En el transcurso de la investigación para poder realizar estas modificaciones al emulador
una de las opciones fue conseguir comunicarse con el modem que el emulador tiene
para mandar comandos AT+ y conseguir así los cambios deseados. Llegados a este
punto hay que saber que el modem de Android filtra los comandos disponibles para no
responder a cualquier cosa que se le envíe. Los comandos disponibles son de lo más
básico, a saber: solicitar operador, cambio de tecnología, colgar llamada, descolgar
llamada, etc.
Así pues, y aunque al final no fue necesario, si se quería enviar comandos
personalizados al emulador, previamente había que diseñar esos comandos, puesto que
el modem simplemente los filtraba, pero ¿quién se comunicaba con el modem para
enviar esos comandos y que éste pudiese filtrarlos? La respuesta es la capa RIL, una
capa intermedia entre aplicaciones y hardware puro que cada fabricante implementa
siguiendo un estándar. Así que de querer modificar o agregar comandos habría que
hacerlo primero en la capa RIL.
23
Para hacer esto hay clonar el repositorio del directorio de AOSP y hacer que cuelgue del
directorio platform/hardware/ril
Los ficheros que debemos modificar, como mínimo, para la agregación del comando son:
ril_commands.h, ril_commands.cpp y ril.cpp.
Una vez la capa RIL esta modificada y compilada solo resta añadir la librería al emulador
pusheandola al sistema.
Con estos cambios podríamos comunicarnos con el modem del emulador. Para ello
necesitaríamos arrancar el emulador, y desde una terminal abrir una shell:
1. $ adb shell
Una vez dentro solo queda mandar el comando. Pero para ello necesitamos primero el
puerto en el que está escuchando la capa RIL. Para saberlo ejecutamos el siguiente
comando:
1. # getprops rild.libargs
Esto arrojará un resultado como el que sigue:
Figura 5: Conociendo el puerto RIL
Indicándonos el puerto al que debe ser mandado el comando. Para mandar el comando
AT+ deberemos pues realizar lo siguiente:
1. $ echo AT+COMANDO > /dev/PUERTO
24
Para saber si el modem está recibiendo dichos comandos podemos escuchar el log de
la siguiente manera:
1. $ adb logcat –v time –b radio
Con –v time indicamos que queremos ver la hora en el log y con –b radio filtramos la
radio del modem, sin este último parámetro obtendríamos toda la información que
transcurre por el emulador.
Figura 6: Solicitud del operador de red por parte de la capa RIL
25
Llegado a este punto hay que mencionar que es posible reiniciar el modem sin reiniciar
el emulador. Aunque no existe un comando AT+ de lo que si disponemos es de la capa
RIL y una implementación de utilidades bajo la herramienta llamada radiooptions, que
permiten lanzar algunas acciones sobre la radio y la sim del emulador. Entre ellas la
posibilidad de hacer un reinicio limpio y rápido de la radio:
1. $ radiooptions 1 1 0
En el ejemplo, dicho comando seleccionad la opción RESET_MODEM para la SIM 1 y la
opción más común a la hora de elegir el estilo del modem-socket.
Figura 7: Opciones del comando radiooptions de la capa RIL
26
La ventaja del comando radiooptions es que permite reiniciar la radio de forma
continuada. Esto había sido en principio una gran alternativa en caso de que no fuese
factible cargar los valores al emulador por código. Así podrían cargarse a través de
comandos y reiniciar la radio para comenzar otra nueva prueba, cargar otros valores, etc.
Este comando es implementado solo por algunos fabricantes, como por ejemplo
Samsung en sus Samsung Galaxy S5. Sin embargo Google ha decidido no
implementarlo en los Nexus 4.
Finalmente la opción elegida para la carga de valores fue la lectura de un fichero dentro
del modem que describiré a continuación.
5.2 Estructura de las modificaciones
El primer cambio se trata del fichero hardware-config.ini. Dicho fichero una vez
cargado en memoria es una estructura de datos interna a la que se puede acceder
de manera global desde cualquier punto del emulador. Es creada y asignada en los
primeros momentos del arranque del emulador y se mantiene en memoria mientras
éste esté funcionando.
Para que estos valores se hagan efectivos deben estar presentes primero en el
archivo de configuración config.ini de la AVD en cuestión y posteriormente deben ser
leídos y cargados en memoria.
Hay dos grandes cargas dentro de la clase Telephony.
La primera se realiza en el fichero sim.c encargado de emular el comportamiento de
la tarjeta SIM dentro del emulador. En este fichero se leen los campos de la estructura
global de configuración y son asignados los valores a las variables internas previo
checkeo de la lectura de éstas para evitar que sean asignados valores incorrectos.
En todos los casos se asigna un valor por defecto en caso de que el checkeo falle.
Los parámetros modificados dentro de este fichero son:
Número de intentos para introducir el PIN, PIN*, PUK, SIM Serial y SIM status
La siguiente carga se encuentra en android_modem.c
En este fichero se crea, configura y gestiona el comportamiento del modem del
emulador. Inicialmente se crea con la función amodem_create().
27
La primera modificación en esta función pasa por leer un fichero de texto plano al que
separados por comas y dado un orden concreto, se le pasan ciertos valores que
mencionaré posteriormente junto al funcionamiento del fichero. Además dicha función
llama a una función personalizada amodem_reset( AModem modem) que se encarga
de leer, checkear y asignar los valores de la estructura a las variables internas del
modem.
El funcionamiento del fichero es de la siguiente manera: En la primera línea se
encuentra un comentario que indica las columnas en las que se separa cada línea.
Cada línea representa un estado del modem y puede contener diferentes valores
separados por comas que alteren uno o más parámetros del emulador. Además, la
primera columna siempre representa los instantes de tiempo que se quiere que el
emulador permanezca en ese estado.
Por ejemplo, podemos indicarle con una sola línea que permanezca 5 iteraciones en
el siguiente estado: tecnología 2g con roaming activado. Y en la siguiente línea que
permanezca una sola iteración en 3g sin roaming. Así al terminar las cinco iteraciones
pasará automáticamente a 3g y desactivará el roaming.
Al terminar de leerse el fichero vuelve a comenzar por la primera línea
automáticamente.
La lectura del fichero se realiza una única vez y permanece en memoria solo mientras
el emulador este vivo. Aunque el emulador posee dos funciones para guardar y cargar
estados que reflejen un instante exacto de su comportamiento y por lo tanto pudiera
permitirnos guardar ese estado actual y los valores del fichero ante un posible
shutdown del emulador, estas funciones no están implementadas actualmente por el
emulador de Android.
El siguiente paso necesario es controlar los tiempos en los que se cargan las líneas
del fichero y se hacen efectivas. Para ello he hecho un hook a una de las funciones
del modem que tiene un comportamiento periódico y que se encarga de controlar el
nivel de fuerza de la señal, la función handleSignalStrength.
Dicha función es en realidad un comando AT+ llamado CFQ que se encarga de
solicitar el nivel de señal periódicamente. Así pues cada vez que se realiza una
solicitud desde las capas superiores, en realidad se aprovecha para modificar, si
procede, el estado del emulador.
Para gestionar el tiempo en el que el emulador debe permanecer en un estado
concreto se utilizan dos contadores: uno maneja el número de línea actual y el otro el
número de iteraciones para esa línea. De forma que al saltar de línea el contador de
iteraciones se reinicia.
28
Dado que la lectura del fichero se realiza por línea entera y se guardan todos los
caracteres incluidas las comas en memoria, cada vez que handleSignalStrength
solicita una nueva línea este debe encargarse de descodificarla, esto es: realizar un
split por comas y comprobar y asignar parámetros a las variables internas que
procedan. Para dicho split y dado que el emulador esta en c se ha utilizado la función
strtok.
Esta función dado un puntero a char devuelve otro puntero a char, lo cual obliga hacer
una comprobación de distinto de null para poder acceder al valor del campo del
fichero y con ello a añadir un valor especial que representa que ese campo está sin
valor. Lo cual nos obliga a que todos los campos estén rellenados y como mínimo
tendrán su valor especial. Es importante saber esto porque dentro de este fichero no
hay una clave-valor como puede ser en un json o un xml. Esta gramática así como
implementar etiquetas para controlar el comportamiento como ETIQUETA LABEL_1
y GOTO LABEL_1 están pensadas como mejoras futuras ayudando así a la
comprensión del fichero y su gestión.
29
5.3 Lista de propiedades del hardware virtual
La forma de la lista sigue los siguientes campos:
- Name: indica la variable que debe ser añadida al fichero config.ini de
cada máquina virtual si se quiere hacer uso de esta característica.
- Type: no es relevante en el fichero config.ini pero si lo es para el
desarrollo interno.
- Default: solo valido para el desarrollo interno, contiene el valor por
defecto si no se utiliza esta variable en el config.ini
- Abstract y Description: Campos internos para describir la variable. Su
valor es informativo.
Todas estas variables son para ser añadidas en el fichero de configuración, en caso de
querer modificar el fichero interno debe seguirse la guía interna de dicho fichero y separar
los valores y solo los valores por comas. No se debe indicar el parámetro en sí dado que
están ordenados.
1. SIM card serial
name = hw.gsmModem.simSerial
type = string
default = 89014103211118510720
abstract = SIM card serial
description = Serial of the emulated SIM card, maximum 20 digits.
2. GSM IMEI number
name = hw.gsmModem.imei
type = string
default = 000000000000000
abstract = IMEI number
description = IMEI number of the GSM modem, maximum 15 digits.
3. GSM IMSI number
name = hw.gsmModem.imsi
type = string
default = 310260000000000
abstract = IMSI number
description = IMSI number of the GSM modem, maximum 15 digits.
30
4. Operator Home Name
name = hw.gsmModem.operatorHomeName
type = string
default = Android
abstract = Operator Home Name
description = Operator Home (Network) Name, maximum 40 characters.
5. Operator Home MCC and MNC
name = hw.gsmModem.operatorHomeMccMnc
type = string
default = 310260
abstract = Operator Home MCC + MNC
description = Operator Home (Network) MCC + MNC, maximum 6 digits.
6. Operator Roaming Name
name = hw.gsmModem.operatorRoamingName
type = string
default = Android
abstract = Operator Roaming Name
description = Operator Roaming Name, maximum 40 characters.
7. Operator Roaming MCC and MNC
name = hw.gsmModem.operatorRoamingMccMnc
type = string
default = 310295
abstract = Operator Roaming MCC + MNC
description = Operator Roaming MCC + MNC, maximum 6 digits.
8. Operator Data Network
name = hw.gsmModem.operatorDataNetwork
type = string
default = 3
abstract = Operator Data Network
description = Operator Data Network technology, maximum 1 character.
31
9. Operator Registration
name = hw.gsmModem.operatorRegistration
type = string
default = 1
abstract = Operator State
description = Operator State Network or Roaming.
10. Operator SIM State
name = hw.gsmModem.simStatus
type = string
default = 2
abstract = SIM Status
description = SIM Status, maximum 1 character.
11. Operator Operator Status Home
name = hw.gsmModem.operatorStatusHome
type = string
default = 1
abstract = Operator Status Home
description = Operator Status Home, maximum 1 character.
12. Operator Operator Status Roaming
name = hw.gsmModem.operatorStatusRoaming
type = string
default = 2
abstract = Operator Status Roaming
description = Operator Status Roaming, maximum 1 character.
13. Operator Operator Index
name = hw.gsmModem.operatorIndex
type = string
default = 0
abstract = Operator Index
description = Operator Index, maximum 1 character.
14. Operator Radio State
name = hw.gsmModem.radioState
type = string
default = 0
abstract = Radio State
description = Radio State, maximum 1 character.
32
15. Operator RSSI
name = hw.gsmModem.rssi
type = string
default = 7
abstract = RSSI Value
description = RSSI Value, maximum 3 character.
16. Operator BER
name = hw.gsmModem.ber
type = string
default = 99
abstract = BER Value
description = BER Value, maximum 3 character.
17. Operator LAC
name = hw.gsmModem.areaCode
type = string
default = -1
abstract = Area Code
description = Location Area Code, maximum 3 character.
18. Operator Cell ID
name = hw.gsmModem.cellId
type = string
default = -1
abstract = Cell Id
description = Cell Id, maximum 3 character.
19. Operator PIN
name = hw.gsmModem.pin
type = string
default = 0000
abstract = PIN
description = PIN, maximum 4 character.
20. Operator PIN retries
name = hw.gsmModem.pinRetries
type = string
default = 3
abstract = PIN retries
description = PIN retries, maximum 1 character.
33
21. Operator PUK
name = hw.gsmModem.puk
type = string
default = 12345678
abstract = PUK
description = PUK, maximum 8 character.
34
6. LINEAS FUTURAS
Siendo una primera versión es obvio que habrá cosas que mejorar, y que muchas de
ellas saldrán del uso de la herramienta que puedan darle los usuarios/desarrolladores.
Creo que la base del desarrollo está bien asentada pero algunas de las mejoras que se
podrían realizar son las siguientes:
- Rehacer el fichero de lectura para que sea más fácil de leer por el usuario. La
idea sería tener una sintaxis de etiquetas y un par de instrucciones que permitiesen
manejar el comportamiento del fichero.
- Añadir parejas clave-valor al fichero como si se tratase de un json
- Mejorar la granularidad del tiempo sobrescribiendo uno de los bucles principales
del emulador para no tener que hacer el hook de la función handleSignalStrength
- Externalizar el fichero de lectura para que pueda ser cargado mediante opciones
al lanzar el emulador. Una segunda mejora sobre esta misma línea sería poder cargar el
fichero mediante telnet contra el emulador.
7. CONCLUSIONES
Como conclusión final he de decir que ha sido toda una experiencia desarrollar sobre
el emulador de Android. Me ha permitido aprender no solo sobre el emulador sino
también conocer un poco más en profundidad el lenguaje C así como el funcionamiento
de los teléfonos a bajo nivel.
Aunque ha habido momentos difíciles por la escasa documentación que hay sobre el
funcionamiento del emulador a la hora de realizar ciertas acciones o de investigar ha sido
un privilegio poder trabajar en este proyecto.
Además de mi experiencia personal considero que los objetivos han sido cumplidos y
queda asentada una buena base para continuar trabajando en ella en el futuro
mejorando el emulador con los cambios aquí propuestos y los que puedan surgir a través
de los casos de uso.
35
8. AGRADECIMIENTOS
Me gustaría finalizar dando las gracias a todas aquellas personas que me han
ayudado a lo largo de todo el proceso dándome su apoyo e ideas en los momentos en
que no sabía cómo seguir. A mis tutores F.Javier Ferrández Pastor y José Vicente
Noguera por sus ideas sobre RIL y la lectura de ficheros entre otras muchas cosas
Así como a Luis Alberto Vivas Tajuelo por sus opiniones e ideas sobre el proyecto.
9. BIBLIOGRAFÍA
http://developer.android.com/
https://android.googlesource.com
https://wiki.diebin.at/Under_the_hood_of_Android_Emulator
https://software.intel.com/es-es/android/articles/speeding-up-the-android-
emulator-on-intel-architecture
36