Notice:
This post is older than 5 years – the content might be outdated.
With a working HAL, we are able to go further, directly into the Android framework, where the most important system internals are implemented. First we take a look at the Java Native Interface (JNI) Layer, which is located between the HAL and a custom system service, before continuing to the binder, permissions and the manager.
JNI
The JNI enables us to use native implementations in Java, in our case the HAL, which is written in C. So the JNI Layer is nothing more than a wrapper that loads our lcd1313M1hw.imx6.so when init_native is called and defines a wrapper function for each function defined in the HAL.
Because I changed some of the available methods, I need to modify some parts of the JNI located at frameworks/base/services/core/jni/com_android_server_LCDService.cpp. In the JNI Layer, we need to register the JNI for our service (described below) in the Makefile and the onload.cpp. The changes are built into the libandroid_servers.so.
The Service and Binder
By going deeper into the Java world of Android’s framework, we must deal with the mighty Binder. Actually, the Binder does nothing more than inter-process communication (IPC), but that is a really big part in Android. With Binder, it is possible for objects to communicate by only knowing about their interfaces. Explicit dependencies to each other are not necessary at build or compile time.
That means to implement our Java service, we need an interface in order to enable the binder to connect apps or system-internal objects with our service. This interface is defined in AIDL, the Android Interface Definition Language. It is not too different from a traditional Java interface and very easy to understand. We just needed to change the method definitions to the new ones. You can find it under frameworks/base/core/java/android/os/ILCDService.aidl.
Permissions? We need our own permissions?
After we applied the AIDL to its related Makefile, we can go on to the service implementation. For that we need to create an LCDService.java file located at frameworks/base/services/java/com/android/server. Like most parts until now, we just needed to adjust the old implementation to the new, slightly changed interfaces. As you can see in the code, the service controls the hardware via the JNI. But one thing bothered me: The service tries to check if a custom LCD permission is granted.
1 2 3 4 5 6 7 8 9 10 11 |
private void checkPermission(String permission) { if(PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)){ throw new SecurityException("Access denied to process: " + Binder.getCallingPid()+ ", must have permission " +permission); } } |
Where was this permission defined? I did not find any more documentation (there wasn’t too much useful documentation to begin with), but I found a patch file of about 5300 lines of code. Grepping for the permission we looked for in this patch file lead us to frameworks/base/core/res/AndroidManifest.xml. And after adding
1 2 3 4 5 6 7 |
<permission android:name="android.permission.LCD_SERVICE" android:protectionLevel="normal" android:description="@string/permdesc_lcdservice" android:label="@string/permlab_lcdservice" /> |
above the android.permission.SET_WALLPAPER_HINTS, it should be possible to use this permission.
Manager
Because we do no want app developers to use the service directly, we need a service manager which should be accessible through the ordinary SDK. Such a manager exists for nearly all services, and as soon as it is implemented, you are able to test your whole work.
The manger is implemented under frameworks/base/core/java/android/os/LCDManager.java, the same directory in which our ILCDService.aidl is located. As so often before, I just needed to adjust the old manager to the new interfaces. Otherwise, the manager is a really simple construct. In its constructor, the LCDService is handed over and the available methods are just wrappers for their respective counterparts in the service. Not our first wrapper at this point …
Now we are just two little changes away from running the first test app for the Grove LCD. Of course, we want to receive a Manager instance for the new Service by the standard way to do it: via getSystemService. This is why we need to register all the stuff described above in frameworks/base/core/java/android/app/ContextImpl.java by adding:
1 2 3 4 5 6 7 8 9 10 11 |
static { registerService(LCD_SERVICE, new ServiceFetcher(){ public Object createStaticService(ContextImpl ctx){ IBinder b = ServiceManager.getService(LCD_SERVICE); ILCDService service = ILCDService.Stub.asInterface(b); return new LCDManager(service); }}); } |
Normally, a few of this registrations should be located at this file, but for reasons we do not know, there aren’t. We had to do some more research for the right place to register. Take a look at the code if you’re interested in the nitty gritty.
Testing!
At this part, if everything ran correctly until know (not like our first try and the „const“ described in the previous part of this series), you should be able to write a first test application for the Grove LCD.
For several reasons you need to build this test application within the source tree. We will speak about a way to publish our interface for all developers in the next part. But as an example you can control the LCD now directly through the service (only usable for a built-in app):
1 2 3 4 5 6 7 8 9 10 11 |
import android.os.ILCDService; //onCreate IBinder b = ServiceManager.getService("lcd1313M1"); ILCDService lcd = ILCDService.Stub.asInterface(b); lcd.displayInit(); lcd.setRGB(0, 0, 0); |
Or through the ServiceManager:
1 2 3 4 5 6 7 8 9 |
import android.os.LCDManager; //onCreate LCDManager manager = getSystemService(LCD_SERVICE); manager.displayInit(); manager.setRGB(255,0,0); |
For more background on custom service integration look at the Linaro website and the TI Wiki.
Read on
Now we’re prepared to write a wrapper with the SDK Add-On, coming up in the last part of this series! In the meantime, have a look at our Smart Devices division and learn how you can join us as a software developer.