From 48c5eb018b1731bd47caccd43cda2cd36ebc271f Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Sun, 29 Jul 2012 21:23:08 -0700 Subject: [PATCH] DO NOT MERGE. Add package-name-prefix blacklist for location updates. The Settings.Secure value locationPackagePrefixBlacklist and locationPackagePrefixWhitelist contains comma seperated package-name prefixes. Location & geo-fence updates are silently dropped if the receiving package name has a prefix on the blacklist. Status updates are not affected. All other API's work as before. A content observer is used so run-time updates to the blacklist apply immediately. There is both a blacklist and a whitelist. The blacklist applies first, and then exemptions are allowed from the whitelist. In other words, if your package name prefix matches both the black AND white list, then it is allowed. Change-Id: I4ea2ad56fa6bd75d32151bc250ac25c26a5777c4 --- core/java/android/app/ContextImpl.java | 6 +- .../android/location/ILocationManager.aidl | 12 +- .../android/location/LocationManager.java | 20 +- .../server/LocationManagerService.java | 191 +++++++++++++++--- 4 files changed, 190 insertions(+), 39 deletions(-) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b902550d1455c..411f6d00b0bfe 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -361,10 +361,10 @@ class ContextImpl extends Context { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); }}); - registerService(LOCATION_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(LOCATION_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(LOCATION_SERVICE); - return new LocationManager(ILocationManager.Stub.asInterface(b)); + return new LocationManager(ctx, ILocationManager.Stub.asInterface(b)); }}); registerService(NETWORK_POLICY_SERVICE, new ServiceFetcher() { diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 2255bf2ecaaad..38a29d3a358ef 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -39,11 +39,11 @@ interface ILocationManager boolean providerMeetsCriteria(String provider, in Criteria criteria); void requestLocationUpdates(String provider, in Criteria criteria, long minTime, float minDistance, - boolean singleShot, in ILocationListener listener); + boolean singleShot, in ILocationListener listener, String packageName); void requestLocationUpdatesPI(String provider, in Criteria criteria, long minTime, float minDistance, - boolean singleShot, in PendingIntent intent); - void removeUpdates(in ILocationListener listener); - void removeUpdatesPI(in PendingIntent intent); + boolean singleShot, in PendingIntent intent, String packageName); + void removeUpdates(in ILocationListener listener, String packageName); + void removeUpdatesPI(in PendingIntent intent, String packageName); boolean addGpsStatusListener(IGpsStatusListener listener); void removeGpsStatusListener(IGpsStatusListener listener); @@ -54,13 +54,13 @@ interface ILocationManager boolean sendExtraCommand(String provider, String command, inout Bundle extras); void addProximityAlert(double latitude, double longitude, float distance, - long expiration, in PendingIntent intent); + long expiration, in PendingIntent intent, String packageName); void removeProximityAlert(in PendingIntent intent); Bundle getProviderInfo(String provider); boolean isProviderEnabled(String provider); - Location getLastKnownLocation(String provider); + Location getLastKnownLocation(String provider, String packageName); // Used by location providers to tell the location manager when it has a new location. // Passive is true if the location is coming from the passive provider, in which case diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 12995740cb731..5c256a31609fc 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -17,6 +17,7 @@ package android.location; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Looper; @@ -160,6 +161,8 @@ public class LocationManager { */ public static final String EXTRA_GPS_ENABLED = "enabled"; + private final Context mContext; + // Map from LocationListeners to their associated ListenerTransport objects private HashMap mListeners = new HashMap(); @@ -260,8 +263,9 @@ public class LocationManager { * right way to create an instance of this class is using the * factory Context.getSystemService. */ - public LocationManager(ILocationManager service) { + public LocationManager(Context context, ILocationManager service) { mService = service; + mContext = context; } private LocationProvider createProvider(String name, Bundle info) { @@ -657,7 +661,8 @@ public class LocationManager { transport = new ListenerTransport(listener, looper); } mListeners.put(listener, transport); - mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport); + mService.requestLocationUpdates(provider, criteria, minTime, minDistance, + singleShot, transport, mContext.getPackageName()); } } catch (RemoteException ex) { Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex); @@ -837,7 +842,8 @@ public class LocationManager { } try { - mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent); + mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, + intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "requestLocationUpdates: RemoteException", ex); } @@ -1005,7 +1011,7 @@ public class LocationManager { try { ListenerTransport transport = mListeners.remove(listener); if (transport != null) { - mService.removeUpdates(transport); + mService.removeUpdates(transport, mContext.getPackageName()); } } catch (RemoteException ex) { Log.e(TAG, "removeUpdates: DeadObjectException", ex); @@ -1028,7 +1034,7 @@ public class LocationManager { Log.d(TAG, "removeUpdates: intent = " + intent); } try { - mService.removeUpdatesPI(intent); + mService.removeUpdatesPI(intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "removeUpdates: RemoteException", ex); } @@ -1087,7 +1093,7 @@ public class LocationManager { } try { mService.addProximityAlert(latitude, longitude, radius, - expiration, intent); + expiration, intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "addProximityAlert: RemoteException", ex); } @@ -1153,7 +1159,7 @@ public class LocationManager { throw new IllegalArgumentException("provider==null"); } try { - return mService.getLastKnownLocation(provider); + return mService.getLastKnownLocation(provider, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "getLastKnowLocation: RemoteException", ex); return null; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 2918dbcdb6358..8c1581cf12975 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -32,6 +32,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.content.res.Resources; +import android.database.ContentObserver; import android.database.Cursor; import android.location.Address; import android.location.Criteria; @@ -79,6 +80,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Observable; @@ -109,6 +112,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private static final String INSTALL_LOCATION_PROVIDER = android.Manifest.permission.INSTALL_LOCATION_PROVIDER; + private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist"; + private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist"; + // Location Providers may sometimes deliver location updates // slightly faster that requested - provide grace period so // we don't unnecessarily filter events that are otherwise on @@ -193,6 +199,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + // for prefix blacklist + private String[] mWhitelist = new String[0]; + private String[] mBlacklist = new String[0]; + // for Settings change notification private ContentQueryMap mSettings; @@ -205,20 +215,23 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final PendingIntent mPendingIntent; final Object mKey; final HashMap mUpdateRecords = new HashMap(); + final String mPackageName; int mPendingBroadcasts; String mRequiredPermissions; - Receiver(ILocationListener listener) { + Receiver(ILocationListener listener, String packageName) { mListener = listener; mPendingIntent = null; mKey = listener.asBinder(); + mPackageName = packageName; } - Receiver(PendingIntent intent) { + Receiver(PendingIntent intent, String packageName) { mPendingIntent = intent; mListener = null; mKey = intent; + mPackageName = packageName; } @Override @@ -601,6 +614,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Load providers loadProviders(); + loadBlacklist(); // Register for Network (Wifi or Mobile) updates IntentFilter intentFilter = new IntentFilter(); @@ -1110,11 +1124,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private Receiver getReceiver(ILocationListener listener) { + private Receiver getReceiver(ILocationListener listener, String packageName) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { - receiver = new Receiver(listener); + receiver = new Receiver(listener, packageName); mReceivers.put(binder, receiver); try { @@ -1129,10 +1143,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return receiver; } - private Receiver getReceiver(PendingIntent intent) { + private Receiver getReceiver(PendingIntent intent, String packageName) { Receiver receiver = mReceivers.get(intent); if (receiver == null) { - receiver = new Receiver(intent); + receiver = new Receiver(intent, packageName); mReceivers.put(intent, receiver); } return receiver; @@ -1157,7 +1171,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void requestLocationUpdates(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, ILocationListener listener) { + long minTime, float minDistance, boolean singleShot, ILocationListener listener, + String packageName) { + checkPackageName(Binder.getCallingUid(), packageName); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously // rather than only the best one? @@ -1170,7 +1186,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { synchronized (mLock) { requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(listener)); + getReceiver(listener, packageName)); } } catch (SecurityException se) { throw se; @@ -1194,7 +1210,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void requestLocationUpdatesPI(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, PendingIntent intent) { + long minTime, float minDistance, boolean singleShot, PendingIntent intent, + String packageName) { + checkPackageName(Binder.getCallingUid(), packageName); validatePendingIntent(intent); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously @@ -1208,7 +1226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { synchronized (mLock) { requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(intent)); + getReceiver(intent, packageName)); } } catch (SecurityException se) { throw se; @@ -1270,10 +1288,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void removeUpdates(ILocationListener listener) { + public void removeUpdates(ILocationListener listener, String packageName) { try { synchronized (mLock) { - removeUpdatesLocked(getReceiver(listener)); + removeUpdatesLocked(getReceiver(listener, packageName)); } } catch (SecurityException se) { throw se; @@ -1284,10 +1302,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void removeUpdatesPI(PendingIntent intent) { + public void removeUpdatesPI(PendingIntent intent, String packageName) { try { synchronized (mLock) { - removeUpdatesLocked(getReceiver(intent)); + removeUpdatesLocked(getReceiver(intent, packageName)); } } catch (SecurityException se) { throw se; @@ -1446,15 +1464,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final long mExpiration; final PendingIntent mIntent; final Location mLocation; + final String mPackageName; public ProximityAlert(int uid, double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { mUid = uid; mLatitude = latitude; mLongitude = longitude; mRadius = radius; mExpiration = expiration; mIntent = intent; + mPackageName = packageName; mLocation = new Location(""); mLocation.setLatitude(latitude); @@ -1522,6 +1542,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run PendingIntent intent = alert.getIntent(); long expiration = alert.getExpiration(); + if (inBlacklist(alert.mPackageName)) { + continue; + } + if ((expiration == -1) || (now <= expiration)) { boolean entered = mProximitiesEntered.contains(alert); boolean inProximity = @@ -1632,11 +1656,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void addProximityAlert(double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { validatePendingIntent(intent); try { synchronized (mLock) { - addProximityAlertLocked(latitude, longitude, radius, expiration, intent); + addProximityAlertLocked(latitude, longitude, radius, expiration, intent, + packageName); } } catch (SecurityException se) { throw se; @@ -1648,7 +1673,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private void addProximityAlertLocked(double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { if (LOCAL_LOGV) { Slog.v(TAG, "addProximityAlert: latitude = " + latitude + ", longitude = " + longitude + @@ -1656,6 +1681,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run ", intent = " + intent); } + checkPackageName(Binder.getCallingUid(), packageName); + // Require ability to access all providers for now if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) || !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) { @@ -1666,12 +1693,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run expiration += System.currentTimeMillis(); } ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(), - latitude, longitude, radius, expiration, intent); + latitude, longitude, radius, expiration, intent, packageName); mProximityAlerts.put(intent, alert); if (mProximityReceiver == null) { mProximityListener = new ProximityListener(); - mProximityReceiver = new Receiver(mProximityListener); + mProximityReceiver = new Receiver(mProximityListener, packageName); for (int i = mProviders.size() - 1; i >= 0; i--) { LocationProviderInterface provider = mProviders.get(i); @@ -1787,13 +1814,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return isAllowedBySettingsLocked(provider); } - public Location getLastKnownLocation(String provider) { + public Location getLastKnownLocation(String provider, String packageName) { if (LOCAL_LOGV) { Slog.v(TAG, "getLastKnownLocation: " + provider); } try { synchronized (mLock) { - return _getLastKnownLocationLocked(provider); + return _getLastKnownLocationLocked(provider, packageName); } } catch (SecurityException se) { throw se; @@ -1803,8 +1830,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private Location _getLastKnownLocationLocked(String provider) { + private Location _getLastKnownLocationLocked(String provider, String packageName) { checkPermissionsSafe(provider, null); + checkPackageName(Binder.getCallingUid(), packageName); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { @@ -1815,6 +1843,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } + if (inBlacklist(packageName)) { + return null; + } + return mLastKnownLocation.get(provider); } @@ -1877,6 +1909,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Receiver receiver = r.mReceiver; boolean receiverDead = false; + if (inBlacklist(receiver.mPackageName)) { + continue; + } + Location lastLoc = r.mLastFixBroadcast; if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) { if (lastLoc == null) { @@ -2315,6 +2351,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + public class BlacklistObserver extends ContentObserver { + public BlacklistObserver(Handler handler) { + super(handler); + } + @Override + public void onChange(boolean selfChange) { + reloadBlacklist(); + } + } + + private void loadBlacklist() { + // Register for changes + BlacklistObserver observer = new BlacklistObserver(mLocationHandler); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + BLACKLIST_CONFIG_NAME), false, observer); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + WHITELIST_CONFIG_NAME), false, observer); + reloadBlacklist(); + } + + private void reloadBlacklist() { + String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME); + String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME); + synchronized (mLock) { + mWhitelist = whitelist; + Slog.i(TAG, "whitelist: " + arrayToString(mWhitelist)); + mBlacklist = blacklist; + Slog.i(TAG, "blacklist: " + arrayToString(mBlacklist)); + } + } + + private static String arrayToString(String[] array) { + StringBuilder s = new StringBuilder(); + s.append('['); + boolean first = true; + for (String a : array) { + if (!first) s.append(','); + first = false; + s.append(a); + } + s.append(']'); + return s.toString(); + } + + private String[] getStringArray(String key) { + String flatString = Settings.Secure.getString(mContext.getContentResolver(), key); + if (flatString == null) { + return new String[0]; + } + String[] splitStrings = flatString.split(","); + ArrayList result = new ArrayList(); + for (String pkg : splitStrings) { + pkg = pkg.trim(); + if (pkg.isEmpty()) { + continue; + } + result.add(pkg); + } + return result.toArray(new String[result.size()]); + } + + /** + * Return true if in blacklist, and not in whitelist. + */ + private boolean inBlacklist(String packageName) { + synchronized (mLock) { + for (String black : mBlacklist) { + if (packageName.startsWith(black)) { + if (inWhitelist(packageName)) { + continue; + } else { + if (LOCAL_LOGV) Log.d(TAG, "dropping location (blacklisted): " + + packageName + " matches " + black); + return true; + } + } + } + } + return false; + } + + /** + * Return true if any of packages are in whitelist + */ + private boolean inWhitelist(String pkg) { + synchronized (mLock) { + for (String white : mWhitelist) { + if (pkg.startsWith(white)) return true; + } + } + return false; + } + + private void checkPackageName(int uid, String packageName) { + if (packageName == null) { + throw new SecurityException("packageName cannot be null"); + } + String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages == null) { + throw new SecurityException("invalid UID " + uid); + } + for (String pkg : packages) { + if (packageName.equals(pkg)) return; + } + throw new SecurityException("invalid package name"); + } + private void log(String log) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.d(TAG, log); @@ -2346,6 +2489,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run j.getValue().dump(pw, " "); } } + pw.println(" Package blacklist:" + arrayToString(mBlacklist)); + pw.println(" Package whitelist:" + arrayToString(mWhitelist)); pw.println(" Records by Provider:"); for (Map.Entry> i : mRecordsByProvider.entrySet()) {