diff --git a/api/system-current.txt b/api/system-current.txt index f2ec2970f9ced..8979471ab9a05 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4171,7 +4171,7 @@ package android.location { method @Deprecated @NonNull public String getProvider(); method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/api/test-current.txt b/api/test-current.txt index 576cbacb12902..ee5aa66550c26 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1717,7 +1717,7 @@ package android.location { } public final class LocationRequest implements android.os.Parcelable { - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index ff004094ec59d..f87988c1594c2 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -88,6 +88,16 @@ import java.util.function.Consumer; @RequiresFeature(PackageManager.FEATURE_LOCATION) public class LocationManager { + /** + * For apps targeting Android S and above, LocationRequest system APIs may not be used with + * PendingIntent location requests. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L; + /** * For apps targeting Android S and above, location clients may receive historical locations * (from before the present time) under some circumstances. diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 3bb4781f99ef2..e03643c4c6328 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -250,7 +250,7 @@ public final class LocationRequest implements Parcelable { boolean hiddenFromAppOps, boolean locationSettingsIgnored, boolean lowPower, - @Nullable WorkSource workSource) { + WorkSource workSource) { Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE); Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis); @@ -265,7 +265,7 @@ public final class LocationRequest implements Parcelable { mHideFromAppOps = hiddenFromAppOps; mLowPower = lowPower; mLocationSettingsIgnored = locationSettingsIgnored; - mWorkSource = workSource; + mWorkSource = Objects.requireNonNull(workSource); } /** @@ -645,12 +645,15 @@ public final class LocationRequest implements Parcelable { @SystemApi @Deprecated public void setWorkSource(@Nullable WorkSource workSource) { + if (workSource == null) { + workSource = new WorkSource(); + } mWorkSource = workSource; } /** - * Returns the work source used for power blame for this request. If null, the system is free to - * assign power blame as it deems most appropriate. + * Returns the work source used for power blame for this request. If empty, the system is free + * to assign power blame as it deems most appropriate. * * @return the work source used for power blame for this request * @@ -658,7 +661,7 @@ public final class LocationRequest implements Parcelable { */ @TestApi @SystemApi - public @Nullable WorkSource getWorkSource() { + public @NonNull WorkSource getWorkSource() { return mWorkSource; } @@ -1062,9 +1065,9 @@ public final class LocationRequest implements Parcelable { } /** - * Sets the work source to use for power blame for this location request. Defaults to null, - * which implies the system is free to assign power blame as it determines best for this - * request (which usually means blaming the owner of the location listener). + * Sets the work source to use for power blame for this location request. Defaults to an + * empty WorkSource, which implies the system is free to assign power blame as it determines + * best for this request (which usually means blaming the owner of the location listener). * *

Permissions enforcement occurs when resulting location request is actually used, not * when this method is invoked. @@ -1108,7 +1111,7 @@ public final class LocationRequest implements Parcelable { mHiddenFromAppOps, mLocationSettingsIgnored, mLowPower, - mWorkSource); + new WorkSource(mWorkSource)); } } } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 04847d5fec35c..b476e6de70556 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4111,7 +4111,7 @@ package android.location { method @Deprecated @NonNull public String getProvider(); method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java index 6f35c8ba1e0a9..ecdd429cd0c8f 100644 --- a/services/core/java/com/android/server/location/LocationFudger.java +++ b/services/core/java/com/android/server/location/LocationFudger.java @@ -111,7 +111,7 @@ public class LocationFudger { */ public Location createCoarse(Location fine) { synchronized (this) { - if (fine == mCachedFineLocation) { + if (fine == mCachedFineLocation || fine == mCachedCoarseLocation) { return mCachedCoarseLocation; } } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 227cdeefdc3b4..f72fee6891909 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -17,12 +17,14 @@ package com.android.server.location; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.app.compat.CompatChanges.isChangeEnabled; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.NETWORK_PROVIDER; +import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE; import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -75,7 +77,6 @@ import android.os.WorkSource.WorkChain; import android.stats.location.LocationStatsEnums; import android.util.IndentingPrintWriter; import android.util.Log; -import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ProviderProperties; @@ -570,7 +571,7 @@ public class LocationManagerService extends ILocationManager.Stub { new IllegalArgumentException()); } - request = validateLocationRequest(request); + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -592,7 +593,21 @@ public class LocationManagerService extends ILocationManager.Stub { // clients in the system process must have an attribution tag set Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); - request = validateLocationRequest(request); + // pending intents requests may not use system apis because we do not keep track if clients + // lose the relevant permissions, and thus should not get the benefit of those apis. its + // simplest to ensure these apis are simply never set for pending intent requests. the same + // does not apply for listener requests since those will have the process (including the + // listener) killed on permission removal + boolean usesSystemApi = request.isLowPower() + || request.isHiddenFromAppOps() + || request.isLocationSettingsIgnored() + || !request.getWorkSource().isEmpty(); + if (usesSystemApi + && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) { + throw new SecurityException("PendingIntent location requests may not use system APIs"); + } + + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -601,9 +616,9 @@ public class LocationManagerService extends ILocationManager.Stub { manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent); } - private LocationRequest validateLocationRequest(LocationRequest request) { - WorkSource workSource = request.getWorkSource(); - if (workSource != null && !workSource.isEmpty()) { + private LocationRequest validateLocationRequest(LocationRequest request, + CallerIdentity identity) { + if (!request.getWorkSource().isEmpty()) { mContext.enforceCallingOrSelfPermission( permission.UPDATE_DEVICE_STATS, "setting a work source requires " + permission.UPDATE_DEVICE_STATS); @@ -634,23 +649,25 @@ public class LocationManagerService extends ILocationManager.Stub { } } - if (request.getWorkSource() != null) { - if (request.getWorkSource().isEmpty()) { - sanitized.setWorkSource(null); - } else if (request.getWorkSource().getPackageName(0) == null) { - Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); - sanitized.setWorkSource(null); - } else { - List workChains = request.getWorkSource().getWorkChains(); - if (workChains != null && !workChains.isEmpty() && workChains.get( - 0).getAttributionTag() == null) { - Log.w(TAG, - "received (and ignoring) illegal worksource with no attribution tag"); - sanitized.setWorkSource(null); - } + WorkSource workSource = new WorkSource(request.getWorkSource()); + if (workSource.size() > 0 && workSource.getPackageName(0) == null) { + Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); + workSource.clear(); + } else { + List workChains = workSource.getWorkChains(); + if (workChains != null && !workChains.isEmpty() + && workChains.get(0).getAttributionTag() == null) { + Log.w(TAG, + "received (and ignoring) illegal worksource with no attribution tag"); + workSource.clear(); } } + if (workSource.isEmpty()) { + identity.addToWorkSource(workSource); + } + sanitized.setWorkSource(workSource); + return sanitized.build(); } @@ -684,15 +701,7 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = manager.getLastLocation(identity, permissionLevel, false); - - // lastly - note app ops - if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - identity)) { - return null; - } - - return location; + return manager.getLastLocation(identity, permissionLevel, false); } @Nullable @@ -710,7 +719,7 @@ public class LocationManagerService extends ILocationManager.Stub { // clients in the system process must have an attribution tag set Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null); - request = validateLocationRequest(request); + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -727,7 +736,6 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - // use fine permission level to avoid creating unnecessary coarse locations Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL, PERMISSION_FINE, false, Long.MAX_VALUE); if (location == null) { @@ -1116,9 +1124,8 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - ipw.print("Location Manager State:"); + ipw.println("Location Manager State:"); ipw.increaseIndent(); - ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); ipw.println("User Info:"); ipw.increaseIndent(); diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index c6a9a1f3a6b3a..ecf82e8f8bd6e 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -31,6 +31,7 @@ import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST; +import static com.android.internal.location.ProviderRequest.INTERVAL_DISABLED; import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -238,8 +239,7 @@ class LocationProviderManager extends protected abstract class Registration extends RemoteListenerRegistration { - @PermissionLevel protected final int mPermissionLevel; - private final WorkSource mWorkSource; + private final @PermissionLevel int mPermissionLevel; // we cache these values because checking/calculating on the fly is more expensive private boolean mPermitted; @@ -247,22 +247,17 @@ class LocationProviderManager extends private LocationRequest mProviderLocationRequest; private boolean mIsUsingHighPower; - @Nullable private Location mLastLocation = null; + private @Nullable Location mLastLocation = null; protected Registration(LocationRequest request, CallerIdentity identity, LocationTransport transport, @PermissionLevel int permissionLevel) { super(Objects.requireNonNull(request), identity, transport); Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); + Preconditions.checkArgument(!request.getWorkSource().isEmpty()); + mPermissionLevel = permissionLevel; - - if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) { - mWorkSource = request.getWorkSource(); - } else { - mWorkSource = identity.addToWorkSource(null); - } - - mProviderLocationRequest = super.getRequest(); + mProviderLocationRequest = request; } @GuardedBy("mLock") @@ -376,6 +371,10 @@ class LocationProviderManager extends return mLastLocation; } + public @PermissionLevel int getPermissionLevel() { + return mPermissionLevel; + } + public final boolean isForeground() { return mForeground; } @@ -389,10 +388,6 @@ class LocationProviderManager extends return LocationProviderManager.this; } - protected final WorkSource getWorkSource() { - return mWorkSource; - } - @GuardedBy("mLock") private void onHighPowerUsageChanged() { boolean isUsingHighPower = isUsingHighPower(); @@ -609,7 +604,7 @@ class LocationProviderManager extends mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class)) .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); mWakeLock.setReferenceCounted(true); - mWakeLock.setWorkSource(getWorkSource()); + mWakeLock.setWorkSource(request.getWorkSource()); } @Override @@ -628,7 +623,7 @@ class LocationProviderManager extends onAlarm(); } else if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this, - getWorkSource()); + getRequest().getWorkSource()); } // start listening for provider enabled/disabled events @@ -690,7 +685,7 @@ class LocationProviderManager extends if (maxLocationAgeMs > MIN_REQUEST_DELAY_MS) { Location lastLocation = getLastLocationUnsafe( getIdentity().getUserId(), - PERMISSION_FINE, // acceptLocationChange() handles coarsening this + getPermissionLevel(), getRequest().isLocationSettingsIgnored(), maxLocationAgeMs); if (lastLocation != null) { @@ -732,18 +727,8 @@ class LocationProviderManager extends return null; } - Location location; - switch (mPermissionLevel) { - case PERMISSION_FINE: - location = fineLocation; - break; - case PERMISSION_COARSE: - location = mLocationFudger.createCoarse(fineLocation); - break; - default: - // shouldn't be possible to have a client added without location permissions - throw new AssertionError(); - } + Location location = Objects.requireNonNull( + getPermittedLocation(fineLocation, getPermissionLevel())); Location lastDeliveredLocation = getLastDeliveredLocation(); if (lastDeliveredLocation != null) { @@ -765,7 +750,7 @@ class LocationProviderManager extends } // note app ops - if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()), getIdentity())) { if (D) { Log.w(TAG, "noteOp denied for " + getIdentity()); @@ -962,7 +947,7 @@ class LocationProviderManager extends } protected final class GetCurrentLocationListenerRegistration extends Registration implements - IBinder.DeathRecipient, ProviderEnabledListener, OnAlarmListener { + IBinder.DeathRecipient, OnAlarmListener { private volatile LocationTransport mTransport; @@ -974,11 +959,6 @@ class LocationProviderManager extends mTransport = transport; } - @GuardedBy("mLock") - void deliverLocation(@Nullable Location location) { - executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location)); - } - @Override protected void onListenerUnregister() { mTransport = null; @@ -1001,29 +981,13 @@ class LocationProviderManager extends onAlarm(); } else if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this, - getWorkSource()); - } - - // if this request is ignoring location settings, then we don't want to immediately fail - // it if the provider is disabled or becomes disabled. - if (!getRequest().isLocationSettingsIgnored()) { - // start listening for provider enabled/disabled events - addEnabledListener(this); - - // if the provider is currently disabled fail immediately - int userId = getIdentity().getUserId(); - if (!isEnabled(userId)) { - deliverLocation(null); - } + getRequest().getWorkSource()); } } @GuardedBy("mLock") @Override protected void onProviderListenerUnregister() { - // stop listening for provider enabled/disabled events - removeEnabledListener(this); - // remove alarm for expiration if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.cancel(this); @@ -1032,6 +996,38 @@ class LocationProviderManager extends ((IBinder) getKey()).unlinkToDeath(this, 0); } + @GuardedBy("mLock") + @Override + protected LocationListenerOperation onProviderListenerActive() { + Location lastLocation = getLastLocationUnsafe( + getIdentity().getUserId(), + getPermissionLevel(), + getRequest().isLocationSettingsIgnored(), + MAX_CURRENT_LOCATION_AGE_MS); + if (lastLocation != null) { + return acceptLocationChange(lastLocation); + } + + return null; + } + + @GuardedBy("mLock") + @Override + protected LocationListenerOperation onProviderListenerInactive() { + if (!getRequest().isLocationSettingsIgnored()) { + // if we go inactive for any reason, fail immediately + return acceptLocationChange(null); + } + + return null; + } + + void deliverNull() { + synchronized (mLock) { + executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(null)); + } + } + @Override public void onAlarm() { if (D) { @@ -1041,9 +1037,10 @@ class LocationProviderManager extends } synchronized (mLock) { - deliverLocation(null); // no need to remove alarm after it's fired mExpirationRealtimeMs = Long.MAX_VALUE; + + deliverNull(); } } @@ -1062,29 +1059,16 @@ class LocationProviderManager extends } // lastly - note app ops - Location location; - if (fineLocation == null) { - location = null; - } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()), getIdentity())) { if (D) { Log.w(TAG, "noteOp denied for " + getIdentity()); } - location = null; - } else { - switch (mPermissionLevel) { - case PERMISSION_FINE: - location = fineLocation; - break; - case PERMISSION_COARSE: - location = mLocationFudger.createCoarse(fineLocation); - break; - default: - // shouldn't be possible to have a client added without location permissions - throw new AssertionError(); - } + fineLocation = null; } + Location location = getPermittedLocation(fineLocation, getPermissionLevel()); + return new LocationListenerOperation() { @Override public Location getLocation() { @@ -1119,22 +1103,6 @@ class LocationProviderManager extends }; } - @Override - public void onProviderEnabledChanged(String provider, int userId, boolean enabled) { - Preconditions.checkState(mName.equals(provider)); - - if (userId != getIdentity().getUserId()) { - return; - } - - // if the provider is disabled we give up on current location immediately - if (!getRequest().isLocationSettingsIgnored() && !enabled) { - synchronized (mLock) { - deliverLocation(null); - } - } - } - @Override public void binderDied() { try { @@ -1449,24 +1417,34 @@ class LocationProviderManager extends } } - Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel, - ignoreLocationSettings, Long.MAX_VALUE); + // lastly - note app ops + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), + identity)) { + return null; + } - // we don't note op here because we don't know what the client intends to do with the - // location, the client is responsible for noting if necessary + Location location = getPermittedLocation( + getLastLocationUnsafe( + identity.getUserId(), + permissionLevel, + ignoreLocationSettings, + Long.MAX_VALUE), + permissionLevel); - if (identity.getPid() == Process.myPid() && location != null) { + if (location != null && identity.getPid() == Process.myPid()) { // if delivering to the same process, make a copy of the location first (since // location is mutable) - return new Location(location); - } else { - return location; + location = new Location(location); } + + return location; } /** * This function does not perform any permissions or safety checks, by calling it you are - * committing to performing all applicable checks yourself. + * committing to performing all applicable checks yourself. This always returns a "fine" + * location, even if the permissionLevel is coarse. You are responsible for coarsening the + * location if necessary. */ @Nullable public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel, @@ -1535,11 +1513,10 @@ class LocationProviderManager extends mLastLocations.put(userId, lastLocation); } - Location coarseLocation = mLocationFudger.createCoarse(location); if (isEnabled(userId)) { - lastLocation.set(location, coarseLocation); + lastLocation.set(location); } - lastLocation.setBypass(location, coarseLocation); + lastLocation.setBypass(location); } } @@ -1560,51 +1537,26 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { - // shortcut various failure conditions so that we can return immediately rather than - // waiting for location to timeout - if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName())) { - registration.deliverLocation(null); - return null; - } - if (!request.isLocationSettingsIgnored()) { - if (!isEnabled(identity.getUserId())) { - registration.deliverLocation(null); - return null; - } - if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) { - registration.deliverLocation(null); - return null; - } - } - - Location lastLocation = getLastLocationUnsafe( - identity.getUserId(), - permissionLevel, - request.isLocationSettingsIgnored(), - MAX_CURRENT_LOCATION_AGE_MS); - if (lastLocation != null) { - registration.deliverLocation(lastLocation); - return null; - } - - // if last location isn't good enough then we add a location request long ident = Binder.clearCallingIdentity(); try { addRegistration(callback.asBinder(), registration); + if (!registration.isActive()) { + // if the registration never activated, fail it immediately + registration.deliverNull(); + } } finally { Binder.restoreCallingIdentity(ident); } } ICancellationSignal cancelTransport = CancellationSignal.createTransport(); - CancellationSignal cancellationSignal = CancellationSignal.fromTransport(cancelTransport); - cancellationSignal.setOnCancelListener(SingleUseCallback.wrap( - () -> { - synchronized (mLock) { - removeRegistration(callback.asBinder(), registration); - } - })); + CancellationSignal.fromTransport(cancelTransport) + .setOnCancelListener(SingleUseCallback.wrap( + () -> { + synchronized (mLock) { + removeRegistration(callback.asBinder(), registration); + } + })); return cancelTransport; } @@ -1619,16 +1571,16 @@ class LocationProviderManager extends public void registerLocationRequest(LocationRequest request, CallerIdentity identity, @PermissionLevel int permissionLevel, ILocationListener listener) { + LocationListenerRegistration registration = new LocationListenerRegistration( + request, + identity, + new LocationListenerTransport(listener), + permissionLevel); + synchronized (mLock) { long ident = Binder.clearCallingIdentity(); try { - addRegistration( - listener.asBinder(), - new LocationListenerRegistration( - request, - identity, - new LocationListenerTransport(listener), - permissionLevel)); + addRegistration(listener.asBinder(), registration); } finally { Binder.restoreCallingIdentity(ident); } @@ -1637,16 +1589,16 @@ class LocationProviderManager extends public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { + LocationPendingIntentRegistration registration = new LocationPendingIntentRegistration( + request, + callerIdentity, + new LocationPendingIntentTransport(mContext, pendingIntent), + permissionLevel); + synchronized (mLock) { long identity = Binder.clearCallingIdentity(); try { - addRegistration( - pendingIntent, - new LocationPendingIntentRegistration( - request, - callerIdentity, - new LocationPendingIntentTransport(mContext, pendingIntent), - permissionLevel)); + addRegistration(pendingIntent, registration); } finally { Binder.restoreCallingIdentity(identity); } @@ -1897,7 +1849,7 @@ class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - long intervalMs = Long.MAX_VALUE; + long intervalMs = INTERVAL_DISABLED; boolean locationSettingsIgnored = false; boolean lowPower = true; ArrayList locationRequests = new ArrayList<>(registrations.size()); @@ -1916,12 +1868,18 @@ class LocationProviderManager extends locationRequests.add(request); } + if (intervalMs == INTERVAL_DISABLED) { + return EMPTY_REQUEST; + } + // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold // interval slightly higher that the minimum interval, and spread the blame across all // contributing registrations under that threshold (since worksource does not allow us to // represent differing power blame ratios). - long thresholdIntervalMs = (intervalMs + 1000) * 3 / 2; - if (thresholdIntervalMs < 0 || thresholdIntervalMs >= PASSIVE_INTERVAL) { + long thresholdIntervalMs; + try { + thresholdIntervalMs = Math.multiplyExact(Math.addExact(intervalMs, 1000) / 2, 3); + } catch (ArithmeticException e) { // check for and handle overflow by setting to one below the passive interval so passive // requests are automatically skipped thresholdIntervalMs = PASSIVE_INTERVAL - 1; @@ -1930,7 +1888,7 @@ class LocationProviderManager extends WorkSource workSource = new WorkSource(); for (Registration registration : registrations) { if (registration.getRequest().getIntervalMillis() <= thresholdIntervalMs) { - workSource.add(registration.getWorkSource()); + workSource.add(registration.getRequest().getWorkSource()); } } @@ -1963,7 +1921,7 @@ class LocationProviderManager extends // location for our calculations instead. this prevents spammy add/remove behavior last = getLastLocationUnsafe( registration.getIdentity().getUserId(), - PERMISSION_FINE, + registration.getPermissionLevel(), false, locationRequest.getIntervalMillis()); } @@ -2256,6 +2214,20 @@ class LocationProviderManager extends updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } + @Nullable + private Location getPermittedLocation(@Nullable Location fineLocation, + @PermissionLevel int permissionLevel) { + switch (permissionLevel) { + case PERMISSION_FINE: + return fineLocation; + case PERMISSION_COARSE: + return fineLocation != null ? mLocationFudger.createCoarse(fineLocation) : null; + default: + // shouldn't be possible to have a client added without location permissions + throw new AssertionError(); + } + } + public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { synchronized (mLock) { ipw.print(mName); @@ -2306,10 +2278,14 @@ class LocationProviderManager extends public void clearMock() { if (mFineLocation != null && mFineLocation.isFromMockProvider()) { mFineLocation = null; + } + if (mCoarseLocation != null && mCoarseLocation.isFromMockProvider()) { mCoarseLocation = null; } if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) { mFineBypassLocation = null; + } + if (mCoarseBypassLocation != null && mCoarseBypassLocation.isFromMockProvider()) { mCoarseBypassLocation = null; } } @@ -2340,14 +2316,14 @@ class LocationProviderManager extends } } - public void set(Location fineLocation, Location coarseLocation) { - mFineLocation = calculateNextFine(mFineLocation, fineLocation); - mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation); + public void set(Location location) { + mFineLocation = calculateNextFine(mFineLocation, location); + mCoarseLocation = calculateNextCoarse(mCoarseLocation, location); } - public void setBypass(Location fineLocation, Location coarseLocation) { - mFineBypassLocation = calculateNextFine(mFineBypassLocation, fineLocation); - mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation); + public void setBypass(Location location) { + mFineBypassLocation = calculateNextFine(mFineBypassLocation, location); + mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, location); } private Location calculateNextFine(@Nullable Location oldFine, Location newFine) { @@ -2369,8 +2345,8 @@ class LocationProviderManager extends } // update last coarse interval only if enough time has passed - if (newCoarse.getElapsedRealtimeNanos() - MIN_COARSE_INTERVAL_MS - > oldCoarse.getElapsedRealtimeNanos()) { + if (newCoarse.getElapsedRealtimeMillis() - MIN_COARSE_INTERVAL_MS + > oldCoarse.getElapsedRealtimeMillis()) { return newCoarse; } else { return oldCoarse; diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index 3cd415ecb1c5c..31ec4a53908cc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -66,6 +66,7 @@ import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.WorkSource; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -111,6 +112,7 @@ public class LocationProviderManagerTest { private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, "mypackage", "attribution"); + private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid()); private Random mRandom; @@ -333,7 +335,7 @@ public class LocationProviderManagerTest { @Test public void testPassive_Listener() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -358,8 +360,11 @@ public class LocationProviderManagerTest { ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_FINE, + listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -402,8 +407,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -415,8 +423,11 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Unregister() throws Exception { ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_FINE, + listener); mManager.unregisterLocationRequest(listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -433,8 +444,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); CountDownLatch blocker = new CountDownLatch(1); IN_PROCESS_EXECUTOR.execute(() -> { @@ -455,7 +469,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NumUpdates() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setMaxUpdates(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -472,7 +489,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setDurationMillis(5000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAlarmHelper().incrementAlarmTime(5000); @@ -484,7 +504,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringNoAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setDurationMillis(25) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Thread.sleep(25); @@ -497,8 +520,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_FastestInterval() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis( - 5000).build(); + LocationRequest request = new LocationRequest.Builder(5000) + .setMinUpdateIntervalMillis(5000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -511,8 +536,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_SmallestDisplacement() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters( - 1f).build(); + LocationRequest request = new LocationRequest.Builder(5000) + .setMinUpdateDistanceMeters(1f) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -526,7 +553,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NoteOpFailure() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(), @@ -544,8 +571,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); CountDownLatch blocker = new CountDownLatch(1); IN_PROCESS_EXECUTOR.execute(() -> { @@ -572,8 +602,8 @@ public class LocationProviderManagerTest { ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -586,8 +616,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Cancel() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - ICancellationSignal cancellationSignal = mManager.getCurrentLocation(locationRequest, + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + ICancellationSignal cancellationSignal = mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); cancellationSignal.cancel(); @@ -599,8 +629,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_ProviderDisabled() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderAllowed(false); mProvider.setProviderAllowed(true); @@ -613,8 +643,8 @@ public class LocationProviderManagerTest { mProvider.setProviderAllowed(false); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderAllowed(true); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -629,8 +659,8 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(loc); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); verify(listener, times(1)).onLocation(locationCaptor.capture()); assertThat(locationCaptor.getValue()).isEqualTo(loc); @@ -639,8 +669,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Timeout() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAlarmHelper().incrementAlarmTime(60000); verify(listener, times(1)).onLocation(isNull()); @@ -654,7 +684,7 @@ public class LocationProviderManagerTest { IDENTITY.getPackageName())).isFalse(); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, @@ -683,7 +713,8 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().getLocationRequests()).isEmpty(); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource( + WORK_SOURCE).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -694,7 +725,10 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().getWorkSource()).isNotNull(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build(); + LocationRequest request2 = new LocationRequest.Builder(1) + .setLowPower(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -725,7 +759,9 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(createLocation(NAME, mRandom)); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(60000).build(); + LocationRequest request1 = new LocationRequest.Builder(60000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); verify(listener1).onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); @@ -742,7 +778,9 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(createLocation(NAME, mRandom)); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(60000).build(); + LocationRequest request1 = new LocationRequest.Builder(60000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isFalse(); @@ -757,7 +795,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_BackgroundThrottle() { ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); @@ -773,7 +813,9 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -781,8 +823,10 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored( - true).build(); + LocationRequest request2 = new LocationRequest.Builder(1) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -796,12 +840,16 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(1).build(); + LocationRequest request1 = new LocationRequest.Builder(1) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored( - true).build(); + LocationRequest request2 = new LocationRequest.Builder(5) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId()); @@ -818,8 +866,10 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored( - true).build(); + LocationRequest request = new LocationRequest.Builder(1) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet()); @@ -835,8 +885,10 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored( - true).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); @@ -851,7 +903,7 @@ public class LocationProviderManagerTest { LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5).build(); + LocationRequest request = new LocationRequest.Builder(5).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mProvider.getRequest().isActive()).isTrue();