diff options
5 files changed, 194 insertions, 14 deletions
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index a2ce606de874..f663e0a04085 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 25da208e2ba0..bef363b75b8b 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 bb005d9ad2c6..8a564f7104e9 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 @@ -965,7 +968,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs final int uid = Binder.getCallingUid(); Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName); - // so wakelock calls will succeed (not totally sure this is still needed) + // providers may use public location API's, need to clear identity long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -1015,7 +1018,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs final int uid = Binder.getCallingUid(); Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName); - // so wakelock calls will succeed (not totally sure this is still needed) + // providers may use public location API's, need to clear identity long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -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), @@ -1097,7 +1107,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent); - mGeofenceManager.addFence(request, geofence, intent, Binder.getCallingUid(), packageName); + // geo-fence manager uses the public location API, need to clear identity + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + try { + mGeofenceManager.addFence(request, geofence, intent, uid, packageName); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override @@ -1108,7 +1125,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); - mGeofenceManager.removeFence(geofence, intent); + // geo-fence manager uses the public location API, need to clear identity + long identity = Binder.clearCallingIdentity(); + try { + mGeofenceManager.removeFence(geofence, intent); + } finally { + Binder.restoreCallingIdentity(identity); + } } @@ -1325,6 +1348,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 +1746,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<String, MockProvider> i : mMockProviders.entrySet()) { diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java index 338cd5d25b39..26d9c1555774 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<GeofenceState> mFences = new LinkedList<GeofenceState>(); - 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 000000000000..71fa9f98e31f --- /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<String> result = new ArrayList<String>(); + 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)); + } +} |