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; } }