package biz.bokhorst.xprivacy; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; import android.os.Binder; import android.util.Log; public class XGoogleApiClient extends XHook { private Methods mMethod; private XGoogleApiClient(Methods method, String restrictionName) { super(restrictionName, method.name(), "GMS5." + method.name()); mMethod = method; } public String getClassName() { return "com.google.android.gms.common.api.GoogleApiClient$Builder"; } // @formatter:off // GoogleApiClient.Builder addConnectionCallbacks(GoogleApiClient.ConnectionCallbacks listener) // https://developer.android.com/reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html // https://developer.android.com/reference/com/google/android/gms/common/api/PendingResult.html // https://developer.android.com/reference/com/google/android/gms/common/api/Status.html // https://developer.android.com/reference/com/google/android/gms/common/api/ResultCallback.html // @formatter:on private enum Methods { addConnectionCallbacks }; public static List<XHook> getInstances() { Util.log(null, Log.INFO, "Loaded GoogleApiClient$Builder uid=" + Binder.getCallingUid()); List<XHook> listHook = new ArrayList<XHook>(); listHook.add(new XGoogleApiClient(Methods.addConnectionCallbacks, null)); return listHook; } @Override protected void before(XParam param) throws Throwable { switch (mMethod) { case addConnectionCallbacks: if (param.args.length > 0 && param.args[0] != null) { Class<?> clazz = param.args[0].getClass(); if (PrivacyManager.getTransient(clazz.getName(), null) == null) { PrivacyManager.setTransient(clazz.getName(), Boolean.toString(true)); XPrivacy.hookAll(XConnectionCallbacks.getInstances(param.args[0]), clazz.getClassLoader(), getSecret(), true); } } break; } } @Override protected void after(XParam param) throws Throwable { // Do nothing } public static Object getPendingResult(ClassLoader loader) throws Throwable { InvocationHandler ih = new PendingResultHandler(loader); Class<?> pr = Class.forName("com.google.android.gms.common.api.PendingResult", false, loader); return Proxy.newProxyInstance(loader, new Class<?>[] { pr }, ih); } private static class PendingResultHandler implements InvocationHandler { private ClassLoader mLoader; private boolean mCancelled = false; public PendingResultHandler(ClassLoader loader) { mLoader = loader; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("await".equals(method.getName())) { // abstract R await() // abstract R await(long time, TimeUnit units) return getStatus(mLoader); } else if ("cancel".equals(method.getName())) { // abstract void cancel() mCancelled = true; return null; } else if ("isCanceled".equals(method.getName())) { // abstract boolean isCanceled() return mCancelled; } else if ("setResultCallback".equals(method.getName())) { // abstract void setResultCallback(ResultCallback<R> callback, // long time, TimeUnit units) // abstract void setResultCallback(ResultCallback<R> callback) Object callback = args[0]; if (callback != null) { // abstract void onResult(R result) Class<?> cStatus = Class.forName("com.google.android.gms.common.api.Status", false, mLoader); callback.getClass().getMethod("onResult", cStatus).invoke(callback, getStatus(mLoader)); } return null; } return null; } } private static Object getStatus(ClassLoader loader) throws Throwable { // public com.google.android.gms.common.api.Status(int) Class<?> cStatus = Class.forName("com.google.android.gms.common.api.Status", false, loader); Constructor<?> iStatus = cStatus.getConstructor(int.class); Object status = iStatus.newInstance(0); // SUCCESS return status; } }