Add background location throttling to LocationManagerService

Test: manual tests
Change-Id: I27f060d2f5338a8750dcbe5cbe4cfadb4edb0464
This commit is contained in:
Soonil Nagarkar
2017-01-18 12:18:49 -08:00
parent 41ec732bb1
commit 7decfb6b76
2 changed files with 152 additions and 64 deletions

View File

@@ -7489,6 +7489,14 @@ public final class Settings {
public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED =
"hdmi_control_auto_device_off_enabled";
/**
* The interval in milliseconds at which location requests will be throttled when they are
* coming from the background.
* @hide
*/
public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
"location_background_throttle_interval_ms";
/**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)

View File

@@ -16,6 +16,7 @@
package com.android.server;
import android.app.ActivityManager;
import android.content.pm.PackageManagerInternal;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
@@ -138,6 +139,9 @@ public class LocationManagerService extends ILocationManager.Stub {
// The maximum interval a location request can have and still be considered "high power".
private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
// default background throttling interval if not overriden in settings
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 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
@@ -157,6 +161,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private GeofenceManager mGeofenceManager;
private PackageManager mPackageManager;
private PowerManager mPowerManager;
private ActivityManager mActivityManager;
private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
private IGnssStatusProvider mGnssStatusProvider;
@@ -171,47 +176,47 @@ public class LocationManagerService extends ILocationManager.Stub {
// --- fields below are protected by mLock ---
// Set of providers that are explicitly enabled
// Only used by passive, fused & test. Network & GPS are controlled separately, and not listed.
private final Set<String> mEnabledProviders = new HashSet<String>();
private final Set<String> mEnabledProviders = new HashSet<>();
// Set of providers that are explicitly disabled
private final Set<String> mDisabledProviders = new HashSet<String>();
private final Set<String> mDisabledProviders = new HashSet<>();
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
new HashMap<String, MockProvider>();
new HashMap<>();
// all receivers
private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
private final ArrayList<LocationProviderInterface> mProviders =
new ArrayList<LocationProviderInterface>();
new ArrayList<>();
// real providers, saved here when mocked out
private final HashMap<String, LocationProviderInterface> mRealProviders =
new HashMap<String, LocationProviderInterface>();
new HashMap<>();
// mapping from provider name to provider
private final HashMap<String, LocationProviderInterface> mProvidersByName =
new HashMap<String, LocationProviderInterface>();
new HashMap<>();
// mapping from provider name to all its UpdateRecords
private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
new HashMap<String, ArrayList<UpdateRecord>>();
new HashMap<>();
private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
private final HashMap<String, Location> mLastLocation = new HashMap<>();
// 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>();
new HashMap<>();
// all providers that operate over proxy, for authorizing incoming location
private final ArrayList<LocationProviderProxy> mProxyProviders =
new ArrayList<LocationProviderProxy>();
new ArrayList<>();
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -252,6 +257,10 @@ public class LocationManagerService extends ILocationManager.Stub {
// fetch power manager
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
// fetch activity manager
mActivityManager
= (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
// prepare worker thread
mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
@@ -286,6 +295,40 @@ public class LocationManagerService extends ILocationManager.Stub {
};
mPackageManager.addOnPermissionsChangeListener(permissionListener);
// listen for background/foreground changes
ActivityManager.OnUidImportanceListener uidImportanceListener
= new ActivityManager.OnUidImportanceListener() {
@Override
public void onUidImportance(int uid, int importance) {
boolean foreground = isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
synchronized (mLock) {
for (Map.Entry<String, ArrayList<UpdateRecord>> entry
: mRecordsByProvider.entrySet()) {
String provider = entry.getKey();
for (UpdateRecord record : entry.getValue()) {
if (record.mReceiver.mUid == uid
&& record.mIsForegroundUid != foreground) {
if (D) Log.d(TAG, "request from uid " + uid + " is now "
+ (foreground ? "foreground" : "background)"));
record.mIsForegroundUid = foreground;
if (!isThrottlingExemptLocked(record.mReceiver)) {
affectedProviders.add(provider);
}
}
}
}
for (String provider : affectedProviders) {
applyRequirementsLocked(provider);
}
}
}
};
mActivityManager.addOnUidImportanceListener(uidImportanceListener,
ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
updateUserProfiles(mCurrentUserId);
@@ -305,6 +348,17 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
true,
new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
updateProvidersLocked();
}
}
}, UserHandle.USER_ALL);
mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
@@ -334,6 +388,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
}
private static boolean isImportanceForeground(int importance) {
return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
}
/**
* Provides a way for components held by the {@link LocationManagerService} to clean-up
* gracefully on system's shutdown.
@@ -483,7 +541,7 @@ public class LocationManagerService extends ILocationManager.Stub {
that matches the signature of at least one package on this list.
*/
Resources resources = mContext.getResources();
ArrayList<String> providerPackageNames = new ArrayList<String>();
ArrayList<String> providerPackageNames = new ArrayList<>();
String[] pkgs = resources.getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames);
if (D) Log.d(TAG, "certificates for location providers pulled from: " +
@@ -651,7 +709,7 @@ public class LocationManagerService extends ILocationManager.Stub {
final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
final Object mKey;
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<>();
// True if app ops has started monitoring this receiver for locations.
boolean mOpMonitoring;
@@ -691,10 +749,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean equals(Object otherObj) {
if (otherObj instanceof Receiver) {
return mKey.equals(((Receiver)otherObj).mKey);
}
return false;
return (otherObj instanceof Receiver) && mKey.equals(((Receiver) otherObj).mKey);
}
@Override
@@ -1011,13 +1066,25 @@ public class LocationManagerService extends ILocationManager.Stub {
mProvidersByName.remove(provider.getName());
}
private boolean isOverlayProviderPackageLocked(String packageName) {
for (LocationProviderInterface provider : mProviders) {
if (provider instanceof LocationProviderProxy) {
if (packageName.equals(
((LocationProviderProxy) provider).getConnectedPackageName())) {
return true;
}
}
}
return false;
}
/**
* Returns "true" if access to the specified location provider is allowed by the current
* user's settings. Access to all location providers is forbidden to non-location-provider
* processes belonging to background users.
*
* @param provider the name of the location provider
* @return
*/
private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
if (mEnabledProviders.contains(provider)) {
@@ -1039,7 +1106,6 @@ public class LocationManagerService extends ILocationManager.Stub {
*
* @param provider the name of the location provider
* @param uid the requestor's UID
* @return
*/
private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
@@ -1197,11 +1263,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
if (getAllowedResolutionLevel(pid, uid) < allowedResolutionLevel) {
return false;
}
return true;
return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
}
boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
@@ -1212,11 +1274,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
if (getAllowedResolutionLevel(pid, uid) < allowedResolutionLevel) {
return false;
}
return true;
return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
}
/**
@@ -1228,7 +1286,7 @@ public class LocationManagerService extends ILocationManager.Stub {
public List<String> getAllProviders() {
ArrayList<String> out;
synchronized (mLock) {
out = new ArrayList<String>(mProviders.size());
out = new ArrayList<>(mProviders.size());
for (LocationProviderInterface provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
@@ -1251,11 +1309,11 @@ public class LocationManagerService extends ILocationManager.Stub {
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
ArrayList<String> out;
int uid = Binder.getCallingUid();;
int uid = Binder.getCallingUid();
long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
out = new ArrayList<String>(mProviders.size());
out = new ArrayList<>(mProviders.size());
for (LocationProviderInterface provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
@@ -1370,14 +1428,12 @@ public class LocationManagerService extends ILocationManager.Stub {
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
final int N = records.size();
for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
deadReceivers = new ArrayList<>();
}
deadReceivers.add(record.mReceiver);
}
@@ -1410,6 +1466,12 @@ public class LocationManagerService extends ILocationManager.Stub {
WorkSource worksource = new WorkSource();
ProviderRequest providerRequest = new ProviderRequest();
ContentResolver resolver = mContext.getContentResolver();
long backgroundThrottleInterval = Settings.Global.getLong(
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
if (records != null) {
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
@@ -1419,10 +1481,22 @@ public class LocationManagerService extends ILocationManager.Stub {
record.mReceiver.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
LocationRequest locationRequest = record.mRequest;
long interval = locationRequest.getInterval();
if (!isThrottlingExemptLocked(record.mReceiver)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
if (interval != locationRequest.getInterval()) {
locationRequest = new LocationRequest(locationRequest);
locationRequest.setInterval(interval);
}
}
providerRequest.locationRequests.add(locationRequest);
if (locationRequest.getInterval() < providerRequest.interval) {
if (interval < providerRequest.interval) {
providerRequest.reportLocation = true;
providerRequest.interval = locationRequest.getInterval();
providerRequest.interval = interval;
}
}
}
@@ -1468,10 +1542,15 @@ public class LocationManagerService extends ILocationManager.Stub {
p.setRequest(providerRequest, worksource);
}
private boolean isThrottlingExemptLocked(Receiver recevier) {
return isOverlayProviderPackageLocked(recevier.mPackageName);
}
private class UpdateRecord {
final String mProvider;
final LocationRequest mRequest;
final Receiver mReceiver;
boolean mIsForegroundUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
@@ -1482,10 +1561,12 @@ public class LocationManagerService extends ILocationManager.Stub {
mProvider = provider;
mRequest = request;
mReceiver = receiver;
mIsForegroundUid = isImportanceForeground(
mActivityManager.getPackageImportance(mReceiver.mPackageName));
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
records = new ArrayList<UpdateRecord>();
records = new ArrayList<>();
mRecordsByProvider.put(provider, records);
}
if (!records.contains(this)) {
@@ -1517,7 +1598,7 @@ public class LocationManagerService extends ILocationManager.Stub {
receiverRecords.remove(this.mProvider);
// and also remove the Receiver if it has no more update records
if (removeReceiver && receiverRecords.size() == 0) {
if (receiverRecords.size() == 0) {
removeUpdatesLocked(mReceiver);
}
}
@@ -1525,14 +1606,9 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("UpdateRecord[");
s.append(mProvider);
s.append(' ').append(mReceiver.mPackageName).append('(');
s.append(mReceiver.mUid).append(')');
s.append(' ').append(mRequest);
s.append(']');
return s.toString();
return "UpdateRecord[" + mProvider + " " + mReceiver.mPackageName
+ "(" + mReceiver.mUid + (mIsForegroundUid ? " foreground" : " background")
+ ")" + " " + mRequest + "]";
}
}
@@ -1681,14 +1757,17 @@ public class LocationManagerService extends ILocationManager.Stub {
throw new IllegalArgumentException("provider name must not be null");
}
if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ " " + name + " " + request + " from " + packageName + "(" + uid + ")");
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) {
throw new IllegalArgumentException("provider doesn't exist: " + name);
}
UpdateRecord record = new UpdateRecord(name, request, receiver);
if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ " " + name + " " + request + " from " + packageName + "(" + uid + " "
+ (record.mIsForegroundUid ? "foreground" : "background")
+ (isOverlayProviderPackageLocked(receiver.mPackageName) ? " [whitelisted]" : "") + ")");
UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
if (oldRecord != null) {
oldRecord.disposeLocked(false);
@@ -1743,7 +1822,7 @@ public class LocationManagerService extends ILocationManager.Stub {
receiver.updateMonitoring(false);
// Record which providers were associated with this listener
HashSet<String> providers = new HashSet<String>();
HashSet<String> providers = new HashSet<>();
HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
if (oldRecords != null) {
// Call dispose() on the obsolete update records.
@@ -2084,9 +2163,7 @@ public class LocationManagerService extends ILocationManager.Stub {
try {
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return false;
return isAllowedByUserSettingsLocked(provider, uid);
return p != null && isAllowedByUserSettingsLocked(provider, uid);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2197,11 +2274,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// Check whether the expiry date has passed
if (record.mRequest.getExpireAt() < now) {
return false;
}
return true;
return record.mRequest.getExpireAt() >= now;
}
private void handleLocationChangedLocked(Location location, boolean passive) {
@@ -2216,7 +2289,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// Update last known locations
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation = null;
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
lastLocation = new Location(provider);
@@ -2296,7 +2369,7 @@ public class LocationManagerService extends ILocationManager.Stub {
continue;
}
Location notifyLocation = null;
Location notifyLocation;
if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
notifyLocation = coarseLocation; // use coarse location
} else {
@@ -2333,14 +2406,14 @@ public class LocationManagerService extends ILocationManager.Stub {
// track expired records
if (r.mRequest.getNumUpdates() <= 0 || r.mRequest.getExpireAt() < now) {
if (deadUpdateRecords == null) {
deadUpdateRecords = new ArrayList<UpdateRecord>();
deadUpdateRecords = new ArrayList<>();
}
deadUpdateRecords.add(r);
}
// track dead receivers
if (receiverDead) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
deadReceivers = new ArrayList<>();
}
if (!deadReceivers.contains(receiver)) {
deadReceivers.add(receiver);
@@ -2417,7 +2490,7 @@ public class LocationManagerService extends ILocationManager.Stub {
for (Receiver receiver : mReceivers.values()) {
if (receiver.mPackageName.equals(packageName)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
deadReceivers = new ArrayList<>();
}
deadReceivers.add(receiver);
}
@@ -2694,6 +2767,13 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.println(" " + record);
}
}
pw.println(" Overlay Provider Packages:");
for (LocationProviderInterface provider : mProviders) {
if (provider instanceof LocationProviderProxy) {
pw.println(" " + provider.getName() + ": "
+ ((LocationProviderProxy) provider).getConnectedPackageName());
}
}
pw.println(" Historical Records by Provider:");
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
: mRequestStatistics.statistics.entrySet()) {