package biz.bokhorst.xprivacy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import android.app.PendingIntent; import android.location.Location; import android.os.Binder; import android.util.Log; public class XLocationClient extends XHook { private Methods mMethod; private static final Map<Object, Object> mMapProxy = new WeakHashMap<Object, Object>(); private XLocationClient(Methods method, String restrictionName) { super(restrictionName, method.name(), String.format("GMS.%s", method.name())); mMethod = method; } public String getClassName() { return "com.google.android.gms.location.LocationClient"; } // @formatter:off // void addGeofences(List<Geofence> geofences, PendingIntent pendingIntent, LocationClient.OnAddGeofencesResultListener listener) // Location getLastLocation() // void removeGeofences(List<String> geofenceRequestIds, LocationClient.OnRemoveGeofencesResultListener listener) // void removeGeofences(PendingIntent pendingIntent, LocationClient.OnRemoveGeofencesResultListener listener) // void removeLocationUpdates(LocationListener listener) // void removeLocationUpdates(PendingIntent callbackIntent) // void requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) // void requestLocationUpdates(LocationRequest request, LocationListener listener) // void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) // https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html // @formatter:on private enum Methods { addGeofences, getLastLocation, removeGeofences, removeLocationUpdates, requestLocationUpdates }; public static List<XHook> getInstances() { Util.log(null, Log.WARN, "Hooking LocationClient uid=" + Binder.getCallingUid()); List<XHook> listHook = new ArrayList<XHook>(); listHook.add(new XLocationClient(Methods.addGeofences, PrivacyManager.cLocation)); listHook.add(new XLocationClient(Methods.getLastLocation, PrivacyManager.cLocation)); listHook.add(new XLocationClient(Methods.removeGeofences, null)); listHook.add(new XLocationClient(Methods.removeLocationUpdates, null)); listHook.add(new XLocationClient(Methods.requestLocationUpdates, PrivacyManager.cLocation)); return listHook; } @Override protected void before(XParam param) throws Throwable { switch (mMethod) { case addGeofences: if (isRestricted(param)) param.setResult(null); break; case getLastLocation: // Do nothing break; case removeGeofences: if (isRestricted(param, PrivacyManager.cLocation, "GMS.addGeofences")) param.setResult(null); break; case removeLocationUpdates: if (param.args.length > 0) if (param.args[0] instanceof PendingIntent) { if (isRestricted(param, PrivacyManager.cLocation, "GMS.requestLocationUpdates")) param.setResult(null); } else if (param.args[0] != null) synchronized (mMapProxy) { if (mMapProxy.containsKey(param.args[0])) param.args[0] = mMapProxy.get(param.args[0]); } break; case requestLocationUpdates: if (param.args.length > 1) if (isRestricted(param)) if (param.args[1] instanceof PendingIntent) param.setResult(null); else if (param.thisObject != null && param.args[1] != null) { // Create proxy ClassLoader cl = param.thisObject.getClass().getClassLoader(); Class<?> ll = Class.forName("com.google.android.gms.location.LocationListener", false, cl); InvocationHandler ih = new OnLocationChangedHandler(Binder.getCallingUid(), param.args[1]); Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] { ll }, ih); // Use proxy synchronized (mMapProxy) { mMapProxy.put(param.args[1], proxy); } param.args[1] = proxy; } break; } } @Override protected void after(XParam param) throws Throwable { switch (mMethod) { case addGeofences: case removeGeofences: // Do nothing break; case getLastLocation: Location location = (Location) param.getResult(); if (location != null) if (isRestricted(param)) param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); break; case removeLocationUpdates: // Do nothing break; case requestLocationUpdates: // Do nothing break; } } private class OnLocationChangedHandler implements InvocationHandler { private int mUid; private Object mTarget; public OnLocationChangedHandler(int uid, Object target) { mUid = uid; mTarget = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("onLocationChanged".equals(method.getName())) args[0] = PrivacyManager.getDefacedLocation(mUid, (Location) args[0]); return method.invoke(mTarget, args); } } }