Share
Embed
droidconfr2013-discover-embedded-linux-with-android
Talk given at droidcon fr 2013
http://fr.droidcon.com/2013/sessions/genymobile/
Code sample are available:
https://github.com/CedricCabessa/droidconfr2013
Video:
http://www.youtube.com/watch?v=BgP-QjjWCfA
slide 1 to 37 have speaker notes
slide 38 to the end are the same without speaker notes (download pdf to have correct ratio, speakerdeck do not support slide rotation)
Cédric Cabessa
June 17, 2013
Transcript
74 slides
-
Introduction Android is for mass market. Ecosystem is oriented toward
multimedia content and consumer. Money is made selling games, music and so on. -
Introduction On the other hand, embedded linux is used to
create everykind of project, you have drivers for everything, you can plug any devices you want. The "hdtv blueray microwave washing machine" come from the Yocto-project -
Introduction Can we keep Android with it's UI, its fully
integrated API and yet use it to build our own embedded system ? -
The Question • How to modify Android to support a
new hardware / new features without asking Google. • Android Style You have a dev board (like a pandaboard) with Android. You want to plug a new device (gpio, i2c, rs232, ...). How to use it with an Android like API -
Case study • http://source.android.com • Nexus • Pandaboard • Manufacturer
support Android is opensource (AOSP), it is fairly easy to grab the source and build your own ROM (if you have hardware support: nexus or dev board with manufacturer support). -
Case study • Character device: /dev/droidcon • Convert upper case
to lower case, in kernel land! In linux, most of the drivers are exposed through the "/dev" fiilesystem. To send data to the driver, you simply open the file and write to it. In real life, we assume you already have a driver for your device -
Case study We assume we have a support for linux
(driver) for our new hardware, we want to create applications that use it. We have to patch every layer from kernel to framework -
Kernel / Driver • Not part of AOSP • Driver
can be built with the kernel • Or as a seperate module (.ko) • GPLv2 Driver is provided by your board manufacturer (kernel) or as a module from your device provider. Of course you can write your own if you have a custom device. -
Kernel / Driver • init.rc on boot insmod /system/lib/modules/droidcon.ko chown
system system /dev/droidcon chmod 0600 /dev/droidcon • device/<vendor>/<product>/device.mk PRODUCT_COPY_FILES := \ device/<vendor>/<product>/droidcon.ko:system/lib/modules/droidcon.ko -
Kernel / Driver • Try it (connect as root) adb
shell # ls -l /dev/droidcon crw------- system system 249, 0 droidcon # echo Hello > /dev/droidcon # cat /dev/droidcon hELLO # with real world devices, it is hard to check everything works just after loading the module -
Kernel / Driver • Our device is only usable by
'system' • Run our app as system? => NO • system_server use the device • App communicate with system_server We will have to create permissions -
HAL We simply load the driver and check permission. It's
time to use it. Hardware Abstraction Layer -
HAL • C library • Talk to driver • Expose
hardware feature • Often closed source :-( expose hardware feature: driver can only support read / write, HAL should expose more advanced semantics that are implemented using read/write/... ex: usb missile launcher: HAL: missile_launch() Driver: write(/dev/foo, 42) -
HAL • hardware/droidcon/lib • libdroidcon.h ssize_t droidcon_getdata(void *buf, size_t count);
ssize_t droidcon_putdata(const void *buf, size_t count); void droidcon_clear(void); • Do complicated thing here! And only here The driver only support open / read / write / close. We create a "droidcon_clear" function, this semantic make sense from a driver user point of view. In fact it is implemented as write(fd, 0, 0) ==> intelligent stuff go here -
JNI • frameworks/base/core/java/android/droidcon/ LibDroidcon.java package android.droidcon; public class LibDroidcon {
public native static void clear(); public native static String getData() throws DroidconException; public native static void putData(String in) throws DroidconException; static { System.loadLibrary("droidcon_jni"); } } Rem: we hack framework! May be we could separate our change from the standard android, but we will have to modify SystemServer so can't stay really separated. -
JNI • hardware/droidcon/jni/ android_droidcon_LibDroidcon.c JNIEXPORT void JNICALL Java_android_droidcon_LibDroidcon_putData (JNIEnv *env,
jclass cls, jstring string) { int ret; const char *buff = (*env)->GetStringUTFChars(env, string, NULL); int length = (*env)->GetStringLength(env, string); ret = droidcon_putdata(buff, length); if (ret < 0) { ThrowDroidconException(env, "fail to put data"); } (*env)->ReleaseStringUTFChars(env, string, buff); } -
JNI • Tip for testing: ◦ Add a Main.java in
android.droidcon public class Main { public static void main (String[] args) { try { LibDroidcon.putData("Hello"); String out = LibDroidcon.getData(); System.out.println(out); LibDroidcon.clear(); } catch (DroidconException e) { Log.d("droidcon", e.toString()); } } } ◦ Run it with the shell # dalvikvm android.droidcon.Main hELLO -
System Service Now we can use our HAL from java.
SystemService run as system. When you use context.getSystemService(...) you only get a proxy that send command through binder IPC to the SystemService. System Service do the real work and check permission. -
System Service • Must implement a Binder interface • framework/base/droidcon/java/android/droidcon/
IDroidconManager.aidl package android.droidcon; /** {@hide} */ interface IDroidconManager { void clear(); String getData(); void putData(String data); } Declare every methods the client (framework) can call on the server (System Service) Can't throw exception here. Use return code or something else. -
System Service package com.android.server; /** @hide */ class DroidconService extends
IDroidconManager.Stub { public String getData() { enforceAccessPermission(); try { return LibDroidcon.getData(); } catch(DroidconException e) { Slog.d(TAG, "cannot getdata"); } return null; } private void enforceAccessPermission() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DROIDCON_ACCESS, "DroidconService"); } IDroidconManager.Stub is automatically generated from IDroidconManager.aidl Check for exception here and return Enforce permission (framework/base/core/res/AndroidManifest.xml) -
System Service • Hack SystemServer.java public void run() { [...]
try { Slog.i(TAG, "Droidcon Service"); droidconService = new DroidconService(context); ServiceManager.addService(Context.DROIDCON_SERVICE, droidconService); } catch (Throwable e) { reportWtf("starting Droidcon Service", e); } -
Framework DroidconService is launched at startup. It listen to a
Binder interface. We have to write the client side: Framework -
Framework • framework/base/droidcon/java/android/droidcon DroidconManager.java public class DroidconManager { IDroidconManager mService;
/** @hide */ public DroidconManager(IDroidconManager service) { mService = service; } public String getData() { try { return mService.getData(); } catch (RemoteException e) { e.printStackTrace(); } return null; } Do nothing but calling methods through the binder interface (and checking return value) -
Framework • Hack ContextImpl.java class ContextImpl extends Context { static
{ [...] registerService(DROIDCON_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(DROIDCON_SERVICE); IDroidconManager service = IDroidconManager.Stub.asInterface (b); return new DroidconManager(service); }}); -
Framework • Create a new SDK ◦ make update-api ◦
make sdk • Configure eclipse -
Framework <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.droidcon" android:versionCode="1" android:versionName="1.0" >
<uses-permission android:name="android.permission.DROIDCON_ACCESS"/> • AndroidManifest.xml -
Conclusion • Hardware support is not an excuse to not
choose Android • Kernel Driver and HAL should be re-usable • Upper layers are mostly "glue code" • Fragmentation? -
Q&A Genymobile SAS - 41, rue Meslay 75003 PARIS -
01 83 64 25 40 www.genymobile.com QUESTIONS ? Cédric Cabessa [email protected] https://speakerdeck.com/cedriccabessa/ droidconfr2013-discover-embedded-linux-with-android https://github.com/CedricCabessa/droidconfr2013 -
The Question • How to modify Android to support a
new hardware / new features without asking Google. • Android Style -
Case study • Character device: /dev/droidcon • Convert upper case
to lower case, in kernel land! -
Kernel / Driver • Not part of AOSP • Driver
can be built with the kernel • Or as a seperate module (.ko) • GPLv2 -
Kernel / Driver • init.rc on boot insmod /system/lib/modules/droidcon.ko chown
system system /dev/droidcon chmod 0600 /dev/droidcon • device/<vendor>/<product>/device.mk PRODUCT_COPY_FILES := \ device/<vendor>/<product>/droidcon.ko:system/lib/modules/droidcon.ko -
Kernel / Driver • Try it (connect as root) adb
shell # ls -l /dev/droidcon crw------- system system 249, 0 droidcon # echo Hello > /dev/droidcon # cat /dev/droidcon hELLO # -
Kernel / Driver • Our device is only usable by
'system' • Run our app as system? => NO • system_server use the device • App communicate with system_server -
HAL • C library • Talk to driver • Expose
hardware feature • Often closed source :-( -
HAL • hardware/droidcon/lib • libdroidcon.h ssize_t droidcon_getdata(void *buf, size_t count);
ssize_t droidcon_putdata(const void *buf, size_t count); void droidcon_clear(void); • Do complicated thing here! And only here -
JNI • frameworks/base/core/java/android/droidcon/ LibDroidcon.java package android.droidcon; public class LibDroidcon {
public native static void clear(); public native static String getData() throws DroidconException; public native static void putData(String in) throws DroidconException; static { System.loadLibrary("droidcon_jni"); } } -
JNI • hardware/droidcon/jni/ android_droidcon_LibDroidcon.c JNIEXPORT void JNICALL Java_android_droidcon_LibDroidcon_putData (JNIEnv *env,
jclass cls, jstring string) { int ret; const char *buff = (*env)->GetStringUTFChars(env, string, NULL); int length = (*env)->GetStringLength(env, string); ret = droidcon_putdata(buff, length); if (ret < 0) { ThrowDroidconException(env, "fail to put data"); } (*env)->ReleaseStringUTFChars(env, string, buff); } -
JNI • Tip for testing: ◦ Add a Main.java in
android.droidcon public class Main { public static void main (String[] args) { try { LibDroidcon.putData("Hello"); String out = LibDroidcon.getData(); System.out.println(out); LibDroidcon.clear(); } catch (DroidconException e) { Log.d("droidcon", e.toString()); } } } ◦ Run it with the shell # dalvikvm android.droidcon.Main hELLO -
System Service • Must implement a Binder interface • framework/base/droidcon/java/android/droidcon/
IDroidconManager.aidl package android.droidcon; /** {@hide} */ interface IDroidconManager { void clear(); String getData(); void putData(String data); } -
System Service package com.android.server; /** @hide */ class DroidconService extends
IDroidconManager.Stub { public String getData() { enforceAccessPermission(); try { return LibDroidcon.getData(); } catch(DroidconException e) { Slog.d(TAG, "cannot getdata"); } return null; } private void enforceAccessPermission() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DROIDCON_ACCESS, "DroidconService"); } -
System Service • Hack SystemServer.java public void run() { [...]
try { Slog.i(TAG, "Droidcon Service"); droidconService = new DroidconService(context); ServiceManager.addService(Context.DROIDCON_SERVICE, droidconService); } catch (Throwable e) { reportWtf("starting Droidcon Service", e); } -
Framework • framework/base/droidcon/java/android/droidcon DroidconManager.java public class DroidconManager { IDroidconManager mService;
/** @hide */ public DroidconManager(IDroidconManager service) { mService = service; } public String getData() { try { return mService.getData(); } catch (RemoteException e) { e.printStackTrace(); } return null; } -
Framework • Hack ContextImpl.java class ContextImpl extends Context { static
{ [...] registerService(DROIDCON_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(DROIDCON_SERVICE); IDroidconManager service = IDroidconManager.Stub.asInterface (b); return new DroidconManager(service); }}); -
Framework • Create a new SDK ◦ make update-api ◦
make sdk • Configure eclipse -
Framework <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.droidcon" android:versionCode="1" android:versionName="1.0" >
<uses-permission android:name="android.permission.DROIDCON_ACCESS"/> • AndroidManifest.xml -
Conclusion • Hardware support is not an excuse to not
choose Android • Kernel Driver and HAL should be re-usable • Upper layers are mostly "glue code" • Fragmentation? -
Q&A Genymobile SAS - 41, rue Meslay 75003 PARIS -
01 83 64 25 40 www.genymobile.com QUESTIONS ? Cédric Cabessa [email protected] https://speakerdeck.com/cedriccabessa/ droidconfr2013-discover-embedded-linux-with-android https://github.com/CedricCabessa/droidconfr2013