Handle tri-state location permission.
Use noteOp instead of checkOp as checkOp doesn't consider
background status.
Watch location permission changes caused by background status.
Restrict age of last location for foreground-only apps.
Bug: 80318398
Test: Manual
Change-Id: I3499d4150478b6f501966953f8e4a08169f3557c
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 00180a8..e341363 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9032,6 +9032,14 @@
"location_background_throttle_package_whitelist";
/**
+ * Maximum staleness allowed for last location when returned to clients with only foreground
+ * location permissions.
+ * @hide
+ */
+ public static final String LOCATION_LAST_LOCATION_MAX_AGE_MILLIS =
+ "location_last_location_max_age_millis";
+
+ /**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
* @hide
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index b7460ac..07606ea 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -272,6 +272,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5ab932..de02e81 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -153,6 +153,10 @@
// default background throttling interval if not overriden in settings
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
+ // Default value for maximum age of last location returned to applications with foreground-only
+ // location permissions.
+ private static final long DEFAULT_LAST_LOCATION_MAX_AGE_MS = 20 * 60 * 1000;
+
// 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
@@ -241,6 +245,9 @@
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
+ // Maximum age of last location returned to clients with foreground-only location permissions.
+ private long mLastLocationMaxAgeMs;
+
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -308,7 +315,8 @@
}
}
};
- mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
+ mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
PackageManager.OnPermissionsChangedListener permissionListener
= new PackageManager.OnPermissionsChangedListener() {
@@ -341,6 +349,7 @@
updateUserProfiles(mCurrentUserId);
updateBackgroundThrottlingWhitelistLocked();
+ updateLastLocationMaxAgeLocked();
// prepare providers
loadProvidersLocked();
@@ -370,6 +379,18 @@
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateLastLocationMaxAgeLocked();
+ }
+ }
+ }
+ );
+ mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
true,
@@ -382,6 +403,7 @@
}
}
}, UserHandle.USER_ALL);
+
mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
@@ -841,6 +863,7 @@
for (String p : mUpdateRecords.keySet()) {
s.append(" ").append(mUpdateRecords.get(p).toString());
}
+ s.append(" monitoring location: ").append(mOpMonitoring);
s.append("]");
return s.toString();
}
@@ -913,7 +936,7 @@
}
} else {
if (!allowMonitoring
- || mAppOps.checkOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
+ || mAppOps.noteOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
!= AppOpsManager.MODE_ALLOWED) {
mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
return false;
@@ -1541,7 +1564,7 @@
boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
return false;
}
}
@@ -1857,6 +1880,14 @@
Arrays.asList(setting.split(",")));
}
+ private void updateLastLocationMaxAgeLocked() {
+ mLastLocationMaxAgeMs =
+ Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+ DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+ }
+
private boolean isThrottlingExemptLocked(Identity identity) {
if (identity.mUid == Process.SYSTEM_UID) {
return true;
@@ -2262,6 +2293,17 @@
if (location == null) {
return null;
}
+
+ // Don't return stale location to apps with foreground-only location permission.
+ String op = getResolutionPermission(allowedResolutionLevel);
+ long locationAgeMs = SystemClock.elapsedRealtime() -
+ location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+ if ((locationAgeMs > mLastLocationMaxAgeMs)
+ && (mAppOps.unsafeCheckOp(op, uid, packageName)
+ == AppOpsManager.MODE_FOREGROUND)) {
+ return null;
+ }
+
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(
Location.EXTRA_NO_GPS_LOCATION);