content: package cn.thens.okbinder2;
  
  import android.os.Binder;
  import android.os.IBinder;
  import android.os.IInterface;
  import android.os.Parcel;
  import android.os.RemoteException;
  import android.util.Log;
  
  import java.util.HashMap;
  import java.util.Map;
  import java.util.NoSuchElementException;
  
  public interface OkBinderFactory {
      Binder newBinder(Class<?> serviceClass, Object remoteObject);
  
      Object newProxy(Class<?> serviceClass, IBinder binder);
  
      interface Function {
          Object invoke(Object obj, Object[] args) throws Throwable;
      }
  
      class BaseBinder extends Binder implements IInterface {
          protected final Object remoteObject;
          protected final String descriptor;
          protected final ClassLoader classLoader;
          private final Map<String, Function> functions = new HashMap<>();
  
          public BaseBinder(Class<?> serviceClass, final Object remoteObject) {
              this.remoteObject = remoteObject;
              this.descriptor = serviceClass.getName();
              this.classLoader = remoteObject.getClass().getClassLoader();
              attachInterface(this, descriptor);
              ObjectFunctions.inject(this);
          }
  
          protected void register(String methodId, Function func) {
              functions.put(methodId, func);
          }
  
          public Object getRemoteObject() {
              return remoteObject;
          }
  
          @Override
          public IBinder asBinder() {
              return this;
          }
  
          @Override
          public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
              if (code == IBinder.FIRST_CALL_TRANSACTION) {
                  try {
                      data.enforceInterface(descriptor);
                      int magicNumber = data.readInt();
                      if (magicNumber != OkBinderVersion.MAGIC_NUMBER) {
                          throw new IllegalArgumentException("Mismatched magic number " + magicNumber);
                      }
                      int version = data.readInt();
                      if (!OkBinderVersion.isSupported(version)) {
                          throw new IllegalArgumentException("Unsupported version " + version +
                                  ", the min supported is " + OkBinderVersion.minSupported());
                      }
                      String functionId = data.readString();
                      int argCount = data.readInt();
                      Object[] args = new Object[argCount];
                      for (int i = 0; i < argCount; i++) {
                          args[i] = OkBinderParcel.read(data, classLoader);
                      }
                      Function function = functions.get(functionId);
                      if (function == null) {
                          throw new NoSuchElementException("Unregistered function " + functionId);
                      }
                      Object result = function.invoke(remoteObject, args);
                      if (reply != null) {
                          reply.writeNoException();
                          if (result != null) {
                              reply.writeInt(1);
                              OkBinderParcel.write(reply, result);
                          } else {
                              reply.writeInt(0);
                          }
                      }
                      return true;
                  } catch (Throwable e) {
                      Throwable cause = ErrorUtils.unwrap(e);
                      Log.e(OkBinder.TAG, "Binder call failed", cause);
                      RemoteException remoteException = new RemoteException();
                      remoteException.initCause(cause);
                      throw remoteException;
                  }
              }
              return super.onTransact(code, data, reply, flags);
          }
      }
  
      class BaseProxy {
          protected final IBinder binder;
          protected final ClassLoader classLoader;
          protected final String descriptor;
  
          public BaseProxy(Class<?> serviceClass, IBinder binder) {
              this.binder = binder;
              this.classLoader = serviceClass.getClassLoader();
              this.descriptor = serviceClass.getName();
          }
  
          protected Object transact(int flags, String methodId, Object... args) {
              if (!binder.isBinderAlive()) {
                  throw new IllegalStateException("binder has died");
              }
              Parcel data = Parcel.obtain();
              Parcel reply = Parcel.obtain();
              Object result = null;
              try {
                  data.writeInterfaceToken(descriptor);
                  data.writeInt(OkBinderVersion.MAGIC_NUMBER);
                  data.writeInt(OkBinderVersion.current());
                  data.writeString(methodId);
                  if (args != null) {
                      data.writeInt(args.length);
                      for (Object arg : args) {
                          OkBinderParcel.write(data, arg);
                      }
                  } else {
                      data.writeInt(0);
                  }
                  binder.transact(Binder.FIRST_CALL_TRANSACTION, data, reply, flags);
                  reply.readException();
                  if (reply.readInt() != 0) {
                      result = OkBinderParcel.read(reply, classLoader);
                  }
              } catch (Throwable e) {
                  throw ErrorUtils.wrap(e);
              } finally {
                  reply.recycle();
                  data.recycle();
              }
              return result;
          }
  
          @Override
          public int hashCode() {
              return ObjectFunctions.hashCode(this);
          }
  
          @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
          @Override
          public boolean equals(Object obj) {
              return ObjectFunctions.equals(this, obj);
          }
  
          @SuppressWarnings("NullableProblems")
          @Override
          public String toString() {
              return ObjectFunctions.toString(this);
          }
      }
  }
  