From 4035f5a7c191a68bc9a5912ce44c43c82e9e5dbf Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 17 Aug 2012 14:43:49 -0700 Subject: [PATCH] Port location blacklist code to MR1. I had to re-do this change for MR1 because LocationManagerService changed so much. Here is the original change description: 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. Bug: 6986553 Change-Id: I1e151e08bd7143e47db005bc3fe9795076398df7 --- .../android/location/ILocationManager.aidl | 2 +- .../android/location/LocationManager.java | 8 +- .../server/LocationManagerService.java | 28 +++- .../server/location/GeofenceManager.java | 14 +- .../server/location/LocationBlacklist.java | 135 ++++++++++++++++++ 5 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 services/java/com/android/server/location/LocationBlacklist.java diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index a2ce606de874b..f663e0a040855 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -45,7 +45,7 @@ interface ILocationManager in PendingIntent intent, String packageName); void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName); - Location getLastLocation(in LocationRequest request); + Location getLastLocation(in LocationRequest request, String packageName); boolean addGpsStatusListener(IGpsStatusListener listener); void removeGpsStatusListener(IGpsStatusListener listener); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 25da208e2ba06..bef363b75b8be 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1174,8 +1174,10 @@ public class LocationManager { * @throws SecurityException if no suitable permission is present */ public Location getLastLocation() { + String packageName = mContext.getPackageName(); + try { - return mService.getLastLocation(null); + return mService.getLastLocation(null, packageName); } catch (RemoteException e) { Log.e(TAG, "RemoteException", e); return null; @@ -1204,12 +1206,12 @@ public class LocationManager { @Deprecated public Location getLastKnownLocation(String provider) { checkProvider(provider); - + String packageName = mContext.getPackageName(); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); try { - return mService.getLastLocation(request); + return mService.getLastLocation(request, packageName); } catch (RemoteException e) { Log.e(TAG, "RemoteException", e); return null; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index bb005d9ad2c65..69ae8336417be 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -63,6 +63,7 @@ import com.android.internal.location.ProviderRequest; import com.android.server.location.GeocoderProxy; import com.android.server.location.GeofenceManager; import com.android.server.location.GpsLocationProvider; +import com.android.server.location.LocationBlacklist; import com.android.server.location.LocationFudger; import com.android.server.location.LocationProviderInterface; import com.android.server.location.LocationProviderProxy; @@ -132,8 +133,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs private IGpsStatusProvider mGpsStatusProvider; private INetInitiatedListener mNetInitiatedListener; private LocationWorkerHandler mLocationHandler; - // track the passive provider for some special cases - private PassiveProvider mPassiveProvider; + private PassiveProvider mPassiveProvider; // track passive provider for special cases + private LocationBlacklist mBlacklist; // --- fields below are protected by mWakeLock --- private int mPendingBroadcasts; @@ -208,7 +209,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs synchronized (mLock) { loadProvidersLocked(); } - mGeofenceManager = new GeofenceManager(mContext); + mBlacklist = new LocationBlacklist(mContext, mLocationHandler); + mBlacklist.init(); + mGeofenceManager = new GeofenceManager(mContext, mBlacklist); mLocationFudger = new LocationFudger(); // Register for Network (Wifi or Mobile) updates @@ -1063,10 +1066,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs } @Override - public Location getLastLocation(LocationRequest request) { + public Location getLastLocation(LocationRequest request, String packageName) { if (D) Log.d(TAG, "getLastLocation: " + request); if (request == null) request = DEFAULT_LOCATION_REQUEST; String perm = checkPermissionAndRequest(request); + checkPackageName(packageName); + + if (mBlacklist.isBlacklisted(packageName)) { + if (D) Log.d(TAG, "not returning last loc for blacklisted app: " + + packageName); + return null; + } synchronized (mLock) { // Figure out the provider. Either its explicitly request (deprecated API's), @@ -1325,6 +1335,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs for (UpdateRecord r : records) { Receiver receiver = r.mReceiver; boolean receiverDead = false; + + if (mBlacklist.isBlacklisted(receiver.mPackageName)) { + if (D) Log.d(TAG, "skipping loc update for blacklisted app: " + + receiver.mPackageName); + continue; + } + if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) { location = lastLocation; // use fine location } else { @@ -1716,8 +1733,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs for (String i : mDisabledProviders) { pw.println(" " + i); } - } + pw.append(" "); + mBlacklist.dump(pw); if (mMockProviders.size() > 0) { pw.println(" Mock Providers:"); for (Map.Entry i : mMockProviders.entrySet()) { diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java index 338cd5d25b392..26d9c15557745 100644 --- a/services/java/com/android/server/location/GeofenceManager.java +++ b/services/java/com/android/server/location/GeofenceManager.java @@ -34,9 +34,13 @@ import android.os.Bundle; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; +import android.util.Log; + +import com.android.server.LocationManagerService; public class GeofenceManager implements LocationListener, PendingIntent.OnFinished { private static final String TAG = "GeofenceManager"; + private static final boolean D = LocationManagerService.D; /** * Assume a maximum land speed, as a heuristic to throttle location updates. @@ -49,6 +53,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish private final LocationManager mLocationManager; private final PowerManager.WakeLock mWakeLock; private final Looper mLooper; // looper thread to take location updates on + private final LocationBlacklist mBlacklist; private Object mLock = new Object(); @@ -56,12 +61,13 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish private Location mLastLocation; private List mFences = new LinkedList(); - public GeofenceManager(Context context) { + public GeofenceManager(Context context, LocationBlacklist blacklist) { mContext = context; mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mLooper = Looper.myLooper(); + mBlacklist = blacklist; LocationRequest request = new LocationRequest() .setQuality(LocationRequest.POWER_NONE) @@ -145,6 +151,12 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish removeExpiredFencesLocked(); for (GeofenceState state : mFences) { + if (mBlacklist.isBlacklisted(state.mPackageName)) { + if (D) Log.d(TAG, "skipping geofence processing for blacklisted app: " + + state.mPackageName); + continue; + } + int event = state.processLocation(location); if ((event & GeofenceState.FLAG_ENTER) != 0) { enterIntents.add(state.mIntent); diff --git a/services/java/com/android/server/location/LocationBlacklist.java b/services/java/com/android/server/location/LocationBlacklist.java new file mode 100644 index 0000000000000..71fa9f98e31f4 --- /dev/null +++ b/services/java/com/android/server/location/LocationBlacklist.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.server.location; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; +import android.util.Log; +import android.util.Slog; + +import com.android.server.LocationManagerService; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Allows applications to be blacklisted from location updates at run-time. + * + * This is a silent blacklist. Applications can still call Location Manager + * API's, but they just won't receive any locations. + */ +public final class LocationBlacklist extends ContentObserver { + private static final String TAG = "LocationBlacklist"; + private static final boolean D = LocationManagerService.D; + private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist"; + private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist"; + + private final Context mContext; + private final Object mLock = new Object(); + + // all fields below synchronized on mLock + private String[] mWhitelist = new String[0]; + private String[] mBlacklist = new String[0]; + + public LocationBlacklist(Context context, Handler handler) { + super(handler); + mContext = context; + } + + public void init() { + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + BLACKLIST_CONFIG_NAME), false, this); +// mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( +// WHITELIST_CONFIG_NAME), false, this); + reloadBlacklist(); + } + + private void reloadBlacklist() { + String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME); + String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME); + synchronized (mLock) { + mWhitelist = whitelist; + Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist)); + mBlacklist = blacklist; + Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist)); + } + } + + /** + * Return true if in blacklist + * (package name matches blacklist, and does not match whitelist) + */ + public boolean isBlacklisted(String packageName) { + synchronized (mLock) { + for (String black : mBlacklist) { + if (packageName.startsWith(black)) { + if (inWhitelist(packageName)) { + continue; + } else { + if (D) 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; + } + + @Override + public void onChange(boolean selfChange) { + reloadBlacklist(); + } + + 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()]); + } + + public void dump(PrintWriter pw) { + pw.println("mWhitelist=" + Arrays.toString(mWhitelist) + " mBlacklist=" + + Arrays.toString(mBlacklist)); + } +}