Android Interface Definition Language

To communicate from one process to another, data stored in memory has to be moved across process boundaries. That means the data has to be "marshalled"—packaged for transport—and "unmarshalled"—put into the right member variables after the data has been moved across the process boundary. (Some Android documentation uses the word "flattened," with the connotation of taking a data stored in several objects and turning it into a "flat" array of bytes that can be sent between processes.)

Java's basic types, such as String, are easy to marshall, but complex types, such as multidimensional arrays, are much harder. Marshalling data spread in an object that holds references to other objects requires following every reference and marshalling all the data that it references.

Usually, marshalling and unmarshalling is performed on the parameters in a remote method call, to let you pass data from one application to another and return results.

Marshalling and unmarshalling data is tedious, and you would find it hard to understand code that had to carry out the task every place it uses inter-process communication. Therefore, most implementations of remote objects or components use an interface definition language that generates calls to marshalling methods. The syntax of the interface definition language resembles the main language in use (Java in this case), so that a remote procedure call closely resembles a normal method call. However, the interface definition language really is a separate language.

AIDL syntax is identical to Java interface definition syntax, except that in AIDL you can label the parameters for remote method calls as in, out, or inout. Any parameter labeled in will be transferred to the remote method, whereas any parameter labeled out will be returned to the caller from the remote method. In the example, from the ApiDemos application we use here, the keywords indicating in and out parameters are not used. The defaults apply: all parameters are in, the return value is used for returning data from the remote method, and any parameter labeled inout will transfer data to the remote method and refer to a value transferred from the remote method when it returns. In the example, the AIDL code is therefore completely compatible, in syntax, to Java code.

When you save your AIDL file in Eclipse, the Android Eclipse plug-in compiles it. Both the calling and implementing side of a remote method interface share the information in the AIDL file.

For the examples in this section, we're excerpting code from the ISecondary.aidl file in the ApiDemos application.

This is how you specify an interface to a remote object:

interface ISecondary { /**

* Request the PID of this service, to do evil things with it.

* This demonstrates the basic types that you can use as parameters

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);

This looks like Java code, but it isn't. It looks like an interface definition. There are two method signatures, and no implementation of the methods. That is all AIDL needs to create code that moves the parameters between applications. Next we will take a look at the code generated by AIDL to see exactly how the parameters are moved from one process to another, and to see how to implement the API defined in this AIDL definition.

The Android SDK plug-in for Eclipse automatically compiles this code to Java, resulting in the following set of Java definitions. Normally this code is not formatted for readability, so what you see here looks different from the file you see in the ApiDemos project in your Eclipse IDE. But it is the same Java code: package com.example.android.apis.app;

import java.lang.String; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; import android.os.Binder;

import android.os.Parcel;

* Example of a secondary interface associated with a service. (Note that

* the interface itself doesn't impact, it is just a matter of how you

public interface ISecondary extends android.os.IInterface {

/** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.android.apis.app.ISecondary {

private static final java.lang.String DESCRIPTOR = "com.example.android.apis.app.ISecondary";

/** Construct the stub at attach it to the interface. */ public Stub() {

this.attachInterface(this, DESCRIPTOR);

* Cast an IBinder object into an ISecondary interface,

* generating a proxy if needed.

public static com.example.android.apis.app.ISecondary asInterface(android.os.IBinder obj) { if ((obj == null)) { return null;

android.os.IInterface iin =

(android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.android.apis.app.ISecondary))) { return ((com.example.android.apis.app.ISecondary) iin);

return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);

public android.os.IBinder asBinder() { return this;

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) {

case INTERFACE_TRANSACTION: {

reply.writeString(DESCRIPTOR); return true;

case TRANSACTION_getPid: {

data.enforceInterface(DESCRIPTOR); int _result = this.getPid(); reply.writeNoException(); reply.writeInt(_result); return true;

case TRANSACTION_basicTypes: {

data.enforceInterface(DESCRIPTOR); int _arg0;

_arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString();

this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);

reply.writeNoException();

return true;

return super.onTransact(code, data, reply, flags);

private static class Proxy implements com.example.android.apis.app.ISecondary {

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) { mRemote = remote;

public android.os.IBinder asBinder() { return mRemote;

public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR;

* Request the PID of this service, to do evil things with it. */

public int getPid() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally {

return _result;

* This demonstrates the basic types that you can use as parameters

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeInt(anInt);

_data.writeLong(aLong);

_data.writeFloat(aFloat);

_data.writeDouble(aDouble);

_data.writeString(aString);

mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally {

static final int TRANSACTION_getPid = (IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_basicTypes = (IBinder.FIRST_CALL_TRANSACTION

* Request the PID of this service, to do evil things with it.

public int getPid() throws android.os.RemoteException;

* This demonstrates the basic types that you can use as parameters

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

That's a lot of code! Now you can appreciate the value of AIDL instead of building a remote object interface by hand. After we see what is going on inside the AIDL-generated code, we will take a look at the other two steps to creating and using a remote object interface: implementing the methods and invoking them.

0 0

Post a comment