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 XFusedLocationApi extends XHook {
private Methods mMethod;
private String mClassName;
private static final Map<Object, Object> mMapProxy = new WeakHashMap<Object, Object>();
private XFusedLocationApi(Methods method, String restrictionName, String className) {
super(restrictionName, method.name(), "GMS5." + method.name());
mMethod = method;
mClassName = className;
}
public String getClassName() {
return mClassName;
}
// @formatter:off
// Location getLastLocation(GoogleApiClient client)
// abstract PendingResult<Status> removeLocationUpdates(GoogleApiClient client, LocationListener listener)
// abstract PendingResult<Status> removeLocationUpdates(GoogleApiClient client, PendingIntent callbackIntent)
// abstract PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener, Looper looper)
// abstract PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener)
// abstract PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent)
// https://developer.android.com/reference/com/google/android/gms/location/FusedLocationProviderApi.html
// @formatter:on
private enum Methods {
getLastLocation, removeLocationUpdates, requestLocationUpdates
};
public static List<XHook> getInstances(Object instance) {
String className = instance.getClass().getName();
Util.log(null, Log.INFO, "Hooking FusedLocationApi class=" + className + " uid=" + Binder.getCallingUid());
List<XHook> listHook = new ArrayList<XHook>();
listHook.add(new XFusedLocationApi(Methods.getLastLocation, PrivacyManager.cLocation, className));
listHook.add(new XFusedLocationApi(Methods.removeLocationUpdates, null, className));
listHook.add(new XFusedLocationApi(Methods.requestLocationUpdates, PrivacyManager.cLocation, className));
return listHook;
}
@Override
protected void before(XParam param) throws Throwable {
switch (mMethod) {
case getLastLocation:
// Do nothing
break;
case removeLocationUpdates:
if (param.args.length > 1)
if (param.args[1] instanceof PendingIntent) {
if (isRestricted(param, PrivacyManager.cLocation, "GMS5.requestLocationUpdates"))
param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader()));
} else
synchronized (mMapProxy) {
if (mMapProxy.containsKey(param.args[1]))
param.args[1] = mMapProxy.get(param.args[1]);
}
break;
case requestLocationUpdates:
if (param.args.length > 2)
if (isRestricted(param))
if (param.args[2] instanceof PendingIntent)
param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader()));
else if (param.thisObject != null && param.args[2] != 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[2]);
Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] { ll }, ih);
// Use proxy
synchronized (mMapProxy) {
mMapProxy.put(param.args[2], proxy);
}
param.args[2] = proxy;
}
break;
}
}
@Override
protected void after(XParam param) throws Throwable {
switch (mMethod) {
case getLastLocation:
Location location = (Location) param.getResult();
if (location != null && isRestricted(param))
param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location));
break;
case removeLocationUpdates:
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);
}
}
}