I have a MainActivity which will issue IPC call to a remote AutoCompleteService service.
During execution of AutoCompleteService‘s IPC function, the service will issue another IPC call back to MainActivity.
MainActivity.java
// Receive IPC call from AutoCompleteService.
private StockInfoObserver.Stub stockInfoObserver = new StockInfoObserver.Stub() {
@Override
public void update(StockInfo stockInfo) throws RemoteException {
// TODO Auto-generated method stub
Log.i(TAG, android.os.Process.myPid() + " : MainActivity receive ipc call : " + Thread.currentThread().getId());
}
};
...
...
...
// Issue IPC call to AutoCompleteService.
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Test on API.
try {
Log.i(TAG, android.os.Process.myPid() + " : MainActivity start issue IPC call to remote service : " + Thread.currentThread().getId());
// autoCompleteApi.handle will issue IPC call to remote service.
autoCompleteApi.handle("abc");
Log.i(TAG, android.os.Process.myPid() + " : MainActivity end issue IPC call to remote service : " + Thread.currentThread().getId());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
AutoCompleteService.java
private AutoCompleteApi.Stub autoCompleteApi = new AutoCompleteApi.Stub() {
private List<StockInfoObserver> stockInfoObservers = new ArrayList<StockInfoObserver>();
@Override
public void handle(String string) {
Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start receive ipc call : " + Thread.currentThread().getId());
try {
for (StockInfoObserver stockInfoObserver : stockInfoObservers) {
Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start IPC call to MainActivity : " + Thread.currentThread().getId());
// stockInfoObserver.update will issue IPC call back to MainActivity
stockInfoObserver.update(null);
Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end IPC call to MainActivity : " + Thread.currentThread().getId());
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end receive ipc call : " + Thread.currentThread().getId());
}
@Override
public void attachStockInfoObserver(StockInfoObserver stockInfoObserver)
throws RemoteException {
if (stockInfoObservers.contains(stockInfoObserver) == false) {
stockInfoObservers.add(stockInfoObserver);
}
}
};
My initial expectation is that, deadlock will occur. This is due to my observation. When issuing an IPC call, the issuer will only return from IPC call, after the IPC receiver finished its IPC function execution.
MainActivityissues IPC call toAutoCompleteServicethroughautoCompleteApi.handle.MainActivitywill now wait tillAutoCompleteServicefinished its execution.AutoCompleteServiceissues IPC call toMainActivitythroughstockInfoObserver.update.AutoCompleteServicewill now wait tillMainActivityfinished its execution.- However,
MainActivity‘s thread is still waiting, there are no thread which will performupdatefunction. - Both processes keep waiting for each others.
However, the above doesn’t occur. This is the Log I’m getting. Everything just work flawless.
// Log in MainActivity TAG
3930 : MainActivity start issue IPC call to remote service : 1
3930 : MainActivity receive ipc call : 1
3930 : MainActivity end issue IPC call to remote service : 1
// Log in AutoCompleteService TAG
3961 : AutoCompleteService start receive ipc call : 494
3961 : AutoCompleteService start IPC call to MainActivity : 494
3961 : AutoCompleteService end IPC call to MainActivity : 494
3961 : AutoCompleteService end receive ipc call : 494
But I don’t really understand. If the MainActivity thread (with Id 1) is not returning from a function call (autoCompleteApi.handle), how can it “jump” over to execute another function (update(StockInfo stockInfo))?
I would be expecting MainActivity receive ipc call being printed by different thread. Not the thread with Id 1. If not, deadlock should occur.
In case you are interested to try to out, kindly download the complete source code right here : https://www.dropbox.com/s/8hd7v5acjd213l1/jstock-android2.zip
An interesting question. My first thought that the incoming IPC call is handled on a different thread (as it normally would occur with an incoming IPC call) turned out to be wrong in this specific scenario.
Looking at the execution stack when the incoming call arrives one sees (most recent stack frames at the top):
So all is taking place in the same thread. The incoming call looks like a subroutine call of the outgoing call. The magic to make this possible is happening in native code (or maybe in the kernel). There is an interesting paper by Thorsten Schreiber explaining some of the internals of the Binder mechanism. Unfortunately it does not the discuss the back call to the same process as it happens in this example.
There is a small remark about the feature in this blog post:
Maybe there is more to find in the OpenBinder website, the root of the Android Binder implementation.