Skip to content
This repository has been archived by the owner on Sep 6, 2019. It is now read-only.

Commit

Permalink
Fixed restricting locations for some Android versions
Browse files Browse the repository at this point in the history
Fixes #1102
  • Loading branch information
M66B committed Jan 13, 2014
1 parent 4836692 commit cb1c2ae
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 100 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Test and beta releases will have experimental functions enabled by default.

**Next release**

* Fixed restricting locations for some Android versions ([issue](https://github.com/M66B/XPrivacy/issues/1102))
* Fixed removing location and phone state listener
* Fixed fake value for sim operator name

Expand Down
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ For interested developers:
* Secure usage data
* Manage packages for services, providers, etc
* Close app view when packages removed
* Hook location intents

* Split settings file?
* Remove indeterminate progress indicator?
13 changes: 0 additions & 13 deletions src/android/location/ILocationListener.java

This file was deleted.

4 changes: 2 additions & 2 deletions src/biz/bokhorst/xprivacy/PrivacyManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,10 @@ public static boolean getRestricted(final XHook hook, Context context, int uid,

if (usage)
if (methodName == null || methodName.equals("")) {
Util.log(hook, Log.WARN, "method empty");
Util.log(hook, Log.WARN, "Method empty");
Util.logStack(hook);
} else if (getMethods(restrictionName).indexOf(new MethodDescription(restrictionName, methodName)) < 0)
Util.log(hook, Log.WARN, "unknown method=" + methodName);
Util.log(hook, Log.WARN, "Unknown method=" + methodName);

// Check cache
String keyCache = String.format("%d.%s.%s", uid, restrictionName, methodName);
Expand Down
2 changes: 1 addition & 1 deletion src/biz/bokhorst/xprivacy/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static void bug(XHook hook, Throwable ex) {
}

public static void logStack(XHook hook) {
log(hook, Log.INFO, Log.getStackTraceString(new Exception("StackTrace")));
log(hook, Log.WARN, Log.getStackTraceString(new Exception("StackTrace")));
}

public static File getDataFile() {
Expand Down
155 changes: 71 additions & 84 deletions src/biz/bokhorst/xprivacy/XLocationManagerService.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package biz.bokhorst.xprivacy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import android.content.Context;
import android.location.ILocationListener;
import android.location.Location;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
import android.util.Log;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import static de.robv.android.xposed.XposedHelpers.findField;

public class XLocationManagerService extends XHook {
private Methods mMethod;
private static final Map<IBinder, XILocationListener> mListener = new WeakHashMap<IBinder, XILocationListener>();
private static boolean mHooked = false;
private static Map<IBinder, Integer> mListenerUid = new WeakHashMap<IBinder, Integer>();

private XLocationManagerService(Methods method, String restrictionName) {
super(restrictionName, method.name(), String.format("Srv.%s", method.name()));
Expand Down Expand Up @@ -68,19 +72,70 @@ public static List<XHook> getInstances() {
}

@Override
protected void before(MethodHookParam param) throws Throwable {
protected void before(final MethodHookParam param) throws Throwable {
if (mMethod == Methods.addGpsStatusListener) {
if (isRestricted(param))
param.setResult(false);
} else if (mMethod == Methods.requestGeofence) {
if (isRestricted(param))
param.setResult(null);
} else if (mMethod == Methods.removeUpdates) {
if (isRestricted(param))
removeLocationListener(param);
if (param.args.length > 0 && param.args[0] != null) {
IBinder binder = ((IInterface) param.args[0]).asBinder();
synchronized (mListenerUid) {
if (mListenerUid.containsKey(binder))
mListenerUid.remove(binder);
else
Util.log(this, Log.WARN, "Remove: listener not found");
}
}
} else if (mMethod == Methods.requestLocationUpdates) {
if (isRestricted(param))
replaceLocationListener(param);
if (param.args.length > 1 && param.args[1] != null) {
if (param.args[1].getClass().getName().startsWith("android.location.ILocationListener")) {
int uid = Binder.getCallingUid();
IBinder binder = ((IInterface) param.args[1]).asBinder();
synchronized (mListenerUid) {
mListenerUid.put(binder, uid);
}

if (!mHooked) {
hookOnLocationChanged(param);
mHooked = true;
}
}
} else if (param.args.length > 2 && param.args[2] != null) {
// TODO: hook intent
if (isRestricted(param))
param.setResult(null);
}
}
}

private void hookOnLocationChanged(final MethodHookParam param) {
try {
Method on = param.args[1].getClass().getDeclaredMethod("onLocationChanged", Location.class);
XposedBridge.hookMethod(on, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam onparam) throws Throwable {
int uid = 0;
IBinder binder = ((IInterface) onparam.thisObject).asBinder();
synchronized (mListenerUid) {
if (mListenerUid.containsKey(binder))
uid = mListenerUid.get(binder);
else
Util.log(XLocationManagerService.this, Log.WARN, "Get: listener not found");
}

if (uid > 0 && onparam.args.length > 0 && onparam.args[0] != null) {
Location location = (Location) onparam.args[0];
if (location != null && isRestricted(param, uid))
onparam.args[0] = PrivacyManager.getDefacedLocation(uid, location);
}
}
});
Util.log(this, Log.WARN, "Hooked " + on + " uid=" + Process.myUid());
} catch (Throwable ex) {
Util.bug(this, ex);
}
}

Expand All @@ -90,9 +145,8 @@ protected void after(MethodHookParam param) throws Throwable {
&& mMethod != Methods.removeUpdates && mMethod != Methods.requestLocationUpdates)
if (mMethod == Methods.getLastLocation) {
Location location = (Location) param.getResult();
if (location != null)
if (isRestricted(param))
param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location));
if (location != null && isRestricted(param))
param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location));
} else if (mMethod == Methods.isProviderEnabled) {
if (isRestricted(param))
param.setResult(false);
Expand All @@ -111,85 +165,18 @@ protected void after(MethodHookParam param) throws Throwable {

@Override
protected boolean isRestricted(MethodHookParam param) throws Throwable {
int uid = Binder.getCallingUid();
return isRestricted(param, uid);
}

private boolean isRestricted(MethodHookParam param, int uid) throws Throwable {
Context context = null;
try {
Field fieldContext = findField(param.thisObject.getClass(), "mContext");
context = (Context) fieldContext.get(param.thisObject);
} catch (Throwable ex) {
// Not all location managers do have a context
Util.bug(this, ex);
}
int uid = Binder.getCallingUid();
return getRestricted(context, uid, true);
}

private void replaceLocationListener(MethodHookParam param) throws Throwable {
if (param.args.length > 1 && param.args[1] != null)
if (param.args[1] instanceof ILocationListener) {
// Replace
ILocationListener listener = (ILocationListener) param.args[1];
XILocationListener xListener = new XILocationListener(listener);
synchronized (mListener) {
mListener.put(listener.asBinder(), xListener);
Util.log(this, Log.INFO, "Added class=" + listener.getClass().getName() + " method=" + param.method
+ " count=" + mListener.size() + " uid=" + Binder.getCallingUid());
}
param.args[1] = xListener;
} else
Util.log(this, Log.WARN, "Unexpected method=" + param.method + " uid=" + Binder.getCallingUid());
}

private void removeLocationListener(MethodHookParam param) {
if (param.args.length > 0 && param.args[0] != null)
if (param.args[0] instanceof ILocationListener) {
ILocationListener listener = (ILocationListener) param.args[0];
synchronized (mListener) {
XILocationListener xlistener = mListener.get(listener.asBinder());
if (xlistener == null)
Util.log(this, Log.WARN, "Unknown class=" + listener.getClass().getName() + " method="
+ param.method + " count=" + mListener.size() + " uid=" + Binder.getCallingUid());
else {
param.args[0] = xlistener;
mListener.remove(listener.asBinder());
Util.log(this, Log.INFO, "Removed class=" + listener.getClass().getName() + " method="
+ param.method + " count=" + mListener.size() + " uid=" + Binder.getCallingUid());
}
}
} else
Util.log(this, Log.WARN, "Unexpected method=" + param.method + " uid=" + Binder.getCallingUid());
}

private class XILocationListener implements ILocationListener {

private ILocationListener mLocationListener;

public XILocationListener(ILocationListener locationListener) {
mLocationListener = locationListener;
}

@Override
public void onLocationChanged(Location location) {
mLocationListener.onLocationChanged(location == null ? location : PrivacyManager.getDefacedLocation(
Binder.getCallingUid(), location));
}

@Override
public void onProviderDisabled(String provider) {
mLocationListener.onProviderDisabled(provider);
}

@Override
public void onProviderEnabled(String provider) {
mLocationListener.onProviderEnabled(provider);
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
mLocationListener.onStatusChanged(provider, status, extras);
}

@Override
public IBinder asBinder() {
return mLocationListener.asBinder();
}
}
}

0 comments on commit cb1c2ae

Please sign in to comment.