I'm trying to understand a piece of code that uses Android Binders and Messages to perform IPC. I've read a few articles, papers and slides on binders, but I'm still confused and have a hard time understanding the code, since I didn't encounter any tutorials/examples of actually implementing binders in java code. I am listing down the sanitized/abbreviated code below as well as my understanding of what it does and where it gets confusing. I would appreciate if someone could help me understand this, especially pertaining to the flow of Parcels and Messages.
MyService.java
public class MyService extends Service
{
private final IMyService.Stub mBinder;
private Context _mContext;
private MyServiceHandler mHandler;
private HanderThread mHandlerThread;
private IHelperService mHelperService;
public MyService() {
this.mBinder = new IMyService.Stub() {
public void start_data(final String data) {
try {
if (MyService.this.mHelperService == null) {
return;
}
Message msg = MyService.this.getHandler().obtainMessage(3, (Object)data);
MyService.this.getHandler().sendMessage(msg);
} catch (SecurityException ex) {
// do some logging
}
};
}
private void startMessageProcess(final String message) {
// Process the message argument
final byte[] dataArray = this.getByteArray(message);
byte[] returnData;
try {
returnData = this.mHelperService.foo(dataArray);
} catch (Exception e) {
// do some logging
}
Message msg = this.getHandler().obtainMessage(3, (Object)message);
this.getHandler.sendMessage(msg);
}
private String getServerData(int id) {
// builds a Uri based on id and posts it, retrieves String data from HTTP entity and returns it
}
private boolean postDataToServer(final byte[] arrayData) {
// builds a Uri, post data to it, gets the status code, returns true if successful
}
private void sendData(final byte[] arrayData) {
final Intent intent = new Intent();
intent.setAction("com.my.service.intent.action.MY_RESULT");
intent.putExtra("com.my.service.intent.extra.RESULT", 0);
intent.putExtra("com.my.service.intent.extra.MY_DATA", arrayData);
this._mContext.sendBroadcast(intent, "com.my.service.permission.MY_PERMISSION");
}
public Handler getHandler() {
return this.mHandler;
}
public IBinder onBind(final Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (action != null && action.equals("com.my.service.intent.action.BIND_MY_SERVICE")) {
this._mContext = this.getApplicationContext();
return (IBinder)this.mBinder;
}
}
return null;
}
public void onCreate() {
super.onCreate();
try {
final Class<?> serviceManager = Class.forName("android.os.ServiceManager");
this.mHelperService = mHelperService.Stub.asInterface((IBinder)serviceManager.getMethod("getService", String.class).invoke(serviceManager, "helperservice"));
if (this.mHelperService == null) {
return;
}
}
catch (ClassNotFoundException ex2) {
Log.e("MyService", "Helper service ClassNotFoundException !!!");
}
catch (Exception ex) {
Log.e("MyService", "onCreate() Exception: " + Log.getStackTraceString((Throwable)ex));
}
this.mHandlerThread = new HandlerThread("MyService");
this.mHandlerThread.start();
this.mHandler = new MyServiceHandler(this.mHandlerThread.getLooper());
}
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent != null) {
super.onStartCommand(intent, flags, startId);
}
return START_NOT_STICKY;
}
private final class MyService Handler extends Handler
{
public MyServiceHandler(final Looper looper) {
super(looper);
}
public void handleMessage(final Message message) {
if (message != null) {
switch (message.what) {
case 2: {
final String data = (String)message.obj;
if (data != null) {
MyService.this.startMessageProcess(data);
return
}
break;
}
case 3: {
MyService.this.sendData((byte[])message.obj);
}
}
}
}
}
}
IMyService.java
public interface IMyService extends IInterface
{
void start_data(final String p0) throws RemoteException;
public abstract static class Stub extends Binder implements IMyService
{
public Stub() {
this.attachInterface((IInterface)this, "com.my.service.IMyService");
}
public static IMyService asInterface(final IBinder binder) {
if (binder == null) {
return null;
}
final IInterface queryLocalInterface = binder.queryLocalInterface("com.my.service.IMyService");
if (queryLocalInterface != null && queryLocalInterface instanceof IMyService) {
return (IMyService)queryLocalInterface;
}
return new Proxy(binder);
}
public IBinder asBinder() {
return (IBinder)this;
}
public boolean onTransact(final int code, final Parcel data, final Parcel reply, final int flags) throws RemoteException {
switch (code) {
case 1: {
data.enforceInterface("com.my.service.IMyService");
this.start_data(data.readString());
reply.writeNoException();
return true;
}
case 1000000000: {
reply.writeString("com.my.service.IMyService");
return true;
}
default: {
return super.onTransact(n, data, reply, flags);
}
}
}
private static class Proxy implements IMyService
{
private IBinder mRemote;
Proxy(final IBinder mRemote) {
this.mRemote = mRemote;
}
public IBinder asBinder() {
return this.mRemote;
}
public String getInterfaceDescriptor() {
return "com.my.service.IMyService";
}
@Override
public void start_data(final String data) throws RemoteException {
final Parcel data = Parcel.obtain();
final Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken("com.my.service.IMyService");
data.writeString(data);
this.mRemote.transact(1, data, reply, 0);
reply.readException();
}
finally {
reply.recycle();
data.recycle();
}
}
}
}
}
IHelperService.java
public interface IHelperService extends IInterface
{
// a bunch of method declarations here
public abstract static class Stub extends Binder implements IHelperService
{
public Stub() {
this.attachInterface((IInterface)this, "android.service.helper.IHelperService");
}
public static IHelperService asInterface(final IBinder binder) {
if (binder == null) {
if (queryLocalInterface != null && queryLocalInterface instanceof IHelperService) {
return (IHelperService)queryLocalInterface;
}
return new Proxy(binder);
}
}
public IBinder asBinder() {
return (IBinder)this;
}
public boolean onTransact(int code, final Parcel data, final Parcle reply, int flags) throws RemoteException {
switch (code) {
// does a bunch of bunch of stuff here based on the incoming code
}
}
}
private static class Proxy implements IHelperService
{
private IBinder mRemote;
Proxy(final IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
// a bunch of method definitions here, everything that was declared above
}
}
I'm having a hard time tracing who sends what to who. For example, in the Proxy of IMyService, start_data calls transact(mRemote), but who is mRemote? Also, in the MyService() constructor as well as startMessageProcess, there are calls to sendMessage, who is it sending to?
Then, there are private methods in MyService that don't seem to be called locally, such as startMessageProcess and getServerData. Who else can call these methods if they're private?