package vandy.mooc.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
/**
* @class GenericServiceConnection
*
* @brief This class provides a generic framework for defining a
* ServiceConnection object to an AIDLInterface that resides in
* a Bound Service. It uses Java reflection to factor out
* common behavior that is otherwise written in a tedious and
* error-prone way for each ServiceConnection object.
*
* Thanks to Peter Koves for providing the reflection logic!
*/
public class GenericServiceConnection<AIDLInterface extends
android.os.IInterface>
implements ServiceConnection {
/**
* The following are dependent on code generated by the AIDL
* compiler, so if that generated code were to change (which is
* unlikely) we'd need to update this code.
*/
private static final String STUB = "Stub";
private static final String AS_INTERFACE = "asInterface";
private static final Class<?>[] AI_PARAMS = {IBinder.class};
/**
* Reference to the AIDL interface object after the client has
* finished binding to the Bound Service.
*/
private AIDLInterface mInterface;
/**
* The stub class of the AIDL interface.
*/
private final Class<?> mStub;
/**
* The reflective asInterface(IBinder service) method of the stub.
*/
private final Method mAsInterface;
/**
* Accessor that returns the AIDL interface object.
*/
public AIDLInterface getInterface() {
return mInterface;
}
/**
* Create a Generic ServiceConnection using the specified AIDL
* interface.
* @param aidl the AIDL Interface class object which must match
* the generic parameter.
*/
public GenericServiceConnection(final Class<AIDLInterface> aidl) {
Class<?> stub = null;
Method method = null;
for (final Class<?> c : aidl.getDeclaredClasses()) {
if (c.getSimpleName().equals(STUB)) {
try {
stub = c;
method = stub.getMethod(AS_INTERFACE,
AI_PARAMS);
break;
} catch (final NoSuchMethodException e) { // Should not be possible
e.printStackTrace();
}
}
}
mStub = stub;
mAsInterface = method;
}
/**
* Hook method called back by the Android Service framework after
* connection is established to a Bound Service.
*/
@SuppressWarnings("unchecked")
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
Log.d("GenericServiceConnection", "Connected to ComponentName " + name);
try {
mInterface = (AIDLInterface)mAsInterface.invoke(mStub,
new Object[]{service});
} catch (IllegalArgumentException e) { // Should not be possible
e.printStackTrace();
} catch (IllegalAccessException e) { // Should not be possible
e.printStackTrace();
} catch (InvocationTargetException e) { // Should not be possible
e.printStackTrace();
}
}
/**
* Called if the Bound Service crashes and is no longer
* available. The ServiceConnection will remain bound, but the
* Service will not respond to any requests.
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mInterface = null;
}
}