Merge "Fix security issues with LocationManager where apps with coarse permissions can get location updates too frequently by repeatedly calling getLastKnownLocation or by registering/unregistering location updates frequently." into jb-mr2-dev
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 1e1cf5a..b47e8a0 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -116,6 +116,8 @@
private static final int MSG_LOCATION_CHANGED = 1;
+ private static final long NANOS_PER_MILLI = 1000000L;
+
// 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
@@ -179,6 +181,11 @@
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+ // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
+ // locations stored here are not fudged for coarse permissions.
+ private final HashMap<String, Location> mLastLocationCoarseInterval =
+ new HashMap<String, Location>();
+
// all providers that operate over proxy, for authorizing incoming location
private final ArrayList<LocationProviderProxy> mProxyProviders =
new ArrayList<LocationProviderProxy>();
@@ -423,6 +430,7 @@
mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
synchronized (mLock) {
mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
for (LocationProviderInterface p : mProviders) {
updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
}
@@ -1407,7 +1415,14 @@
if (!isAllowedByUserSettingsLocked(name, uid)) return null;
- Location location = mLastLocation.get(name);
+ Location location;
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ // Make sure that an app with coarse permissions can't get frequent location
+ // updates by calling LocationManager.getLastKnownLocation repeatedly.
+ location = mLastLocationCoarseInterval.get(name);
+ } else {
+ location = mLastLocation.get(name);
+ }
if (location == null) {
return null;
}
@@ -1673,7 +1688,8 @@
// Check whether sufficient time has passed
long minTime = record.mRequest.getFastestInterval();
- long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
+ long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos())
+ / NANOS_PER_MILLI;
if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
@@ -1726,13 +1742,30 @@
}
lastLocation.set(location);
+ // Update last known coarse interval location if enough time has passed.
+ Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
+ if (lastLocationCoarseInterval == null) {
+ lastLocationCoarseInterval = new Location(location);
+ mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
+ }
+ long timeDiffNanos = location.getElapsedRealtimeNanos()
+ - lastLocationCoarseInterval.getElapsedRealtimeNanos();
+ if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
+ lastLocationCoarseInterval.set(location);
+ }
+ // Don't ever return a coarse location that is more recent than the allowed update
+ // interval (i.e. don't allow an app to keep registering and unregistering for
+ // location updates to overcome the minimum interval).
+ noGPSLocation =
+ lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+
// Skip if there are no UpdateRecords for this provider.
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null || records.size() == 0) return;
// Fetch coarse location
Location coarseLocation = null;
- if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
+ if (noGPSLocation != null) {
coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
}
@@ -2021,6 +2054,7 @@
addProviderLocked(provider);
mMockProviders.put(name, provider);
mLastLocation.put(name, null);
+ mLastLocationCoarseInterval.put(name, null);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
@@ -2043,6 +2077,7 @@
addProviderLocked(realProvider);
}
mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
updateProvidersLocked();
Binder.restoreCallingIdentity(identity);
}
@@ -2174,6 +2209,13 @@
pw.println(" " + provider + ": " + location);
}
+ pw.println(" Last Known Locations Coarse Intervals:");
+ for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) {
+ String provider = entry.getKey();
+ Location location = entry.getValue();
+ pw.println(" " + provider + ": " + location);
+ }
+
mGeofenceManager.dump(pw);
if (mEnabledProviders.size() > 0) {