diff options
20 files changed, 1153 insertions, 848 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fbe381b2ec15..78bc079c692e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9649,14 +9649,6 @@ public final class Settings { "location_ignore_settings_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/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 8600dc48c6a4..415092623531 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -47,16 +47,14 @@ interface ILocationManager Location getLastLocation(in LocationRequest request, String packageName, String featureId); boolean getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationListener listener, - String packageName, String featureId, String listenerIdentifier); + String packageName, String featureId); void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, - in PendingIntent intent, String packageName, String featureId, - String listenerIdentifier); - void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName); + in PendingIntent intent, String packageName, String featureId); + void removeUpdates(in ILocationListener listener, in PendingIntent intent); void requestGeofence(in LocationRequest request, in Geofence geofence, - in PendingIntent intent, String packageName, String featureId, - String listenerIdentifier); + in PendingIntent intent, String packageName, String featureId); void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName); boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName, @@ -73,34 +71,31 @@ interface ILocationManager boolean addGnssMeasurementsListener(in GnssRequest request, in IGnssMeasurementsListener listener, - String packageName, String featureId, - String listenerIdentifier); + String packageName, String featureId); void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, in String packageName); - long getGnssCapabilities(in String packageName); + long getGnssCapabilities(); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, - String packageName, String featureId, String listenerIdentifier); + String packageName, String featureId); void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener); boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, - String packageName, String featureId, String listenerIdentifier); + String packageName, String featureId); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); int getGnssYearOfHardware(); String getGnssHardwareModelName(); int getGnssBatchSize(String packageName); - boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, - String featureId, String listenerIdentifier); + boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId); void removeGnssBatchingCallback(); - boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName); + boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId); void flushGnssBatch(String packageName); boolean stopGnssBatch(); void injectLocation(in Location location); - @UnsupportedAppUsage List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); String getBestProvider(in Criteria criteria, boolean enabledOnly); @@ -116,11 +111,11 @@ interface ILocationManager boolean isProviderEnabledForUser(String provider, int userId); boolean isLocationEnabledForUser(int userId); void setLocationEnabledForUser(boolean enabled, int userId); - void addTestProvider(String name, in ProviderProperties properties, String opPackageName); - void removeTestProvider(String provider, String opPackageName); - void setTestProviderLocation(String provider, in Location loc, String opPackageName); - void setTestProviderEnabled(String provider, boolean enabled, String opPackageName); - List<LocationRequest> getTestProviderCurrentRequests(String provider, String opPackageName); + void addTestProvider(String name, in ProviderProperties properties, String packageName, String featureId); + void removeTestProvider(String provider, String packageName, String featureId); + void setTestProviderLocation(String provider, in Location location, String packageName, String featureId); + void setTestProviderEnabled(String provider, boolean enabled, String packageName, String featureId); + List<LocationRequest> getTestProviderCurrentRequests(String provider); LocationTime getGnssTimeMillis(); boolean sendExtraCommand(String provider, String command, inout Bundle extras); diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 6724324bfcb9..9aa0c870e512 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -585,6 +585,16 @@ public class Location implements Parcelable { return mElapsedRealtimeNanos; } + /** @hide */ + public long getElapsedRealtimeAgeNanos(long referenceRealtimeNs) { + return referenceRealtimeNs - mElapsedRealtimeNanos; + } + + /** @hide */ + public long getElapsedRealtimeAgeNanos() { + return getElapsedRealtimeAgeNanos(SystemClock.elapsedRealtimeNanos()); + } + /** * Set the time of this fix, in elapsed real-time since system boot. * diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index ff7049ee565b..fcbd3e540291 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -652,19 +652,6 @@ public class LocationManager { } /** - * Create a string that allows an app to identify a listener - * - * @param listener The listener - * - * @return A identifying string - */ - private static String getListenerIdentifier(@NonNull Object listener) { - return listener.getClass().getName() - + '@' - + Integer.toHexString(System.identityHashCode(listener)); - } - - /** * Asynchronously returns a single current location fix. This may activate sensors in order to * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return * a cached fix if available. The given callback will be invoked once and only once, either with @@ -742,8 +729,7 @@ public class LocationManager { try { if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, - listenerTransport, mContext.getPackageName(), mContext.getAttributionTag(), - getListenerIdentifier(consumer))) { + listenerTransport, mContext.getPackageName(), mContext.getAttributionTag())) { listenerTransport.register(mContext.getSystemService(AlarmManager.class), remoteCancellationSignal); if (cancellationSignal != null) { @@ -1189,8 +1175,7 @@ public class LocationManager { boolean registered = false; try { mService.requestLocationUpdates(locationRequest, transport, null, - mContext.getPackageName(), mContext.getAttributionTag(), - getListenerIdentifier(listener)); + mContext.getPackageName(), mContext.getAttributionTag()); registered = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1235,8 +1220,7 @@ public class LocationManager { try { mService.requestLocationUpdates(locationRequest, null, pendingIntent, - mContext.getPackageName(), mContext.getAttributionTag(), - getListenerIdentifier(pendingIntent)); + mContext.getPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1293,7 +1277,7 @@ public class LocationManager { transport.unregister(); try { - mService.removeUpdates(transport, null, mContext.getPackageName()); + mService.removeUpdates(transport, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1312,7 +1296,7 @@ public class LocationManager { Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); try { - mService.removeUpdates(null, pendingIntent, mContext.getPackageName()); + mService.removeUpdates(null, pendingIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1515,7 +1499,8 @@ public class LocationManager { requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement, accuracy); try { - mService.addTestProvider(provider, properties, mContext.getOpPackageName()); + mService.addTestProvider(provider, properties, mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1536,7 +1521,8 @@ public class LocationManager { Preconditions.checkArgument(provider != null, "invalid null provider"); try { - mService.removeTestProvider(provider, mContext.getOpPackageName()); + mService.removeTestProvider(provider, mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1570,7 +1556,8 @@ public class LocationManager { } try { - mService.setTestProviderLocation(provider, location, mContext.getOpPackageName()); + mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1599,7 +1586,8 @@ public class LocationManager { Preconditions.checkArgument(provider != null, "invalid null provider"); try { - mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName()); + mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1643,8 +1631,7 @@ public class LocationManager { public List<LocationRequest> getTestProviderCurrentRequests(String providerName) { Preconditions.checkArgument(providerName != null, "invalid null provider"); try { - return mService.getTestProviderCurrentRequests(providerName, - mContext.getOpPackageName()); + return mService.getTestProviderCurrentRequests(providerName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1711,7 +1698,7 @@ public class LocationManager { LocationRequest request = new LocationRequest().setExpireIn(expiration); try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), - mContext.getAttributionTag(), getListenerIdentifier(intent)); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1798,7 +1785,7 @@ public class LocationManager { try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), - mContext.getAttributionTag(), getListenerIdentifier(intent)); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1867,7 +1854,7 @@ public class LocationManager { */ public @NonNull GnssCapabilities getGnssCapabilities() { try { - long gnssCapabilities = mService.getGnssCapabilities(mContext.getPackageName()); + long gnssCapabilities = mService.getGnssCapabilities(); if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) { gnssCapabilities = 0L; } @@ -2493,7 +2480,7 @@ public class LocationManager { try { if (mBatchedLocationCallbackManager.addListener(callback, handler)) { return mService.startGnssBatch(periodNanos, wakeOnFifoFull, - mContext.getPackageName()); + mContext.getPackageName(), mContext.getAttributionTag()); } return false; } catch (RemoteException e) { @@ -3012,7 +2999,7 @@ public class LocationManager { GnssMeasurementsListener transport = new GnssMeasurementsListener(); if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(), - mContext.getAttributionTag(), "gnss measurement callback")) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { @@ -3065,7 +3052,7 @@ public class LocationManager { GnssNavigationMessageListener transport = new GnssNavigationMessageListener(); if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(), - mContext.getAttributionTag(), "gnss navigation callback")) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { @@ -3106,7 +3093,7 @@ public class LocationManager { GnssAntennaInfoListener transport = new GnssAntennaInfoListener(); if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(), - mContext.getAttributionTag(), "gnss antenna info callback")) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { @@ -3143,7 +3130,7 @@ public class LocationManager { BatchedLocationCallback transport = new BatchedLocationCallback(); if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(), - mContext.getAttributionTag(), "batched location callback")) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index fe083812ffed..d5c04dc1f160 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -318,7 +318,6 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, Settings.Global.LOCATION_IGNORE_SETTINGS_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 d814b9c9f1fa..8ccff7605811 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -16,7 +16,6 @@ package com.android.server; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; @@ -27,6 +26,9 @@ import static android.location.LocationManager.NETWORK_PROVIDER; import static android.location.LocationManager.PASSIVE_PROVIDER; import static android.os.PowerManager.locationPowerSaveModeToString; +import static com.android.server.location.CallerIdentity.PERMISSION_COARSE; +import static com.android.server.location.CallerIdentity.PERMISSION_FINE; + import static java.util.concurrent.TimeUnit.NANOSECONDS; import android.Manifest; @@ -34,17 +36,16 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssRequest; import android.location.IBatchedLocationCallback; @@ -58,6 +59,7 @@ import android.location.ILocationManager; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; +import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; import android.os.Binder; @@ -86,14 +88,15 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.location.AbstractLocationProvider; import com.android.server.location.AbstractLocationProvider.State; import com.android.server.location.AppForegroundHelper; +import com.android.server.location.AppOpsHelper; import com.android.server.location.CallerIdentity; +import com.android.server.location.CallerIdentity.PermissionLevel; import com.android.server.location.GeocoderProxy; import com.android.server.location.GeofenceManager; import com.android.server.location.GeofenceProxy; @@ -176,10 +179,6 @@ public class LocationManagerService extends ILocationManager.Stub { private static final String WAKELOCK_KEY = "*location*"; - private static final int RESOLUTION_LEVEL_NONE = 0; - private static final int RESOLUTION_LEVEL_COARSE = 1; - private static final int RESOLUTION_LEVEL_FINE = 2; - private static final String NETWORK_LOCATION_SERVICE_ACTION = "com.android.location.service.v3.NetworkLocationProvider"; private static final String FUSED_LOCATION_SERVICE_ACTION = @@ -208,6 +207,7 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private final Handler mHandler; private final LocalService mLocalService; + private final AppOpsHelper mAppOpsHelper; private final UserInfoHelper mUserInfoHelper; private final SettingsHelper mSettingsHelper; private final AppForegroundHelper mAppForegroundHelper; @@ -217,8 +217,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final PassiveLocationProviderManager mPassiveManager; - private AppOpsManager mAppOps; - private PackageManager mPackageManager; private PowerManager mPowerManager; private GeofenceManager mGeofenceManager; @@ -252,6 +250,7 @@ public class LocationManagerService extends ILocationManager.Stub { LocalServices.addService(LocationManagerInternal.class, mLocalService); + mAppOpsHelper = new AppOpsHelper(mContext); mUserInfoHelper = new UserInfoHelper(mContext); mSettingsHelper = new SettingsHelper(mContext, mHandler); mAppForegroundHelper = new AppForegroundHelper(mContext); @@ -280,32 +279,17 @@ public class LocationManagerService extends ILocationManager.Stub { } private void onSystemReady() { + mAppOpsHelper.onSystemReady(); mUserInfoHelper.onSystemReady(); mSettingsHelper.onSystemReady(); mAppForegroundHelper.onSystemReady(); synchronized (mLock) { - mPackageManager = mContext.getPackageManager(); - mAppOps = mContext.getSystemService(AppOpsManager.class); mPowerManager = mContext.getSystemService(PowerManager.class); mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper); - PowerManagerInternal localPowerManager = - LocalServices.getService(PowerManagerInternal.class); - // add listeners - mAppOps.startWatchingMode( - AppOpsManager.OP_COARSE_LOCATION, - null, - AppOpsManager.WATCH_FOREGROUND_CHANGES, - new AppOpsManager.OnOpChangedInternalListener() { - public void onOpChanged(int op, String packageName) { - // onOpChanged invoked on ui thread, move to our thread to reduce risk - // of blocking ui thread - mHandler.post(() -> onAppOpChanged(packageName)); - } - }); - mPackageManager.addOnPermissionsChangeListener( + mContext.getPackageManager().addOnPermissionsChangeListener( uid -> { // listener invoked on ui thread, move to our thread to reduce risk of // blocking ui thread @@ -316,7 +300,8 @@ public class LocationManagerService extends ILocationManager.Stub { }); }); - localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION, + LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver( + ServiceType.LOCATION, state -> { // listener invoked on ui thread, move to our thread to reduce risk of // blocking ui thread @@ -328,6 +313,8 @@ public class LocationManagerService extends ILocationManager.Stub { }); mBatterySaverMode = mPowerManager.getLocationPowerSaveMode(); + mAppOpsHelper.addListener(this::onAppOpChanged); + mSettingsHelper.addOnLocationEnabledChangedListener(this::onLocationModeChanged); mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener( this::onBackgroundThrottleIntervalChanged); @@ -336,40 +323,32 @@ public class LocationManagerService extends ILocationManager.Stub { mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener( this::onIgnoreSettingsWhitelistChanged); - new PackageMonitor() { + PackageMonitor packageMonitor = new PackageMonitor() { @Override public void onPackageDisappeared(String packageName, int reason) { synchronized (mLock) { - LocationManagerService.this.onPackageDisappearedLocked(packageName); + LocationManagerService.this.onPackageDisappeared(packageName); } } - }.register(mContext, mHandler.getLooper(), true); + }; + packageMonitor.register(mContext, null, true, mHandler); mUserInfoHelper.addListener(this::onUserChanged); mAppForegroundHelper.addListener(this::onAppForegroundChanged); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_SCREEN_OFF); - intentFilter.addAction(Intent.ACTION_SCREEN_ON); - + IntentFilter screenIntentFilter = new IntentFilter(); + screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF); + screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON); mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action == null) { - return; - } - synchronized (mLock) { - switch (action) { - case Intent.ACTION_SCREEN_ON: - case Intent.ACTION_SCREEN_OFF: - onScreenStateChangedLocked(); - break; - } + if (Intent.ACTION_SCREEN_ON.equals(intent.getAction()) + || Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + onScreenStateChanged(); } } - }, UserHandle.ALL, intentFilter, null, mHandler); + }, UserHandle.ALL, screenIntentFilter, null, mHandler); // initialize the current users. we would get the user started notifications for these // users eventually anyways, but this takes care of it as early as possible. @@ -389,7 +368,7 @@ public class LocationManagerService extends ILocationManager.Stub { private void onAppOpChanged(String packageName) { synchronized (mLock) { for (Receiver receiver : mReceivers.values()) { - if (receiver.mCallerIdentity.mPackageName.equals(packageName)) { + if (receiver.mCallerIdentity.packageName.equals(packageName)) { receiver.updateMonitoring(true); } } @@ -398,7 +377,7 @@ public class LocationManagerService extends ILocationManager.Stub { for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { String provider = entry.getKey(); for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mCallerIdentity.mPackageName.equals(packageName)) { + if (record.mReceiver.mCallerIdentity.packageName.equals(packageName)) { affectedProviders.add(provider); } } @@ -436,11 +415,12 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onScreenStateChangedLocked() { - if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + private void onScreenStateChanged() { + synchronized (mLock) { + if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) { + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); + } } } } @@ -466,23 +446,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onPackageDisappearedLocked(String packageName) { - ArrayList<Receiver> deadReceivers = null; + private void onPackageDisappeared(String packageName) { + synchronized (mLock) { + ArrayList<Receiver> deadReceivers = null; - for (Receiver receiver : mReceivers.values()) { - if (receiver.mCallerIdentity.mPackageName.equals(packageName)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<>(); + for (Receiver receiver : mReceivers.values()) { + if (receiver.mCallerIdentity.packageName.equals(packageName)) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<>(); + } + deadReceivers.add(receiver); } - deadReceivers.add(receiver); } - } - // perform removal outside of mReceivers loop - if (deadReceivers != null) { - for (Receiver receiver : deadReceivers) { - removeUpdatesLocked(receiver); + // perform removal outside of mReceivers loop + if (deadReceivers != null) { + for (Receiver receiver : deadReceivers) { + removeUpdatesLocked(receiver); + } } } } @@ -493,7 +474,7 @@ public class LocationManagerService extends ILocationManager.Stub { for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { String provider = entry.getKey(); for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mCallerIdentity.mUid == uid + if (record.mReceiver.mCallerIdentity.uid == uid && record.mIsForegroundUid != foreground) { record.updateForeground(foreground); @@ -574,17 +555,6 @@ public class LocationManagerService extends ILocationManager.Stub { Log.e(TAG, "no geocoder provider found"); } - // bind to geofence proxy - if (mGnssManagerService != null) { - IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy(); - if (gpsGeofenceHardware != null) { - GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware); - if (provider == null) { - Log.e(TAG, "unable to bind to GeofenceProxy"); - } - } - } - // bind to hardware activity recognition HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy = HardwareActivityRecognitionProxy.createAndRegister(mContext); @@ -607,19 +577,33 @@ public class LocationManagerService extends ILocationManager.Stub { Boolean.parseBoolean(fragments[7]) /* supportsBearing */, Integer.parseInt(fragments[8]) /* powerRequirement */, Integer.parseInt(fragments[9]) /* accuracy */); - addTestProvider(name, properties, mContext.getOpPackageName()); + LocationProviderManager manager = getLocationProviderManager(name); + if (manager == null) { + manager = new LocationProviderManager(name); + mProviderManagers.add(manager); + } + manager.setMockProvider(new MockProvider(properties)); } // initialize gnss last because it has no awareness of boot phases and blindly assumes that // all other location providers are loaded at initialization if (GnssManagerService.isGnssSupported()) { - mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper, + mGnssManagerService = new GnssManagerService(mContext, mAppOpsHelper, mSettingsHelper, mAppForegroundHelper, mLocationUsageLogger); mGnssManagerService.onSystemReady(); LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); mProviderManagers.add(gnssManager); gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); + + // bind to geofence proxy + IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy(); + if (gpsGeofenceHardware != null) { + GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware); + if (provider == null) { + Log.e(TAG, "unable to bind to GeofenceProxy"); + } + } } } @@ -742,16 +726,16 @@ public class LocationManagerService extends ILocationManager.Stub { } @Nullable - public Location getLastFineLocation(int userId) { + public Location getLastLocation(int userId, @PermissionLevel int permissionlevel) { synchronized (mLock) { - return mLastLocation.get(userId); - } - } - - @Nullable - public Location getLastCoarseLocation(int userId) { - synchronized (mLock) { - return mLastCoarseLocation.get(userId); + switch (permissionlevel) { + case PERMISSION_COARSE: + return mLastCoarseLocation.get(userId); + case PERMISSION_FINE: + return mLastLocation.get(userId); + default: + throw new AssertionError(); + } } } @@ -1037,7 +1021,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final class Receiver extends LocationManagerServiceUtils.LinkedListenerBase implements PendingIntent.OnFinished { private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; - private final int mAllowedResolutionLevel; // resolution level allowed to receiver private final ILocationListener mListener; final PendingIntent mPendingIntent; @@ -1054,11 +1037,9 @@ public class LocationManagerService extends ILocationManager.Stub { private int mPendingBroadcasts; PowerManager.WakeLock mWakeLock; - private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid, - String packageName, @Nullable String featureId, WorkSource workSource, - boolean hideFromAppOps, @NonNull String listenerIdentifier) { - super(new CallerIdentity(uid, pid, packageName, featureId, listenerIdentifier), - "LocationListener"); + private Receiver(ILocationListener listener, PendingIntent intent, CallerIdentity identity, + WorkSource workSource, boolean hideFromAppOps) { + super(identity); mListener = listener; mPendingIntent = intent; if (listener != null) { @@ -1066,7 +1047,6 @@ public class LocationManagerService extends ILocationManager.Stub { } else { mKey = intent; } - mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid); if (workSource != null && workSource.isEmpty()) { workSource = null; } @@ -1078,7 +1058,7 @@ public class LocationManagerService extends ILocationManager.Stub { // construct/configure wakelock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); if (workSource == null) { - workSource = new WorkSource(mCallerIdentity.mUid, mCallerIdentity.mPackageName); + workSource = new WorkSource(mCallerIdentity.uid, mCallerIdentity.packageName); } mWakeLock.setWorkSource(workSource); @@ -1136,7 +1116,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) { continue; } - if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.mUid)) + if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.uid)) && !isSettingsExempt(updateRecord)) { continue; } @@ -1156,42 +1136,43 @@ public class LocationManagerService extends ILocationManager.Stub { mOpMonitoring = updateMonitoring( requestingLocation, mOpMonitoring, - AppOpsManager.OP_MONITOR_LOCATION); + false); // Now update monitoring of high power requests only. boolean wasHighPowerMonitoring = mOpHighPowerMonitoring; mOpHighPowerMonitoring = updateMonitoring( requestingHighPowerLocation, mOpHighPowerMonitoring, - AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION); + true); if (mOpHighPowerMonitoring != wasHighPowerMonitoring) { - // Send an intent to notify that a high power request has been added/removed. - Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + long identity = Binder.clearCallingIdentity(); + try { + // Send an intent to notify that a high power request has been added/removed. + Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } finally { + Binder.restoreCallingIdentity(identity); + } } } - /** - * Update AppOps monitoring for a single location request and op type. - * - * @param allowMonitoring True if monitoring is allowed for this request/op. - * @param currentlyMonitoring True if AppOps is currently monitoring this request/op. - * @param op AppOps code for the op to update. - * @return True if monitoring is on for this request/op after updating. - */ private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring, - int op) { + boolean highPower) { if (!currentlyMonitoring) { if (allowMonitoring) { - return mAppOps.startOpNoThrow(op, mCallerIdentity.mUid, - mCallerIdentity.mPackageName, false, mCallerIdentity.mFeatureId, null) - == AppOpsManager.MODE_ALLOWED; + if (!highPower) { + return mAppOpsHelper.startLocationMonitoring(mCallerIdentity); + } else { + return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity); + } } } else { - if (!allowMonitoring - || mAppOps.checkOpNoThrow(op, mCallerIdentity.mUid, - mCallerIdentity.mPackageName) != AppOpsManager.MODE_ALLOWED) { - mAppOps.finishOp(op, mCallerIdentity.mUid, mCallerIdentity.mPackageName); + if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity)) { + if (!highPower) { + mAppOpsHelper.stopLocationMonitoring(mCallerIdentity); + } else { + mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity); + } return false; } } @@ -1230,7 +1211,7 @@ public class LocationManagerService extends ILocationManager.Stub { new Location(location)); try { mPendingIntent.send(mContext, 0, locationChanged, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), + CallerIdentity.asPermission(mCallerIdentity.permissionLevel), PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); // call this after broadcasting so we do not increment // if we throw an exception. @@ -1265,7 +1246,7 @@ public class LocationManagerService extends ILocationManager.Stub { providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled); try { mPendingIntent.send(mContext, 0, providerIntent, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), + CallerIdentity.asPermission(mCallerIdentity.permissionLevel), PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); // call this after broadcasting so we do not increment // if we throw an exception. @@ -1289,8 +1270,6 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void binderDied() { - if (D) Log.d(TAG, "Remote " + mListenerName + " died."); - synchronized (mLock) { removeUpdatesLocked(this); clearPendingBroadcastsLocked(); @@ -1380,11 +1359,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName, - String featureId, String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); - + String featureId) { return mGnssManagerService != null && mGnssManagerService.addGnssBatchingCallback( - callback, packageName, featureId, listenerIdentifier); + callback, packageName, featureId); } @Override @@ -1393,9 +1370,10 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { + public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, + String featureId) { return mGnssManagerService != null && mGnssManagerService.startGnssBatch(periodNanos, - wakeOnFifoFull, packageName); + wakeOnFifoFull, packageName, featureId); } @Override @@ -1419,110 +1397,6 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - private String getResolutionPermission(int resolutionLevel) { - switch (resolutionLevel) { - case RESOLUTION_LEVEL_FINE: - return ACCESS_FINE_LOCATION; - case RESOLUTION_LEVEL_COARSE: - return ACCESS_COARSE_LOCATION; - default: - return null; - } - } - - private int getAllowedResolutionLevel(int pid, int uid) { - if (mContext.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { - return RESOLUTION_LEVEL_FINE; - } else if (mContext.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) - == PERMISSION_GRANTED) { - return RESOLUTION_LEVEL_COARSE; - } else { - return RESOLUTION_LEVEL_NONE; - } - } - - private int getCallerAllowedResolutionLevel() { - return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); - } - - private boolean checkCallingOrSelfLocationPermission() { - return mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED - || mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - == PERMISSION_GRANTED; - } - - private void enforceCallingOrSelfLocationPermission() { - if (checkCallingOrSelfLocationPermission()) { - return; - } - - throw new SecurityException("uid " + Binder.getCallingUid() + " does not have " - + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); - } - - private void enforceCallingOrSelfPackageName(String packageName) { - int uid = Binder.getCallingUid(); - if (ArrayUtils.contains(mPackageManager.getPackagesForUid(uid), packageName)) { - return; - } - - throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); - } - - public static int resolutionLevelToOp(int allowedResolutionLevel) { - if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) { - if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) { - return AppOpsManager.OP_COARSE_LOCATION; - } else { - return AppOpsManager.OP_FINE_LOCATION; - } - } - return -1; - } - - private static String resolutionLevelToOpStr(int allowedResolutionLevel) { - switch (allowedResolutionLevel) { - case RESOLUTION_LEVEL_COARSE: - return AppOpsManager.OPSTR_COARSE_LOCATION; - case RESOLUTION_LEVEL_FINE: - // fall through - case RESOLUTION_LEVEL_NONE: - // fall through - default: - // Use the most restrictive ops if not sure. - return AppOpsManager.OPSTR_FINE_LOCATION; - } - } - - private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName, - @Nullable String featureId, int allowedResolutionLevel, @Nullable String message) { - int op = resolutionLevelToOp(allowedResolutionLevel); - if (op >= 0) { - if (mAppOps.noteOpNoThrow(op, uid, packageName, featureId, message) - != AppOpsManager.MODE_ALLOWED) { - return false; - } - } - - return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel; - } - - private 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) { - return false; - } - } - - return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel; - } - - /** - * Returns all providers by name, including passive and the ones that are not permitted to - * be accessed by the calling activity or are currently disabled, but excluding fused. - */ @Override public List<String> getAllProviders() { ArrayList<String> providers = new ArrayList<>(mProviderManagers.size()); @@ -1535,14 +1409,9 @@ public class LocationManagerService extends ILocationManager.Stub { return providers; } - /** - * Return all providers by name, that match criteria and are optionally - * enabled. - * Can return passive provider, but never returns fused provider. - */ @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { - if (!checkCallingOrSelfLocationPermission()) { + if (!CallerIdentity.checkCallingOrSelfLocationPermission(mContext)) { return Collections.emptyList(); } @@ -1556,9 +1425,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (enabledOnly && !manager.isEnabled(UserHandle.getCallingUserId())) { continue; } - if (criteria != null - && !android.location.LocationProvider.propertiesMeetCriteria( - name, manager.getProperties(), criteria)) { + if (criteria != null && !LocationProvider.propertiesMeetCriteria(name, + manager.getProperties(), criteria)) { continue; } providers.add(name); @@ -1567,18 +1435,14 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Return the name of the best provider given a Criteria object. - * This method has been deprecated from the public API, - * and the whole LocationProvider (including #meetsCriteria) - * has been deprecated as well. So this method now uses - * some simplified logic. - */ @Override public String getBestProvider(Criteria criteria, boolean enabledOnly) { - List<String> providers = getProviders(criteria, enabledOnly); - if (providers.isEmpty()) { - providers = getProviders(null, enabledOnly); + List<String> providers; + synchronized (mLock) { + providers = getProviders(criteria, enabledOnly); + if (providers.isEmpty()) { + providers = getProviders(null, enabledOnly); + } } if (!providers.isEmpty()) { @@ -1601,7 +1465,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (records != null) { for (UpdateRecord record : records) { if (!mUserInfoHelper.isCurrentUserId( - UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { + UserHandle.getUserId(record.mReceiver.mCallerIdentity.uid))) { continue; } @@ -1658,20 +1522,17 @@ public class LocationManagerService extends ILocationManager.Stub { // initialize the low power mode to true and set to false if any of the records requires providerRequest.setLowPowerMode(true); for (UpdateRecord record : records) { - int userId = UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid); - if (!mUserInfoHelper.isCurrentUserId(userId)) { + CallerIdentity identity = record.mReceiver.mCallerIdentity; + if (!mUserInfoHelper.isCurrentUserId(identity.userId)) { continue; } - if (!checkLocationAccess( - record.mReceiver.mCallerIdentity.mPid, - record.mReceiver.mCallerIdentity.mUid, - record.mReceiver.mCallerIdentity.mPackageName, - record.mReceiver.mAllowedResolutionLevel)) { + + if (!mAppOpsHelper.checkLocationAccess(identity)) { continue; } final boolean isBatterySaverDisablingLocation = shouldThrottleRequests || (isForegroundOnlyMode && !record.mIsForegroundUid); - if (!manager.isEnabled(userId) || isBatterySaverDisablingLocation) { + if (!manager.isEnabled(identity.userId) || isBatterySaverDisablingLocation) { if (isSettingsExempt(record)) { providerRequest.setLocationSettingsIgnored(true); providerRequest.setLowPowerMode(false); @@ -1718,7 +1579,7 @@ public class LocationManagerService extends ILocationManager.Stub { long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; for (UpdateRecord record : records) { if (mUserInfoHelper.isCurrentUserId( - UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { + UserHandle.getUserId(record.mReceiver.mCallerIdentity.uid))) { LocationRequest locationRequest = record.mRequest; // Don't assign battery blame for update records whose @@ -1735,8 +1596,8 @@ public class LocationManagerService extends ILocationManager.Stub { // Assign blame to caller if there's no WorkSource associated with // the request or if it's invalid. providerRequest.getWorkSource().add( - record.mReceiver.mCallerIdentity.mUid, - record.mReceiver.mCallerIdentity.mPackageName); + record.mReceiver.mCallerIdentity.uid, + record.mReceiver.mCallerIdentity.packageName); } } } @@ -1775,16 +1636,16 @@ public class LocationManagerService extends ILocationManager.Stub { } private boolean isThrottlingExempt(CallerIdentity callerIdentity) { - if (callerIdentity.mUid == Process.SYSTEM_UID) { + if (callerIdentity.uid == Process.SYSTEM_UID) { return true; } if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( - callerIdentity.mPackageName)) { + callerIdentity.packageName)) { return true; } - return mLocalService.isProviderPackage(callerIdentity.mPackageName); + return mLocalService.isProviderPackage(callerIdentity.packageName); } @@ -1794,11 +1655,11 @@ public class LocationManagerService extends ILocationManager.Stub { } if (mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( - record.mReceiver.mCallerIdentity.mPackageName)) { + record.mReceiver.mCallerIdentity.packageName)) { return true; } - return mLocalService.isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName); + return mLocalService.isProviderPackage(record.mReceiver.mCallerIdentity.packageName); } @@ -1821,9 +1682,9 @@ public class LocationManagerService extends ILocationManager.Stub { mRealRequest = request; mRequest = request; mReceiver = receiver; - mIsForegroundUid = mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.mUid); + mIsForegroundUid = mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.uid); - if (D && receiver.mCallerIdentity.mPid == Process.myPid()) { + if (D && receiver.mCallerIdentity.pid == Process.myPid()) { mStackTrace = new Throwable(); } @@ -1835,7 +1696,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Update statistics for historical location requests by package/provider mRequestStatistics.startRequesting( - mReceiver.mCallerIdentity.mPackageName, mReceiver.mCallerIdentity.mFeatureId, + mReceiver.mCallerIdentity.packageName, mReceiver.mCallerIdentity.featureId, provider, request.getInterval(), mIsForegroundUid); } @@ -1845,7 +1706,7 @@ public class LocationManagerService extends ILocationManager.Stub { private void updateForeground(boolean isForeground) { mIsForegroundUid = isForeground; mRequestStatistics.updateForeground( - mReceiver.mCallerIdentity.mPackageName, mReceiver.mCallerIdentity.mFeatureId, + mReceiver.mCallerIdentity.packageName, mReceiver.mCallerIdentity.featureId, mProvider, isForeground); } @@ -1853,19 +1714,18 @@ public class LocationManagerService extends ILocationManager.Stub { * Method to be called when a record will no longer be used. */ private void disposeLocked(boolean removeReceiver) { - String packageName = mReceiver.mCallerIdentity.mPackageName; - mRequestStatistics.stopRequesting(packageName, mReceiver.mCallerIdentity.mFeatureId, - mProvider); + CallerIdentity identity = mReceiver.mCallerIdentity; + mRequestStatistics.stopRequesting(identity.packageName, identity.featureId, mProvider); mLocationUsageLogger.logLocationApiUsage( LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, - packageName, + identity.packageName, mRealRequest, mReceiver.isListener(), mReceiver.isPendingIntent(), /* geofence= */ null, - mAppForegroundHelper.getImportance(mReceiver.mCallerIdentity.mUid)); + mAppForegroundHelper.getImportance(mReceiver.mCallerIdentity.uid)); // remove from mRecordsByProvider ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider); @@ -1889,18 +1749,10 @@ public class LocationManagerService extends ILocationManager.Stub { public String toString() { StringBuilder b = new StringBuilder("UpdateRecord["); b.append(mProvider).append(" "); - b.append(mReceiver.mCallerIdentity.mPackageName); - String featureId = mReceiver.mCallerIdentity.mFeatureId; - if (featureId != null) { - b.append(" ").append(featureId).append(" "); - } - b.append("(").append(mReceiver.mCallerIdentity.mUid); - if (mIsForegroundUid) { - b.append(" foreground"); - } else { - b.append(" background"); + b.append(mReceiver.mCallerIdentity).append(" "); + if (!mIsForegroundUid) { + b.append("(background) "); } - b.append(") "); b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource); if (mStackTrace != null) { @@ -1915,14 +1767,13 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid, - String packageName, @Nullable String featureId, WorkSource workSource, - boolean hideFromAppOps, @NonNull String listenerIdentifier) { + private Receiver getReceiverLocked(ILocationListener listener, CallerIdentity identity, + WorkSource workSource, boolean hideFromAppOps) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); - if (receiver == null) { - receiver = new Receiver(listener, null, pid, uid, packageName, featureId, workSource, - hideFromAppOps, listenerIdentifier); + if (receiver == null && identity != null) { + receiver = new Receiver(listener, null, identity, workSource, + hideFromAppOps); if (!receiver.linkToListenerDeathNotificationLocked( receiver.getListener().asBinder())) { return null; @@ -1933,13 +1784,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName, - @Nullable String featureId, WorkSource workSource, boolean hideFromAppOps, - @NonNull String listenerIdentifier) { + private Receiver getReceiverLocked(PendingIntent intent, CallerIdentity identity, + WorkSource workSource, boolean hideFromAppOps) { Receiver receiver = mReceivers.get(intent); - if (receiver == null) { - receiver = new Receiver(null, intent, pid, uid, packageName, featureId, workSource, - hideFromAppOps, listenerIdentifier); + if (receiver == null && identity != null) { + receiver = new Receiver(null, intent, identity, workSource, + hideFromAppOps); mReceivers.put(intent, receiver); } return receiver; @@ -1953,14 +1803,14 @@ public class LocationManagerService extends ILocationManager.Stub { * @return a version of request that meets the given resolution and consistency requirements * @hide */ - private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel, + private LocationRequest createSanitizedRequest(LocationRequest request, CallerIdentity identity, boolean callerHasLocationHardwarePermission) { LocationRequest sanitizedRequest = new LocationRequest(request); if (!callerHasLocationHardwarePermission) { // allow setting low power mode only for callers with location hardware permission sanitizedRequest.setLowPowerMode(false); } - if (resolutionLevel < RESOLUTION_LEVEL_FINE) { + if (identity.permissionLevel < PERMISSION_FINE) { switch (sanitizedRequest.getQuality()) { case LocationRequest.ACCURACY_FINE: sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK); @@ -1986,72 +1836,58 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, - PendingIntent intent, String packageName, String featureId, - String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); - - enforceCallingOrSelfLocationPermission(); - enforceCallingOrSelfPackageName(packageName); - - synchronized (mLock) { - if (request == null) request = DEFAULT_LOCATION_REQUEST; - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - WorkSource workSource = request.getWorkSource(); - if (workSource != null && !workSource.isEmpty()) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.UPDATE_DEVICE_STATS, null); - } - boolean hideFromAppOps = request.getHideFromAppOps(); - if (hideFromAppOps) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.UPDATE_APP_OPS_STATS, null); - } - if (request.isLocationSettingsIgnored()) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.WRITE_SECURE_SETTINGS, null); - } - boolean callerHasLocationHardwarePermission = - mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) - == PERMISSION_GRANTED; - LocationRequest sanitizedRequest = createSanitizedRequest(request, - allowedResolutionLevel, - callerHasLocationHardwarePermission); - - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); + PendingIntent intent, String packageName, String featureId) { + if (request == null) { + request = DEFAULT_LOCATION_REQUEST; + } - long identity = Binder.clearCallingIdentity(); - try { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); + identity.enforceLocationPermission(); - // We don't check for MODE_IGNORED here; we will do that when we go to deliver - // a location. - checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); + WorkSource workSource = request.getWorkSource(); + if (workSource != null && !workSource.isEmpty()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.UPDATE_DEVICE_STATS, null); + } + boolean hideFromAppOps = request.getHideFromAppOps(); + if (hideFromAppOps) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.UPDATE_APP_OPS_STATS, null); + } + if (request.isLocationSettingsIgnored()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.WRITE_SECURE_SETTINGS, null); + } + boolean callerHasLocationHardwarePermission = + mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) + == PERMISSION_GRANTED; + LocationRequest sanitizedRequest = createSanitizedRequest(request, + identity, + callerHasLocationHardwarePermission); - if (intent == null && listener == null) { - throw new IllegalArgumentException("need either listener or intent"); - } else if (intent != null && listener != null) { - throw new IllegalArgumentException( - "cannot register both listener and intent"); - } + if (intent == null && listener == null) { + throw new IllegalArgumentException("need either listener or intent"); + } else if (intent != null && listener != null) { + throw new IllegalArgumentException( + "cannot register both listener and intent"); + } - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, - packageName, request, listener != null, intent != null, - /* geofence= */ null, - mAppForegroundHelper.getImportance(uid)); + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, + packageName, request, listener != null, intent != null, + /* geofence= */ null, + mAppForegroundHelper.getImportance(identity.uid)); - Receiver receiver; - if (intent != null) { - receiver = getReceiverLocked(intent, pid, uid, packageName, featureId, - workSource, hideFromAppOps, listenerIdentifier); - } else { - receiver = getReceiverLocked(listener, pid, uid, packageName, featureId, - workSource, hideFromAppOps, listenerIdentifier); - } + synchronized (mLock) { + Receiver receiver; + if (intent != null) { + receiver = getReceiverLocked(intent, identity, workSource, hideFromAppOps); + } else { + receiver = getReceiverLocked(listener, identity, workSource, hideFromAppOps); + } + if (receiver != null) { requestLocationUpdatesLocked(sanitizedRequest, receiver); - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -2078,28 +1914,27 @@ public class LocationManagerService extends ILocationManager.Stub { oldRecord.disposeLocked(false); } - int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!manager.isEnabled(userId) && !isSettingsExempt(record)) { - // Notify the listener that updates are currently disabled - but only if the request - // does not ignore location settings - receiver.callProviderEnabledLocked(name, false); - } + long identity = Binder.clearCallingIdentity(); + try { + int userId = UserHandle.getUserId(receiver.mCallerIdentity.uid); + if (!manager.isEnabled(userId) && !isSettingsExempt(record)) { + // Notify the listener that updates are currently disabled - but only if the request + // does not ignore location settings + receiver.callProviderEnabledLocked(name, false); + } - applyRequirementsLocked(name); + applyRequirementsLocked(name); - // Update the monitoring here just in case multiple location requests were added to the - // same receiver (this request may be high power and the initial might not have been). - receiver.updateMonitoring(true); + // Update the monitoring here just in case multiple location requests were added to the + // same receiver (this request may be high power and the initial might not have been). + receiver.updateMonitoring(true); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override - public void removeUpdates(ILocationListener listener, PendingIntent intent, - String packageName) { - enforceCallingOrSelfPackageName(packageName); - - int pid = Binder.getCallingPid(); - int uid = Binder.getCallingUid(); - + public void removeUpdates(ILocationListener listener, PendingIntent intent) { if (intent == null && listener == null) { throw new IllegalArgumentException("need either listener or intent"); } else if (intent != null && listener != null) { @@ -2109,17 +1944,13 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Receiver receiver; if (intent != null) { - receiver = getReceiverLocked(intent, pid, uid, packageName, null, null, false, ""); + receiver = getReceiverLocked(intent, null, null, false); } else { - receiver = getReceiverLocked(listener, pid, uid, packageName, null, null, false, - ""); + receiver = getReceiverLocked(listener, null, null, false); } - long identity = Binder.clearCallingIdentity(); - try { + if (receiver != null) { removeUpdatesLocked(receiver); - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -2161,25 +1992,14 @@ public class LocationManagerService extends ILocationManager.Stub { request = DEFAULT_LOCATION_REQUEST; } - enforceCallingOrSelfLocationPermission(); - enforceCallingOrSelfPackageName(packageName); + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(mContext, packageName, featureId); + identity.enforceLocationPermission(); - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - if (!reportLocationAccessNoThrow(Binder.getCallingPid(), Binder.getCallingUid(), - packageName, featureId, allowedResolutionLevel, null)) { - if (D) { - Log.d(TAG, "not returning last loc for no op app: " + packageName); - } + if (mSettingsHelper.isLocationPackageBlacklisted(identity.userId, identity.packageName)) { return null; } - - int userId = UserHandle.getCallingUserId(); - - if (mSettingsHelper.isLocationPackageBlacklisted(userId, packageName)) { - return null; - } - - if (!mUserInfoHelper.isCurrentUserId(userId)) { + if (!mUserInfoHelper.isCurrentUserId(identity.userId)) { return null; } @@ -2188,40 +2008,26 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) { return null; } - - if (!manager.isEnabled(userId) && !request.isLocationSettingsIgnored()) { + if (!manager.isEnabled(identity.userId) && !request.isLocationSettingsIgnored()) { return null; } - Location location; - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - location = manager.getLastCoarseLocation(userId); - } else { - location = manager.getLastFineLocation(userId); - } - if (location == null) { + // appops check should always be right before delivery + if (!mAppOpsHelper.noteLocationAccess(identity)) { return null; } - // Don't return stale location to apps with foreground-only location permission. - String op = resolutionLevelToOpStr(allowedResolutionLevel); - long locationAgeMs = NANOSECONDS.toMillis( - SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos()); - if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs() - && (mAppOps.unsafeCheckOp(op, Binder.getCallingUid(), packageName) - == AppOpsManager.MODE_FOREGROUND)) { - return null; - } + Location location = manager.getLastLocation(identity.userId, identity.permissionLevel); // make a defensive copy - the client could be in the same process as us - return new Location(location); + return location != null ? new Location(location) : null; } } @Override public boolean getCurrentLocation(LocationRequest locationRequest, ICancellationSignal remoteCancellationSignal, ILocationListener listener, - String packageName, String featureId, String listenerIdentifier) { + String packageName, String featureId) { // side effect of validating locationRequest and packageName Location lastLocation = getLastLocation(locationRequest, packageName, featureId); if (lastLocation != null) { @@ -2246,13 +2052,12 @@ public class LocationManagerService extends ILocationManager.Stub { } } - requestLocationUpdates(locationRequest, listener, null, packageName, featureId, - listenerIdentifier); + requestLocationUpdates(locationRequest, listener, null, packageName, featureId); CancellationSignal cancellationSignal = CancellationSignal.fromTransport( remoteCancellationSignal); if (cancellationSignal != null) { cancellationSignal.setOnCancelListener( - () -> removeUpdates(listener, null, packageName)); + () -> removeUpdates(listener, null)); } return true; } @@ -2265,14 +2070,14 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = gpsManager.getLastFineLocation(UserHandle.getCallingUserId()); + Location location = gpsManager.getLastLocation(UserHandle.getCallingUserId(), + PERMISSION_FINE); if (location == null) { return null; } long currentNanos = SystemClock.elapsedRealtimeNanos(); - long deltaMs = NANOSECONDS.toMillis( - currentNanos - location.getElapsedRealtimeNanos()); + long deltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeAgeNanos(currentNanos)); return new LocationTime(location.getTime() + deltaMs, currentNanos); } } @@ -2295,32 +2100,27 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, - String packageName, String featureId, String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); + String packageName, String featureId) { + if (request == null) { + request = DEFAULT_LOCATION_REQUEST; + } - mContext.enforceCallingOrSelfPermission(ACCESS_FINE_LOCATION, null); - enforceCallingOrSelfPackageName(packageName); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); + identity.enforceLocationPermission(); + + Objects.requireNonNull(intent); - if (request == null) request = DEFAULT_LOCATION_REQUEST; - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - if (intent == null) { - throw new IllegalArgumentException("invalid pending intent: " + null); - } - // Require that caller can manage given document boolean callerHasLocationHardwarePermission = mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) == PERMISSION_GRANTED; - LocationRequest sanitizedRequest = createSanitizedRequest(request, - allowedResolutionLevel, + LocationRequest sanitizedRequest = createSanitizedRequest(request, identity, callerHasLocationHardwarePermission); if (D) { Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent); } - // geo-fence manager uses the public location API, need to clear identity - int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { + if (identity.userId != UserHandle.USER_SYSTEM) { // temporary measure until geofences work for secondary users Log.w(TAG, "proximity alerts are currently available only to the primary user"); return; @@ -2334,15 +2134,9 @@ public class LocationManagerService extends ILocationManager.Stub { /* hasListener= */ false, true, geofence, - mAppForegroundHelper.getImportance(uid)); + mAppForegroundHelper.getImportance(identity.uid)); - long identity = Binder.clearCallingIdentity(); - try { - mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel, - uid, packageName, featureId, listenerIdentifier); - } finally { - Binder.restoreCallingIdentity(identity); - } + mGeofenceManager.addFence(sanitizedRequest, geofence, intent, identity); } @Override @@ -2350,7 +2144,6 @@ public class LocationManagerService extends ILocationManager.Stub { if (intent == null) { throw new IllegalArgumentException("invalid pending intent: " + null); } - enforceCallingOrSelfPackageName(packageName); if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); @@ -2387,13 +2180,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssMeasurementsListener(@Nullable GnssRequest request, - IGnssMeasurementsListener listener, - String packageName, String featureId, - String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); - + IGnssMeasurementsListener listener, String packageName, String featureId) { return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener( - request, listener, packageName, featureId, listenerIdentifier); + request, listener, packageName, featureId); } @Override @@ -2414,18 +2203,17 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public long getGnssCapabilities(String packageName) { - return mGnssManagerService == null ? 0L : mGnssManagerService.getGnssCapabilities( - packageName); + public long getGnssCapabilities() { + return mGnssManagerService == null ? GnssCapabilities.INVALID_CAPABILITIES + : mGnssManagerService.getGnssCapabilities(); } @Override public boolean addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, - String packageName, String featureId, String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); + String packageName, String featureId) { return mGnssManagerService != null && mGnssManagerService.addGnssAntennaInfoListener( - listener, packageName, featureId, listenerIdentifier); + listener, packageName, featureId); } @Override @@ -2437,11 +2225,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, - String packageName, String featureId, String listenerIdentifier) { - Objects.requireNonNull(listenerIdentifier); - + String packageName, String featureId) { return mGnssManagerService != null && mGnssManagerService.addGnssNavigationMessageListener( - listener, packageName, featureId, listenerIdentifier); + listener, packageName, featureId); } @Override @@ -2453,29 +2239,27 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean sendExtraCommand(String providerName, String command, Bundle extras) { - Objects.requireNonNull(providerName); - Objects.requireNonNull(command); - + public boolean sendExtraCommand(String provider, String command, Bundle extras) { + CallerIdentity.enforceCallingOrSelfLocationPermission(mContext); mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, null); - enforceCallingOrSelfLocationPermission(); + + LocationProviderManager manager = getLocationProviderManager( + Objects.requireNonNull(provider)); + if (manager != null) { + manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), + Objects.requireNonNull(command), extras); + } mLocationUsageLogger.logLocationApiUsage( LocationStatsEnums.USAGE_STARTED, LocationStatsEnums.API_SEND_EXTRA_COMMAND, - providerName); - - LocationProviderManager manager = getLocationProviderManager(providerName); - if (manager != null) { - manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command, - extras); - } + provider); mLocationUsageLogger.logLocationApiUsage( LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_SEND_EXTRA_COMMAND, - providerName); + provider); return true; } @@ -2537,12 +2321,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void setLocationEnabledForUser(boolean enabled, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - null); - } - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS, - "Requires WRITE_SECURE_SETTINGS permission"); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "setLocationEnabledForUser", null); + + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS, null); LocationManager.invalidateLocalLocationEnabledCaches(); mSettingsHelper.setLocationEnabled(enabled, userId); @@ -2601,7 +2383,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void handleLocationChangedLocked(LocationProviderManager manager, Location location, + private void handleLocationChangedLocked(LocationProviderManager manager, Location fineLocation, Location coarseLocation) { if (!mProviderManagers.contains(manager)) { Log.w(TAG, "received location from unknown provider: " + manager.getName()); @@ -2610,7 +2392,7 @@ public class LocationManagerService extends ILocationManager.Stub { // notify passive provider if (manager != mPassiveManager) { - mPassiveManager.updateLocation(location); + mPassiveManager.updateLocation(fineLocation); } long now = SystemClock.elapsedRealtime(); @@ -2624,59 +2406,45 @@ public class LocationManagerService extends ILocationManager.Stub { // Broadcast location to all listeners for (UpdateRecord r : records) { Receiver receiver = r.mReceiver; + CallerIdentity identity = receiver.mCallerIdentity; boolean receiverDead = false; - int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!manager.isEnabled(userId) && !isSettingsExempt(r)) { + if (!manager.isEnabled(identity.userId) && !isSettingsExempt(r)) { continue; } - if (!mUserInfoHelper.isCurrentUserId(userId) - && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) { - if (D) { - Log.d(TAG, "skipping loc update for background user " + userId - + " (app: " + receiver.mCallerIdentity.mPackageName + ")"); - } + if (!mUserInfoHelper.isCurrentUserId(identity.userId) + && !isProviderPackage(identity.packageName)) { continue; } - if (mSettingsHelper.isLocationPackageBlacklisted(userId, - receiver.mCallerIdentity.mPackageName)) { - if (D) { - Log.d(TAG, "skipping loc update for blacklisted app: " + - receiver.mCallerIdentity.mPackageName); - } + if (mSettingsHelper.isLocationPackageBlacklisted(identity.userId, + identity.packageName)) { continue; } - Location notifyLocation; - if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - notifyLocation = coarseLocation; // use coarse location - } else { - notifyLocation = location; // use fine location - } - if (shouldBroadcastSafeLocked(notifyLocation, r.mLastFixBroadcast, r, now)) { - r.mLastFixBroadcast = notifyLocation; - // Report location access before delivering location to the client. This will - // note location delivery to appOps, so it should be called only when a - // location is really being delivered to the client. - if (!reportLocationAccessNoThrow( - receiver.mCallerIdentity.mPid, - receiver.mCallerIdentity.mUid, - receiver.mCallerIdentity.mPackageName, - receiver.mCallerIdentity.mFeatureId, - receiver.mAllowedResolutionLevel, - "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) { - if (D) { - Log.d(TAG, "skipping loc update for no op app: " - + receiver.mCallerIdentity.mPackageName); - } + Location location; + switch (identity.permissionLevel) { + case PERMISSION_COARSE: + location = coarseLocation; + break; + case PERMISSION_FINE: + location = fineLocation; + break; + default: + throw new AssertionError(); + } + + if (shouldBroadcastSafeLocked(location, r.mLastFixBroadcast, r, now)) { + r.mLastFixBroadcast = location; + + // appops check should always be right before delivery + if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity)) { continue; } - if (!receiver.callLocationChangedLocked(notifyLocation)) { - Log.w(TAG, "RemoteException calling onLocationChanged on " - + receiver); + + if (!receiver.callLocationChangedLocked(location)) { receiverDead = true; } r.mRealRequest.decrementNumUpdates(); @@ -2733,7 +2501,6 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - @Override public String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, @@ -2752,9 +2519,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void addTestProvider(String provider, ProviderProperties properties, - String packageName) { - if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) - != AppOpsManager.MODE_ALLOWED) { + String packageName, String featureId) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(mContext, packageName, featureId); + if (!mAppOpsHelper.noteMockLocationAccess(identity)) { return; } @@ -2770,9 +2538,10 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void removeTestProvider(String provider, String packageName) { - if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) - != AppOpsManager.MODE_ALLOWED) { + public void removeTestProvider(String provider, String packageName, String featureId) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(mContext, packageName, featureId); + if (!mAppOpsHelper.noteMockLocationAccess(identity)) { return; } @@ -2790,15 +2559,17 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void setTestProviderLocation(String provider, Location location, String packageName) { - Preconditions.checkArgument(location.isComplete(), - "incomplete location object, missing timestamp or accuracy?"); - - if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) - != AppOpsManager.MODE_ALLOWED) { + public void setTestProviderLocation(String provider, Location location, String packageName, + String featureId) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(mContext, packageName, featureId); + if (!mAppOpsHelper.noteMockLocationAccess(identity)) { return; } + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + LocationProviderManager manager = getLocationProviderManager(provider); if (manager == null) { throw new IllegalArgumentException("provider doesn't exist: " + provider); @@ -2808,9 +2579,11 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void setTestProviderEnabled(String provider, boolean enabled, String packageName) { - if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) - != AppOpsManager.MODE_ALLOWED) { + public void setTestProviderEnabled(String provider, boolean enabled, String packageName, + String featureId) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(mContext, packageName, featureId); + if (!mAppOpsHelper.noteMockLocationAccess(identity)) { return; } @@ -2824,12 +2597,8 @@ public class LocationManagerService extends ILocationManager.Stub { @Override @NonNull - public List<LocationRequest> getTestProviderCurrentRequests(String provider, - String packageName) { - if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) - != AppOpsManager.MODE_ALLOWED) { - return Collections.emptyList(); - } + public List<LocationRequest> getTestProviderCurrentRequests(String provider) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null); LocationProviderManager manager = getLocationProviderManager(provider); if (manager == null) { diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java index ba1c81cdd975..9d0fe5e936bb 100644 --- a/services/core/java/com/android/server/LocationManagerServiceUtils.java +++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; import android.os.RemoteException; -import android.util.Log; import com.android.server.location.CallerIdentity; @@ -32,9 +31,6 @@ import java.util.function.Consumer; */ public class LocationManagerServiceUtils { - private static final String TAG = "LocManagerServiceUtils"; - private static final boolean D = Log.isLoggable(TAG, Log.DEBUG); - /** * Listener that can be linked to a binder. * @param <TListener> listener type @@ -49,10 +45,9 @@ public class LocationManagerServiceUtils { public LinkedListener( @Nullable TRequest request, @NonNull TListener listener, - String listenerName, @NonNull CallerIdentity callerIdentity, @NonNull Consumer<TListener> binderDeathCallback) { - super(callerIdentity, listenerName); + super(callerIdentity); mListener = listener; mRequest = request; mBinderDeathCallback = binderDeathCallback; @@ -65,7 +60,6 @@ public class LocationManagerServiceUtils { @Override public void binderDied() { - if (D) Log.d(TAG, "Remote " + mListenerName + " died."); mBinderDeathCallback.accept(mListener); } } @@ -75,28 +69,20 @@ public class LocationManagerServiceUtils { */ public abstract static class LinkedListenerBase implements IBinder.DeathRecipient { protected final CallerIdentity mCallerIdentity; - protected final String mListenerName; - LinkedListenerBase( - @NonNull CallerIdentity callerIdentity, @NonNull String listenerName) { + LinkedListenerBase(@NonNull CallerIdentity callerIdentity) { mCallerIdentity = callerIdentity; - mListenerName = listenerName; } @Override public String toString() { - return mListenerName + "[" + mCallerIdentity.mPackageName + "(" + mCallerIdentity.mPid - + ")]"; + return mCallerIdentity.toString(); } public CallerIdentity getCallerIdentity() { return mCallerIdentity; } - public String getListenerName() { - return mListenerName; - } - /** * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death. */ @@ -105,9 +91,6 @@ public class LocationManagerServiceUtils { binder.linkToDeath(this, 0 /* flags */); return true; } catch (RemoteException e) { - // if the remote process registering the listener is already dead, just swallow the - // exception and return - Log.w(TAG, "Could not link " + mListenerName + " death callback.", e); return false; } } @@ -119,7 +102,7 @@ public class LocationManagerServiceUtils { try { binder.unlinkToDeath(this, 0 /* flags */); } catch (NoSuchElementException e) { - Log.w(TAG, "Could not unlink " + mListenerName + " death callback.", e); + // ignore } } } diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java new file mode 100644 index 000000000000..9c279166ac8a --- /dev/null +++ b/services/core/java/com/android/server/location/AppOpsHelper.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2020 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 static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_LOCATION; + +import static com.android.server.LocationManagerService.D; +import static com.android.server.LocationManagerService.TAG; + +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.Context; +import android.os.Binder; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.FgThread; + +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Provides helpers and listeners for appops. + */ +public class AppOpsHelper { + + /** + * Listener for current user changes. + */ + public interface LocationAppOpListener { + + /** + * Called when something has changed about a location appop for the given package. + */ + void onAppOpsChanged(String packageName); + } + + private final Context mContext; + private final CopyOnWriteArrayList<LocationAppOpListener> mListeners; + + @GuardedBy("this") + @Nullable + private AppOpsManager mAppOps; + + public AppOpsHelper(Context context) { + mContext = context; + mListeners = new CopyOnWriteArrayList<>(); + } + + /** Called when system is ready. */ + public synchronized void onSystemReady() { + if (mAppOps != null) { + return; + } + + mAppOps = Objects.requireNonNull(mContext.getSystemService(AppOpsManager.class)); + mAppOps.startWatchingMode( + AppOpsManager.OP_COARSE_LOCATION, + null, + AppOpsManager.WATCH_FOREGROUND_CHANGES, + new AppOpsManager.OnOpChangedInternalListener() { + public void onOpChanged(int op, String packageName) { + // invoked on ui thread, move to fg thread so ui thread isn't blocked + FgThread.getHandler().sendMessage( + PooledLambda.obtainMessage(AppOpsHelper::onAppOpChanged, + AppOpsHelper.this, packageName)); + } + }); + } + + private void onAppOpChanged(String packageName) { + if (D) { + Log.v(TAG, "location appop changed for " + packageName); + } + + for (LocationAppOpListener listener : mListeners) { + listener.onAppOpsChanged(packageName); + } + } + + /** + * Adds a listener for app ops events. Callbacks occur on an unspecified thread. + */ + public void addListener(LocationAppOpListener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener for app ops events. + */ + public void removeListener(LocationAppOpListener listener) { + mListeners.remove(listener); + } + + /** + * Checks if the given identity may have locations delivered without noting that a location is + * being delivered. This is a looser guarantee than {@link #noteLocationAccess(CallerIdentity)}, + * and this function does not validate package arguments and so should not be used with + * unvalidated arguments or before actually delivering locations. + * + * @see AppOpsManager#checkOpNoThrow(int, int, String) + */ + public boolean checkLocationAccess(CallerIdentity callerIdentity) { + synchronized (this) { + Preconditions.checkState(mAppOps != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + return mAppOps.checkOpNoThrow( + CallerIdentity.asAppOp(callerIdentity.permissionLevel), + callerIdentity.uid, + callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Notes location access to the given identity, ie, location delivery. This method should be + * called right before a location is delivered, and if it returns false, the location should not + * be delivered. + */ + public boolean noteLocationAccess(CallerIdentity identity) { + return noteOpNoThrow(CallerIdentity.asAppOp(identity.permissionLevel), identity); + } + + /** + * Notifies app ops that the given identity is using location at normal/low power levels. If + * this function returns false, do not later call + * {@link #stopLocationMonitoring(CallerIdentity)}. + */ + public boolean startLocationMonitoring(CallerIdentity identity) { + return startLocationMonitoring(OP_MONITOR_LOCATION, identity); + } + + /** + * Notifies app ops that the given identity is no longer using location at normal/low power + * levels. + */ + public void stopLocationMonitoring(CallerIdentity identity) { + stopLocationMonitoring(OP_MONITOR_LOCATION, identity); + } + + /** + * Notifies app ops that the given identity is using location at high levels. If this function + * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}. + */ + public boolean startHighPowerLocationMonitoring(CallerIdentity identity) { + return startLocationMonitoring(OP_MONITOR_HIGH_POWER_LOCATION, identity); + } + + /** + * Notifies app ops that the given identity is no longer using location at high power levels. + */ + public void stopHighPowerLocationMonitoring(CallerIdentity identity) { + stopLocationMonitoring(OP_MONITOR_HIGH_POWER_LOCATION, identity); + } + + /** + * Notes access to any mock location APIs. If this call returns false, access to the APIs should + * silently fail. + */ + public boolean noteMockLocationAccess(CallerIdentity callerIdentity) { + synchronized (this) { + Preconditions.checkState(mAppOps != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + // note that this is not the no throw version of noteOp, this call may throw exceptions + return mAppOps.noteOp( + AppOpsManager.OP_MOCK_LOCATION, + callerIdentity.uid, + callerIdentity.packageName, + callerIdentity.featureId, + null) == AppOpsManager.MODE_ALLOWED; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private boolean startLocationMonitoring(int appOp, CallerIdentity callerIdentity) { + synchronized (this) { + Preconditions.checkState(mAppOps != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + return mAppOps.startOpNoThrow( + appOp, + callerIdentity.uid, + callerIdentity.packageName, + false, + callerIdentity.featureId, + null) == AppOpsManager.MODE_ALLOWED; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void stopLocationMonitoring(int appOp, CallerIdentity callerIdentity) { + synchronized (this) { + Preconditions.checkState(mAppOps != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + mAppOps.finishOp( + appOp, + callerIdentity.uid, + callerIdentity.packageName, + callerIdentity.featureId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) { + synchronized (this) { + Preconditions.checkState(mAppOps != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + return mAppOps.noteOpNoThrow( + appOp, + callerIdentity.uid, + callerIdentity.packageName, + callerIdentity.featureId, + null) == AppOpsManager.MODE_ALLOWED; + } finally { + Binder.restoreCallingIdentity(identity); + } + } +} diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java index 75ba5b8c212e..b84fd13415fc 100644 --- a/services/core/java/com/android/server/location/CallerIdentity.java +++ b/services/core/java/com/android/server/location/CallerIdentity.java @@ -16,25 +16,211 @@ package com.android.server.location; -import android.annotation.NonNull; +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.annotation.IntDef; import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.Context; +import android.os.Binder; +import android.os.UserHandle; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Represents the calling process's uid, pid, and package name. */ -public class CallerIdentity { - public final int mUid; - public final int mPid; - public final String mPackageName; - public final @Nullable String mFeatureId; - public final @NonNull String mListenerIdentifier; - - public CallerIdentity(int uid, int pid, String packageName, @Nullable String featureId, - @NonNull String listenerIdentifier) { - mUid = uid; - mPid = pid; - mPackageName = packageName; - mFeatureId = featureId; - mListenerIdentifier = listenerIdentifier; +public final class CallerIdentity { + + public static final int PERMISSION_NONE = 0; + public static final int PERMISSION_COARSE = 1; + public static final int PERMISSION_FINE = 2; + + @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionLevel {} + + /** + * Converts the given permission level to the corresponding permission. + */ + public static String asPermission(@PermissionLevel int permissionLevel) { + switch (permissionLevel) { + case PERMISSION_COARSE: + return ACCESS_COARSE_LOCATION; + case PERMISSION_FINE: + return ACCESS_FINE_LOCATION; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Converts the given permission level to the corresponding appop. + */ + public static int asAppOp(@PermissionLevel int permissionLevel) { + switch (permissionLevel) { + case PERMISSION_COARSE: + return AppOpsManager.OP_COARSE_LOCATION; + case PERMISSION_FINE: + return AppOpsManager.OP_FINE_LOCATION; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Creates a CallerIdentity from the current binder identity, using the given package and + * feature id. The package will be checked to enforce it belongs to the calling uid, and a + * security exception will be thrown if it is invalid. + */ + public static CallerIdentity fromBinder(Context context, String packageName, + @Nullable String featureId) { + int uid = Binder.getCallingUid(); + if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) { + throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); + } + + return fromBinderUnsafe(context, packageName, featureId); + } + + /** + * Creates a CallerIdentity from the current binder identity, using the given package and + * feature id. The package will not be checked to enforce that it belongs to the calling uid - + * this method should only be used if the package will be validated by some other means, such as + * an appops call. + */ + public static CallerIdentity fromBinderUnsafe(Context context, String packageName, + @Nullable String featureId) { + return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), + UserHandle.getCallingUserId(), packageName, featureId, + getBinderPermissionLevel(context)); + } + + /** + * Throws a security exception if the caller does not hold a location permission. + */ + public static void enforceCallingOrSelfLocationPermission(Context context) { + enforceLocationPermission(Binder.getCallingUid(), getBinderPermissionLevel(context)); + } + + /** + * Returns false if the caller does not hold a location permission, true otherwise. + */ + public static boolean checkCallingOrSelfLocationPermission(Context context) { + return checkLocationPermission(getBinderPermissionLevel(context)); + } + + private static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel) { + if (checkLocationPermission(permissionLevel)) { + return; + } + + throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION + + " or " + ACCESS_FINE_LOCATION + "."); + } + + private static boolean checkLocationPermission(@PermissionLevel int permissionLevel) { + return permissionLevel >= PERMISSION_COARSE; + } + + private static @PermissionLevel int getBinderPermissionLevel(Context context) { + if (context.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) { + return PERMISSION_FINE; + } + if (context.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED) { + return PERMISSION_COARSE; + } + + return PERMISSION_NONE; + } + + /** The calling UID. */ + public final int uid; + + /** The calling PID. */ + public final int pid; + + /** The calling user. */ + public final int userId; + + /** The calling package name. */ + public final String packageName; + + /** The calling feature id. */ + public final @Nullable String featureId; + + /** + * The calling location permission level. This field should only be used for validating + * permissions for API access. It should not be used for validating permissions for location + * access - that must be done through appops. + */ + public final @PermissionLevel int permissionLevel; + + @VisibleForTesting + public CallerIdentity(int uid, int pid, int userId, String packageName, + @Nullable String featureId, @PermissionLevel int permissionLevel) { + this.uid = uid; + this.pid = pid; + this.userId = userId; + this.packageName = Objects.requireNonNull(packageName); + this.featureId = featureId; + this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE, + PERMISSION_FINE, "permissionLevel"); + } + + /** + * Throws a security exception if the CallerIdentity does not hold a location permission. + */ + public void enforceLocationPermission() { + enforceLocationPermission(uid, permissionLevel); + } + + @Override + public String toString() { + int length = 10 + packageName.length(); + if (featureId != null) { + length += featureId.length(); + } + + StringBuilder builder = new StringBuilder(length); + builder.append(pid).append("/").append(packageName); + if (featureId != null) { + builder.append("["); + if (featureId.startsWith(packageName)) { + builder.append(featureId.substring(packageName.length())); + } else { + builder.append(featureId); + } + builder.append("]"); + } + return builder.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CallerIdentity)) { + return false; + } + CallerIdentity that = (CallerIdentity) o; + return uid == that.uid + && pid == that.pid + && packageName.equals(that.packageName) + && Objects.equals(featureId, that.featureId); + } + + @Override + public int hashCode() { + return Objects.hash(uid, pid, packageName, featureId); } } diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java index 4e9c06761e54..195b059b7374 100644 --- a/services/core/java/com/android/server/location/GeofenceManager.java +++ b/services/core/java/com/android/server/location/GeofenceManager.java @@ -16,9 +16,6 @@ package com.android.server.location; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.Context; @@ -38,7 +35,6 @@ import android.util.Log; import android.util.Slog; import com.android.server.FgThread; -import com.android.server.LocationManagerService; import com.android.server.PendingIntentUtils; import java.io.PrintWriter; @@ -125,16 +121,10 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish } public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, - int allowedResolutionLevel, int uid, String packageName, @Nullable String featureId, - @NonNull String listenerIdentifier) { - if (D) { - Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence - + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName); - } - + CallerIdentity identity) { GeofenceState state = new GeofenceState(geofence, request.getExpirationRealtimeMs(SystemClock.elapsedRealtime()), - allowedResolutionLevel, uid, packageName, featureId, listenerIdentifier, intent); + identity, intent); synchronized (mLock) { // first make sure it doesn't already exist for (int i = mFences.size() - 1; i >= 0; i--) { @@ -182,26 +172,14 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish } synchronized (mLock) { - Iterator<GeofenceState> iter = mFences.iterator(); - while (iter.hasNext()) { - GeofenceState state = iter.next(); - if (state.mPackageName.equals(packageName)) { - iter.remove(); - } - } + mFences.removeIf(state -> state.mIdentity.packageName.equals(packageName)); scheduleUpdateFencesLocked(); } } private void removeExpiredFencesLocked() { long time = SystemClock.elapsedRealtime(); - Iterator<GeofenceState> iter = mFences.iterator(); - while (iter.hasNext()) { - GeofenceState state = iter.next(); - if (state.mExpireAt < time) { - iter.remove(); - } - } + mFences.removeIf(state -> state.mExpireAt < time); } private void scheduleUpdateFencesLocked() { @@ -266,24 +244,17 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish double minFenceDistance = Double.MAX_VALUE; boolean needUpdates = false; for (GeofenceState state : mFences) { - if (mSettingsStore.isLocationPackageBlacklisted(ActivityManager.getCurrentUser(), - state.mPackageName)) { - if (D) { - Slog.d(TAG, "skipping geofence processing for blacklisted app: " - + state.mPackageName); - } + CallerIdentity identity = state.mIdentity; + if (mSettingsStore.isLocationPackageBlacklisted(identity.userId, + identity.packageName)) { continue; } - int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel); + int op = CallerIdentity.asAppOp(identity.permissionLevel); if (op >= 0) { - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid, - state.mPackageName, state.mFeatureId, state.mListenerIdentifier) + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid, + identity.packageName, identity.featureId, null) != AppOpsManager.MODE_ALLOWED) { - if (D) { - Slog.d(TAG, "skipping geofence processing for no op app: " - + state.mPackageName); - } continue; } } @@ -429,7 +400,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish public void dump(PrintWriter pw) { for (GeofenceState state : mFences) { - pw.println(state.mPackageName + " " + state.mFence); + pw.println(state.mIdentity + " " + state.mFence); } } diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java index a91a1dcb78e7..13318bfa6492 100644 --- a/services/core/java/com/android/server/location/GeofenceState.java +++ b/services/core/java/com/android/server/location/GeofenceState.java @@ -17,8 +17,6 @@ package com.android.server.location; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.PendingIntent; import android.location.Geofence; import android.location.Location; @@ -37,29 +35,20 @@ public class GeofenceState { public final Geofence mFence; private final Location mLocation; public final long mExpireAt; - public final int mAllowedResolutionLevel; - public final int mUid; - public final String mPackageName; - public final @Nullable String mFeatureId; - public final @NonNull String mListenerIdentifier; + public final CallerIdentity mIdentity; public final PendingIntent mIntent; int mState; // current state double mDistanceToCenter; // current distance to center of fence - public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid, - String packageName, @Nullable String featureId, @NonNull String listenerIdentifier, + public GeofenceState(Geofence fence, long expireAt, CallerIdentity identity, PendingIntent intent) { mState = STATE_UNKNOWN; mDistanceToCenter = Double.MAX_VALUE; mFence = fence; mExpireAt = expireAt; - mAllowedResolutionLevel = allowedResolutionLevel; - mUid = uid; - mPackageName = packageName; - mFeatureId = featureId; - mListenerIdentifier = listenerIdentifier; + mIdentity = identity; mIntent = intent; mLocation = new Location(""); diff --git a/services/core/java/com/android/server/location/LocationPermissionUtil.java b/services/core/java/com/android/server/location/LocationPermissionUtil.java index 4465f31cb45b..80b93b5e8ef6 100644 --- a/services/core/java/com/android/server/location/LocationPermissionUtil.java +++ b/services/core/java/com/android/server/location/LocationPermissionUtil.java @@ -49,13 +49,13 @@ public final class LocationPermissionUtil { private static boolean hasPermissionLocationHardware(Context context, CallerIdentity callerIdentity) { return context.checkPermission(android.Manifest.permission.LOCATION_HARDWARE, - callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED; + callerIdentity.pid, callerIdentity.uid) == PackageManager.PERMISSION_GRANTED; } private static boolean hasPermissionUpdateAppOpsStats(Context context, CallerIdentity callerIdentity) { return context.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, - callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED; + callerIdentity.pid, callerIdentity.uid) == PackageManager.PERMISSION_GRANTED; } private LocationPermissionUtil() {} diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index f6652892c3de..9c908dfb4276 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -179,13 +179,12 @@ public abstract class RemoteListenerHelper<TRequest, TListener extends IInterfac if (LocationPermissionUtil.doesCallerReportToAppOps(context, callerIdentity)) { // The caller is identified as a location provider that will report location // access to AppOps. Skip noteOp but do checkOp to check for location permission. - return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid, - callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED; + return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.uid, + callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED; } - return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid, - callerIdentity.mPackageName, callerIdentity.mFeatureId, - "Location sent to " + callerIdentity.mListenerIdentifier) + return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.uid, + callerIdentity.packageName, callerIdentity.featureId, null) == AppOpsManager.MODE_ALLOWED; } diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java index 5fe21bdf8950..7ab258c29b46 100644 --- a/services/core/java/com/android/server/location/SettingsHelper.java +++ b/services/core/java/com/android/server/location/SettingsHelper.java @@ -22,7 +22,6 @@ import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTE import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS; import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST; -import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS; import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M; import static android.provider.Settings.Secure.LOCATION_MODE; import static android.provider.Settings.Secure.LOCATION_MODE_OFF; @@ -92,7 +91,6 @@ public class SettingsHelper { private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000; private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS = 30 * 60 * 1000; - private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000; private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f; private final Context mContext; @@ -285,21 +283,6 @@ public class SettingsHelper { } /** - * Retrieve maximum age of the last location. - */ - public long getMaxLastLocationAgeMs() { - long identity = Binder.clearCallingIdentity(); - try { - return Settings.Global.getLong( - mContext.getContentResolver(), - LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, - DEFAULT_MAX_LAST_LOCATION_AGE_MS); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid * coarsening. */ diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java index d839095542c7..4ee1100b5297 100644 --- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java @@ -112,7 +112,7 @@ public abstract class GnssAntennaInfoProvider foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> { if (!hasPermission(mContext, callerIdentity)) { logPermissionDisabledEventNotReported( - TAG, callerIdentity.mPackageName, "GNSS antenna info"); + TAG, callerIdentity.packageName, "GNSS antenna info"); return; } listener.onGnssAntennaInfoReceived(gnssAntennaInfos); diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index 9e64e3aeae59..711f45cb7d28 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -16,15 +16,11 @@ package com.android.server.location.gnss; -import static android.app.AppOpsManager.OP_FINE_LOCATION; import static android.location.LocationManager.GPS_PROVIDER; import android.Manifest; -import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppOpsManager; import android.content.Context; -import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssRequest; import android.location.IBatchedLocationCallback; @@ -41,7 +37,6 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Process; import android.os.RemoteException; -import android.os.UserHandle; import android.stats.location.LocationStatsEnums; import android.util.ArrayMap; import android.util.Log; @@ -54,6 +49,7 @@ import com.android.server.LocalServices; import com.android.server.LocationManagerServiceUtils.LinkedListener; import com.android.server.LocationManagerServiceUtils.LinkedListenerBase; import com.android.server.location.AppForegroundHelper; +import com.android.server.location.AppOpsHelper; import com.android.server.location.CallerIdentity; import com.android.server.location.LocationUsageLogger; import com.android.server.location.RemoteListenerHelper; @@ -76,6 +72,7 @@ public class GnssManagerService { } private final Context mContext; + private final AppOpsHelper mAppOpsHelper; private final SettingsHelper mSettingsHelper; private final AppForegroundHelper mAppForegroundHelper; private final LocationUsageLogger mLocationUsageLogger; @@ -112,8 +109,6 @@ public class GnssManagerService { @GuardedBy("this") @Nullable private LocationManagerInternal mLocationManagerInternal; - @GuardedBy("this") - @Nullable private AppOpsManager mAppOpsManager; private final Object mGnssBatchingLock = new Object(); @@ -127,19 +122,21 @@ public class GnssManagerService { @GuardedBy("mGnssBatchingLock") private boolean mGnssBatchingInProgress = false; - public GnssManagerService(Context context, SettingsHelper settingsHelper, - AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger) { - this(context, settingsHelper, appForegroundHelper, locationUsageLogger, null); + public GnssManagerService(Context context, AppOpsHelper appOpsHelper, + SettingsHelper settingsHelper, AppForegroundHelper appForegroundHelper, + LocationUsageLogger locationUsageLogger) { + this(context, appOpsHelper, settingsHelper, appForegroundHelper, locationUsageLogger, null); } // Can use this constructor to inject GnssLocationProvider for testing @VisibleForTesting - GnssManagerService(Context context, SettingsHelper settingsHelper, + GnssManagerService(Context context, AppOpsHelper appOpsHelper, SettingsHelper settingsHelper, AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger, GnssLocationProvider gnssLocationProvider) { Preconditions.checkState(isGnssSupported()); mContext = context; + mAppOpsHelper = appOpsHelper; mSettingsHelper = settingsHelper; mAppForegroundHelper = appForegroundHelper; mLocationUsageLogger = locationUsageLogger; @@ -169,11 +166,11 @@ public class GnssManagerService { return; } + mAppOpsHelper.onSystemReady(); mSettingsHelper.onSystemReady(); mAppForegroundHelper.onSystemReady(); mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mAppForegroundHelper.addListener(this::onAppForegroundChanged); } @@ -207,11 +204,7 @@ public class GnssManagerService { * Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in * {@link android.location.GnssCapabilities}. */ - public long getGnssCapabilities(String packageName) { - if (!checkLocationAppOp(packageName)) { - return GnssCapabilities.INVALID_CAPABILITIES; - } - + public long getGnssCapabilities() { return mGnssCapabilitiesProvider.getGnssCapabilities(); } @@ -222,10 +215,6 @@ public class GnssManagerService { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { - return 0; - } - synchronized (mGnssBatchingLock) { return mGnssBatchingProvider.getBatchSize(); } @@ -235,11 +224,13 @@ public class GnssManagerService { * Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered * as a collection. */ - public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { + public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, + String featureId) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); + if (!mAppOpsHelper.checkLocationAccess(identity)) { return false; } @@ -259,25 +250,19 @@ public class GnssManagerService { * Adds a GNSS batching callback for delivering GNSS location batch results. */ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName, - @Nullable String featureId, @NonNull String listenerIdentity) { + @Nullable String featureId) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { - return false; - } + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); - CallerIdentity callerIdentity = - new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, - featureId, listenerIdentity); synchronized (mGnssBatchingLock) { mGnssBatchingCallback = callback; mGnssBatchingDeathCallback = new LinkedListener<>( /* request= */ null, callback, - "BatchedLocationCallback", - callerIdentity, + identity, (IBatchedLocationCallback listener) -> { stopGnssBatch(); removeGnssBatchingCallback(); @@ -297,10 +282,6 @@ public class GnssManagerService { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { - return; - } - synchronized (mGnssBatchingLock) { mGnssBatchingProvider.flush(); } @@ -378,7 +359,7 @@ public class GnssManagerService { LinkedListener<TRequest, TListener> linkedListener = entry.getValue(); CallerIdentity callerIdentity = linkedListener.getCallerIdentity(); TRequest request = linkedListener.getRequest(); - if (callerIdentity.mUid != uid) { + if (callerIdentity.uid != uid) { continue; } @@ -396,21 +377,19 @@ public class GnssManagerService { TListener listener, String packageName, @Nullable String featureId, - @NonNull String listenerIdentifier, RemoteListenerHelper<TRequest, TListener> gnssDataProvider, ArrayMap<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners, Consumer<TListener> binderDeathCallback) { mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); + if (!mAppOpsHelper.checkLocationAccess(identity)) { return false; } - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName, featureId, listenerIdentifier); LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener, - listenerIdentifier, callerIdentity, binderDeathCallback); + identity, binderDeathCallback); IBinder binder = listener.asBinder(); if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) { return false; @@ -429,11 +408,11 @@ public class GnssManagerService { /* hasListener= */ true, /* hasIntent= */ false, /* geofence= */ null, - mAppForegroundHelper.getImportance(callerIdentity.mUid)); + mAppForegroundHelper.getImportance(identity.uid)); } - if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid) - || isThrottlingExempt(callerIdentity)) { - gnssDataProvider.addListener(request, listener, callerIdentity); + if (mAppForegroundHelper.isAppForeground(identity.uid) + || isThrottlingExempt(identity)) { + gnssDataProvider.addListener(request, listener, identity); } return true; } @@ -463,7 +442,7 @@ public class GnssManagerService { gnssDataProvider == mGnssMeasurementsProvider ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, - linkedListener.getCallerIdentity().mPackageName, + linkedListener.getCallerIdentity().packageName, /* LocationRequest= */ null, /* hasListener= */ true, /* hasIntent= */ false, @@ -485,7 +464,6 @@ public class GnssManagerService { listener, packageName, featureId, - "Gnss status", mGnssStatusProvider, mGnssStatusListeners, this::unregisterGnssStatusCallback); @@ -506,8 +484,7 @@ public class GnssManagerService { */ public boolean addGnssMeasurementsListener(@Nullable GnssRequest request, IGnssMeasurementsListener listener, String packageName, - @Nullable String featureId, - @NonNull String listenerIdentifier) { + @Nullable String featureId) { if (request != null && request.isFullTracking()) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE, null); @@ -518,7 +495,6 @@ public class GnssManagerService { listener, packageName, featureId, - listenerIdentifier, mGnssMeasurementsProvider, mGnssMeasurementsListeners, this::removeGnssMeasurementsListener); @@ -533,10 +509,6 @@ public class GnssManagerService { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!checkLocationAppOp(packageName)) { - return; - } - mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections( measurementCorrections); } @@ -556,18 +528,16 @@ public class GnssManagerService { * * @param listener called when GNSS antenna info is received * @param packageName name of requesting package - * @return true if listener is successfully added, false otherwise */ public boolean addGnssAntennaInfoListener( IGnssAntennaInfoListener listener, String packageName, - @Nullable String featureId, @NonNull String listenerIdentifier) { + @Nullable String featureId) { synchronized (mGnssAntennaInfoListeners) { return addGnssDataListenerLocked( /* request= */ null, listener, packageName, featureId, - listenerIdentifier, mGnssAntennaInfoProvider, mGnssAntennaInfoListeners, this::removeGnssAntennaInfoListener); @@ -591,14 +561,13 @@ public class GnssManagerService { */ public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName, - @Nullable String featureId, @NonNull String listenerIdentifier) { + @Nullable String featureId) { synchronized (mGnssNavigationMessageListeners) { return addGnssDataListenerLocked( /* request= */ null, listener, packageName, featureId, - listenerIdentifier, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners, this::removeGnssNavigationMessageListener); @@ -641,7 +610,7 @@ public class GnssManagerService { return; } - int userId = UserHandle.getUserId(gnssBatchingDeathCallback.getCallerIdentity().mUid); + int userId = gnssBatchingDeathCallback.getCallerIdentity().userId; if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, userId)) { Log.w(TAG, "reportLocationBatch() called without user permission"); return; @@ -655,27 +624,19 @@ public class GnssManagerService { } private boolean isThrottlingExempt(CallerIdentity callerIdentity) { - if (callerIdentity.mUid == Process.SYSTEM_UID) { + if (callerIdentity.uid == Process.SYSTEM_UID) { return true; } if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( - callerIdentity.mPackageName)) { + callerIdentity.packageName)) { return true; } synchronized (this) { Preconditions.checkState(mLocationManagerInternal != null); } - return mLocationManagerInternal.isProviderPackage(callerIdentity.mPackageName); - } - - private boolean checkLocationAppOp(String packageName) { - synchronized (this) { - Preconditions.checkState(mAppOpsManager != null); - } - return mAppOpsManager.checkOp(OP_FINE_LOCATION, Binder.getCallingUid(), packageName) - == AppOpsManager.MODE_ALLOWED; + return mLocationManagerInternal.isProviderPackage(callerIdentity.packageName); } /** diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 76c3ad02e7e9..b42670149ab4 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -117,7 +117,7 @@ public abstract class GnssMeasurementsProvider foreach((IGnssMeasurementsListener listener, CallerIdentity callerIdentity) -> { if (!hasPermission(mContext, callerIdentity)) { logPermissionDisabledEventNotReported( - TAG, callerIdentity.mPackageName, "GNSS measurements"); + TAG, callerIdentity.packageName, "GNSS measurements"); return; } listener.onGnssMeasurementsReceived(event); diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java index d2ecdeebd9d0..3417e6e7805d 100644 --- a/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java +++ b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java @@ -79,7 +79,7 @@ public abstract class GnssStatusListenerHelper extends final float[] basebandCn0s) { foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> { if (!hasPermission(mContext, callerIdentity)) { - logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName, + logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName, "GNSS status"); return; } @@ -91,7 +91,7 @@ public abstract class GnssStatusListenerHelper extends public void onNmeaReceived(final long timestamp, final String nmea) { foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> { if (!hasPermission(mContext, callerIdentity)) { - logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName, "NMEA"); + logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName, "NMEA"); return; } listener.onNmeaReceived(timestamp, nmea); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/AppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/AppOpsHelperTest.java new file mode 100644 index 000000000000..7fc10b14209b --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/AppOpsHelperTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2020 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 static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.AppOpsManager.OP_COARSE_LOCATION; +import static android.app.AppOpsManager.OP_FINE_LOCATION; +import static android.app.AppOpsManager.OP_MOCK_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_LOCATION; + +import static com.android.server.location.CallerIdentity.PERMISSION_COARSE; +import static com.android.server.location.CallerIdentity.PERMISSION_FINE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.app.AppOpsManager; +import android.content.Context; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AppOpsHelperTest { + + private static final long TIMEOUT_MS = 5000; + + @Mock private Context mContext; + @Mock private AppOpsManager mAppOps; + + private List<AppOpsManager.OnOpChangedInternalListener> mListeners = new ArrayList<>(); + + private AppOpsHelper mHelper; + + @Before + public void setUp() { + initMocks(this); + + doReturn(mAppOps).when(mContext).getSystemService(AppOpsManager.class); + doAnswer(invocation -> mListeners.add(invocation.getArgument(3))).when(mAppOps) + .startWatchingMode( + eq(AppOpsManager.OP_COARSE_LOCATION), + isNull(), + eq(AppOpsManager.WATCH_FOREGROUND_CHANGES), + any(AppOpsManager.OnOpChangedInternalListener.class)); + + mHelper = new AppOpsHelper(mContext); + mHelper.onSystemReady(); + } + + private void sendAppOp(String packageName) { + for (AppOpsManager.OnOpChangedInternalListener listener : mListeners) { + listener.onOpChanged(AppOpsManager.OP_COARSE_LOCATION, packageName); + } + } + + @Test + public void testListener() { + AppOpsHelper.LocationAppOpListener listener = mock( + AppOpsHelper.LocationAppOpListener.class); + mHelper.addListener(listener); + + sendAppOp("mypackage1"); + verify(listener, timeout(TIMEOUT_MS)).onAppOpsChanged("mypackage1"); + + sendAppOp("mypackage2"); + verify(listener, timeout(TIMEOUT_MS)).onAppOpsChanged("mypackage2"); + } + + @Test + public void testCheckLocationAccess() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + doReturn(MODE_ALLOWED).when( + mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage")); + assertThat(mHelper.checkLocationAccess(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage")); + assertThat(mHelper.checkLocationAccess(identity)).isFalse(); + + identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_COARSE); + + doReturn(MODE_ALLOWED).when( + mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage")); + assertThat(mHelper.checkLocationAccess(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage")); + assertThat(mHelper.checkLocationAccess(identity)).isFalse(); + } + + @Test + public void testNoteLocationAccess() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + doReturn(MODE_ALLOWED).when( + mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"), + eq("myfeature"), nullable(String.class)); + assertThat(mHelper.noteLocationAccess(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"), + eq("myfeature"), nullable(String.class)); + assertThat(mHelper.noteLocationAccess(identity)).isFalse(); + + + identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_COARSE); + + doReturn(MODE_ALLOWED).when( + mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"), + eq("myfeature"), nullable(String.class)); + assertThat(mHelper.noteLocationAccess(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"), + eq("myfeature"), nullable(String.class)); + assertThat(mHelper.noteLocationAccess(identity)).isFalse(); + } + + @Test + public void testStartLocationMonitoring() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + doReturn(MODE_ALLOWED).when( + mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"), + eq(false), eq("myfeature"), nullable(String.class)); + assertThat(mHelper.startLocationMonitoring(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"), + eq(false), eq("myfeature"), nullable(String.class)); + assertThat(mHelper.startLocationMonitoring(identity)).isFalse(); + } + + @Test + public void testStartHighPowerLocationMonitoring() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + doReturn(MODE_ALLOWED).when( + mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000), + eq("mypackage"), + eq(false), eq("myfeature"), nullable(String.class)); + assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000), + eq("mypackage"), + eq(false), eq("myfeature"), nullable(String.class)); + assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse(); + } + + @Test + public void testStopLocationMonitoring() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + mHelper.stopLocationMonitoring(identity); + verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature"); + } + + @Test + public void testStopHighPowerLocationMonitoring() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + mHelper.stopHighPowerLocationMonitoring(identity); + verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature"); + } + + @Test + public void testNoteMockLocationAccess() { + CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature", + PERMISSION_FINE); + + doReturn(MODE_ALLOWED).when( + mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"), + nullable(String.class)); + assertThat(mHelper.noteMockLocationAccess(identity)).isTrue(); + + doReturn(MODE_IGNORED).when( + mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"), + nullable(String.class)); + assertThat(mHelper.noteMockLocationAccess(identity)).isFalse(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java index be2a5c529181..36ab1b3ef1ac 100644 --- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java @@ -61,11 +61,12 @@ import android.os.RemoteException; import com.android.server.LocalServices; import com.android.server.location.AppForegroundHelper; +import com.android.server.location.AppOpsHelper; +import com.android.server.location.LocationUsageLogger; +import com.android.server.location.SettingsHelper; import com.android.server.location.gnss.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative; import com.android.server.location.gnss.GnssMeasurementsProvider.GnssMeasurementProviderNative; import com.android.server.location.gnss.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative; -import com.android.server.location.LocationUsageLogger; -import com.android.server.location.SettingsHelper; import org.junit.After; import org.junit.Before; @@ -77,7 +78,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -103,7 +104,8 @@ public class GnssManagerServiceTest { private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider; private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider; - // Managers and services + @Mock + private PackageManager mPackageManager; @Mock private AppOpsManager mAppOpsManager; @Mock @@ -131,6 +133,9 @@ public class GnssManagerServiceTest { Context.APP_OPS_SERVICE); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn( mAppOpsManager); + when(mMockContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( + new String[]{"com.android.server"}); enableLocationPermissions(); when(mAppForegroundHelper.isAppForeground(anyInt())).thenReturn(true); @@ -179,8 +184,11 @@ public class GnssManagerServiceTest { when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true); when(mMockGnssBatchingProvider.stop()).thenReturn(true); + // Managers and services + AppOpsHelper appOpsHelper = new AppOpsHelper(mMockContext); + // Create GnssManagerService - mGnssManagerService = new GnssManagerService(mMockContext, mSettingsHelper, + mGnssManagerService = new GnssManagerService(mMockContext, appOpsHelper, mSettingsHelper, mAppForegroundHelper, new LocationUsageLogger(), mMockGnssLocationProvider); mGnssManagerService.onSystemReady(); @@ -232,7 +240,7 @@ public class GnssManagerServiceTest { new GnssSingleSatCorrection.Builder().build(); return new GnssMeasurementCorrections.Builder().setSingleSatelliteCorrectionList( - Arrays.asList(gnssSingleSatCorrection)).build(); + Collections.singletonList(gnssSingleSatCorrection)).build(); } private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() { @@ -262,7 +270,7 @@ public class GnssManagerServiceTest { signalGainCorrectionsDbi, signalGainCorrectionsUncertaintyDbi); - List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList(); + List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList<>(); gnssAntennaInfos.add(new GnssAntennaInfo.Builder() .setCarrierFrequencyMHz(carrierFrequencyMHz) .setPhaseCenterOffset(phaseCenterOffset) @@ -284,7 +292,7 @@ public class GnssManagerServiceTest { PackageManager.PERMISSION_GRANTED); // AppOpsManager will return true if OP_FINE_LOCATION is checked - when(mAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer( + when(mAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())).thenAnswer( (InvocationOnMock invocation) -> { int code = (int) (invocation.getArguments()[0]); if (code == AppOpsManager.OP_FINE_LOCATION) { @@ -303,7 +311,7 @@ public class GnssManagerServiceTest { Mockito.doThrow(new SecurityException()).when( mMockContext).checkPermission(anyString(), anyInt(), anyInt()); - when(mAppOpsManager.checkOp(anyInt(), anyInt(), + when(mAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(AppOpsManager.MODE_ERRORED); when(mLocationManagerInternal.isProviderEnabledForUser(eq(GPS_PROVIDER), anyInt())) @@ -390,8 +398,7 @@ public class GnssManagerServiceTest { when(mMockGnssCapabilitiesProvider.getGnssCapabilities()).thenReturn(mGnssCapabilities); enableLocationPermissions(); - assertThat(mGnssManagerService.getGnssCapabilities("com.android.server")).isEqualTo( - mGnssCapabilities); + assertThat(mGnssManagerService.getGnssCapabilities()).isEqualTo(mGnssCapabilities); } @Test @@ -421,7 +428,7 @@ public class GnssManagerServiceTest { assertThrows(SecurityException.class, () -> mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, - "com.android.server")); + "com.android.server", null)); verify(mMockGnssBatchingProvider, times(0)).start(periodNanos, wakeOnFifoFull); } @@ -433,7 +440,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); assertThat(mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, - "com.android.server")) + "com.android.server", null)) .isEqualTo( true); verify(mMockGnssBatchingProvider, times(1)).start(100L, true); @@ -447,8 +454,7 @@ public class GnssManagerServiceTest { disableLocationPermissions(); assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssBatchingCallback( - mockBatchedLocationCallback, "com.android.server", "abcd123", - "TestBatchedLocationCallback")); + mockBatchedLocationCallback, "com.android.server", null)); mGnssManagerService.onReportLocation(mockLocationList); @@ -463,8 +469,8 @@ public class GnssManagerServiceTest { enableLocationPermissions(); assertThat(mGnssManagerService.addGnssBatchingCallback( - mockBatchedLocationCallback, "com.android.server", - "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + mockBatchedLocationCallback, "com.android.server", null)) + .isEqualTo(true); mGnssManagerService.onReportLocation(mockLocationList); @@ -480,11 +486,11 @@ public class GnssManagerServiceTest { enableLocationPermissions(); assertThat(mGnssManagerService.addGnssBatchingCallback( - mockBatchedLocationCallback1, "com.android.server", - "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + mockBatchedLocationCallback1, "com.android.server", null)) + .isEqualTo(true); assertThat(mGnssManagerService.addGnssBatchingCallback( - mockBatchedLocationCallback2, "com.android.server", - "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + mockBatchedLocationCallback2, "com.android.server", null)) + .isEqualTo(true); mGnssManagerService.onReportLocation(mockLocationList); @@ -517,7 +523,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback, - "com.android.server", "abcd123", "TestBatchedLocationCallback"); + "com.android.server", null); disableLocationPermissions(); @@ -538,7 +544,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback, - "com.android.server", "abcd123", "TestBatchedLocationCallback"); + "com.android.server", null); mGnssManagerService.removeGnssBatchingCallback(); @@ -623,7 +629,7 @@ public class GnssManagerServiceTest { assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssMeasurementsListener( new GnssRequest.Builder().build(), mockGnssMeasurementsListener, - "com.android.server", "abcd123", "TestGnssMeasurementsListener")); + "com.android.server", null)); mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived( @@ -642,8 +648,7 @@ public class GnssManagerServiceTest { assertThat(mGnssManagerService.addGnssMeasurementsListener( new GnssRequest.Builder().build(), mockGnssMeasurementsListener, - "com.android.server", "abcd123", - "TestGnssMeasurementsListener")).isEqualTo(true); + "com.android.server", null)).isEqualTo(true); mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived( @@ -690,8 +695,7 @@ public class GnssManagerServiceTest { mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(), mockGnssMeasurementsListener, - "com.android.server", "abcd123", - "TestGnssMeasurementsListener"); + "com.android.server", null); disableLocationPermissions(); @@ -714,8 +718,7 @@ public class GnssManagerServiceTest { mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(), mockGnssMeasurementsListener, - "com.android.server", "abcd123", - "TestGnssMeasurementsListener"); + "com.android.server", null); disableLocationPermissions(); @@ -738,7 +741,7 @@ public class GnssManagerServiceTest { assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssAntennaInfoListener( mockGnssAntennaInfoListener, - "com.android.server", "abcd123", "TestGnssAntennaInfoListener")); + "com.android.server", null)); mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); verify(mockGnssAntennaInfoListener, times(0)) @@ -754,7 +757,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener, - "com.android.server", "abcd123", "TestGnssAntennaInfoListener")).isEqualTo(true); + "com.android.server", null)).isEqualTo(true); mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); verify(mockGnssAntennaInfoListener, times(1)) @@ -771,7 +774,7 @@ public class GnssManagerServiceTest { mGnssManagerService.addGnssAntennaInfoListener( mockGnssAntennaInfoListener, - "com.android.server", "abcd123", "TestGnssAntennaInfoListener"); + "com.android.server", null); disableLocationPermissions(); @@ -793,7 +796,7 @@ public class GnssManagerServiceTest { mGnssManagerService.addGnssAntennaInfoListener( mockGnssAntennaInfoListener, - "com.android.server", "abcd123", "TestGnssAntennaInfoListener"); + "com.android.server", null); mGnssManagerService.removeGnssAntennaInfoListener( mockGnssAntennaInfoListener); @@ -813,8 +816,7 @@ public class GnssManagerServiceTest { assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssNavigationMessageListener( - mockGnssNavigationMessageListener, "com.android.server", - "abcd123", "TestGnssNavigationMessageListener")); + mockGnssNavigationMessageListener, "com.android.server", null)); mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); @@ -831,8 +833,8 @@ public class GnssManagerServiceTest { enableLocationPermissions(); assertThat(mGnssManagerService.addGnssNavigationMessageListener( - mockGnssNavigationMessageListener, "com.android.server", - "abcd123", "TestGnssNavigationMessageListener")).isEqualTo(true); + mockGnssNavigationMessageListener, "com.android.server", null)) + .isEqualTo(true); mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); @@ -849,8 +851,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); mGnssManagerService.addGnssNavigationMessageListener( - mockGnssNavigationMessageListener, "com.android.server", - "abcd123", "TestGnssNavigationMessageListener"); + mockGnssNavigationMessageListener, "com.android.server", null); disableLocationPermissions(); @@ -872,8 +873,7 @@ public class GnssManagerServiceTest { enableLocationPermissions(); mGnssManagerService.addGnssNavigationMessageListener( - mockGnssNavigationMessageListener, "com.android.server", - "abcd123", "TestGnssNavigationMessageListener"); + mockGnssNavigationMessageListener, "com.android.server", null); mGnssManagerService.removeGnssNavigationMessageListener( mockGnssNavigationMessageListener); |