From b6375a4f7ffb46d0e8b5a908b35bbf28e9b60908 Mon Sep 17 00:00:00 2001 From: Soonil Nagarkar Date: Wed, 29 Jan 2020 15:23:06 -0800 Subject: [PATCH] Create AppForegroundHelper Create a new helper to deal with application foreground state changes. 1) Rename various Helpers. 2) Move GnssManagerService into location dir. 3) Use new AppForegroundHelper everywhere. 4) Move sendNiRequest to internal API 5) Some refactoring of NetInitiatedActivity Test: atest AppForegroundHelperTest Change-Id: Ifffe0e6a34452a18d017c6b190547b904d77b430 --- .../internal/app/NetInitiatedActivity.java | 22 +- .../android/location/ILocationManager.aidl | 2 - .../android/location/LocationManager.java | 15 - .../location/LocationManagerInternal.java | 15 + .../server/LocationManagerService.java | 411 +++++++------- .../server/LocationManagerServiceUtils.java | 51 +- .../location/AbstractLocationProvider.java | 4 +- .../server/location/AppForegroundHelper.java | 131 +++++ .../server/location/GeofenceManager.java | 4 +- .../server/location/GnssLocationProvider.java | 8 +- ...SettingsStore.java => SettingsHelper.java} | 78 ++- ...UserInfoStore.java => UserInfoHelper.java} | 109 ++-- .../location/gnss/GnssManagerService.java | 514 ++++++------------ .../location/AppForegroundHelperTest.java | 112 ++++ ...StoreTest.java => UserInfoHelperTest.java} | 51 +- .../location/gnss/GnssManagerServiceTest.java | 57 +- 16 files changed, 803 insertions(+), 781 deletions(-) create mode 100644 services/core/java/com/android/server/location/AppForegroundHelper.java rename services/core/java/com/android/server/location/{LocationSettingsStore.java => SettingsHelper.java} (88%) rename services/core/java/com/android/server/location/{UserInfoStore.java => UserInfoHelper.java} (70%) create mode 100644 services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java rename services/tests/mockingservicestests/src/com/android/server/location/{UserInfoStoreTest.java => UserInfoHelperTest.java} (73%) diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 89aa770d7f1c5..92e9fe492442d 100644 --- a/core/java/com/android/internal/app/NetInitiatedActivity.java +++ b/core/java/com/android/internal/app/NetInitiatedActivity.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.location.LocationManager; +import android.location.LocationManagerInternal; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -32,6 +32,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.location.GpsNetInitiatedHandler; +import com.android.server.LocalServices; /** * This activity is shown to the user for him/her to accept or deny network-initiated @@ -68,14 +69,14 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { - case GPS_NO_RESPONSE_TIME_OUT: { - if (notificationId != -1) { - sendUserResponse(default_response); + case GPS_NO_RESPONSE_TIME_OUT: { + if (notificationId != -1) { + sendUserResponse(default_response); + } + finish(); } - finish(); - } - break; - default: + break; + default: } } }; @@ -137,9 +138,8 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa // Respond to NI Handler under GnssLocationProvider, 1 = accept, 2 = deny private void sendUserResponse(int response) { if (DEBUG) Log.d(TAG, "sendUserResponse, response: " + response); - LocationManager locationManager = (LocationManager) - this.getSystemService(Context.LOCATION_SERVICE); - locationManager.sendNiResponse(notificationId, response); + LocationManagerInternal lm = LocalServices.getService(LocationManagerInternal.class); + lm.sendNiResponse(notificationId, response); } @UnsupportedAppUsage diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 79bec920c10ee..6a5c0ec9457a8 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -69,8 +69,6 @@ interface ILocationManager double upperRightLatitude, double upperRightLongitude, int maxResults, in GeocoderParams params, out List
addrs); - boolean sendNiResponse(int notifId, int userResponse); - boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, String packageName, String featureId, String listenerIdentifier); void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 0c5fe787bbbc2..197787e5b6e62 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -2397,21 +2397,6 @@ public class LocationManager { } } - /** - * Used by NetInitiatedActivity to report user response - * for network initiated GPS fix requests. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public boolean sendNiResponse(int notifId, int userResponse) { - try { - return mService.sendNiResponse(notifId, userResponse); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - private void checkPendingIntent(PendingIntent pendingIntent) { Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); if (!pendingIntent.isTargetedToPackage()) { diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 44d9d2372665f..69162bab31676 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -41,4 +41,19 @@ public abstract class LocationManagerInternal { * @throws IllegalArgumentException if provider is null */ public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed); + + /** + * Returns true if the given package belongs to a location provider, and so should be afforded + * some special privileges. + * + * @param packageName The package name to check + * @return True is the given package belongs to a location provider, false otherwise + */ + public abstract boolean isProviderPackage(@NonNull String packageName); + + /** + * Should only be used by GNSS code. + */ + // TODO: there is no reason for this to exist as part of any API. move all the logic into gnss + public abstract void sendNiResponse(int notifId, int userResponse); } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index f1f2d2abea3f6..3bc93cc130238 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -30,7 +30,6 @@ import static android.os.PowerManager.locationPowerSaveModeToString; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -88,6 +87,7 @@ 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.CallerIdentity; import com.android.server.location.GeocoderProxy; import com.android.server.location.GeofenceManager; @@ -98,12 +98,12 @@ import com.android.server.location.LocationProviderProxy; import com.android.server.location.LocationRequestStatistics; import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageStatistics; -import com.android.server.location.LocationSettingsStore; import com.android.server.location.LocationUsageLogger; import com.android.server.location.MockProvider; import com.android.server.location.MockableLocationProvider; import com.android.server.location.PassiveProvider; -import com.android.server.location.UserInfoStore; +import com.android.server.location.SettingsHelper; +import com.android.server.location.UserInfoHelper; import com.android.server.location.gnss.GnssManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -140,7 +140,6 @@ public class LocationManagerService extends ILocationManager.Stub { public Lifecycle(Context context) { super(context); mService = new LocationManagerService(context); - LocalServices.addService(LocationManagerInternal.class, mService.new LocalService()); } @Override @@ -181,9 +180,6 @@ public class LocationManagerService extends ILocationManager.Stub { // maximum age of a location before it is no longer considered "current" private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000; - private static final int FOREGROUND_IMPORTANCE_CUTOFF - = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; - // Location Providers may sometimes deliver location updates // slightly faster that requested - provide grace period so // we don't unnecessarily filter events that are otherwise on @@ -195,21 +191,23 @@ public class LocationManagerService extends ILocationManager.Stub { private final Object mLock = new Object(); private final Context mContext; private final Handler mHandler; - private final UserInfoStore mUserInfoStore; - private final LocationSettingsStore mSettingsStore; + private final LocalService mLocalService; + private final UserInfoHelper mUserInfoHelper; + private final SettingsHelper mSettingsHelper; + private final AppForegroundHelper mAppForegroundHelper; private final LocationUsageLogger mLocationUsageLogger; + @Nullable private GnssManagerService mGnssManagerService = null; + private final PassiveLocationProviderManager mPassiveManager; private AppOpsManager mAppOps; private PackageManager mPackageManager; private PowerManager mPowerManager; - private ActivityManager mActivityManager; private GeofenceManager mGeofenceManager; private LocationFudger mLocationFudger; private GeocoderProxy mGeocodeProvider; - @Nullable private GnssManagerService mGnssManagerService; @GuardedBy("mLock") private String mExtraLocationControllerPackage; @@ -245,8 +243,13 @@ public class LocationManagerService extends ILocationManager.Stub { private LocationManagerService(Context context) { mContext = context; mHandler = FgThread.getHandler(); - mUserInfoStore = new UserInfoStore(mContext); - mSettingsStore = new LocationSettingsStore(mContext, mHandler); + mLocalService = new LocalService(); + + LocalServices.addService(LocationManagerInternal.class, mLocalService); + + mUserInfoHelper = new UserInfoHelper(mContext); + mSettingsHelper = new SettingsHelper(mContext, mHandler); + mAppForegroundHelper = new AppForegroundHelper(mContext); mLocationUsageLogger = new LocationUsageLogger(); // set up passive provider - we do this early because it has no dependencies on system @@ -272,17 +275,23 @@ public class LocationManagerService extends ILocationManager.Stub { } private void onSystemReady() { - mUserInfoStore.onSystemReady(); - mSettingsStore.onSystemReady(); + mUserInfoHelper.onSystemReady(); + mSettingsHelper.onSystemReady(); + mAppForegroundHelper.onSystemReady(); + + if (GnssManagerService.isGnssSupported()) { + mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper, + mAppForegroundHelper, mLocationUsageLogger); + mGnssManagerService.onSystemReady(); + } synchronized (mLock) { mPackageManager = mContext.getPackageManager(); mAppOps = mContext.getSystemService(AppOpsManager.class); mPowerManager = mContext.getSystemService(PowerManager.class); - mActivityManager = mContext.getSystemService(ActivityManager.class); mLocationFudger = new LocationFudger(mContext, mHandler); - mGeofenceManager = new GeofenceManager(mContext, mSettingsStore); + mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper); PowerManagerInternal localPowerManager = LocalServices.getService(PowerManagerInternal.class); @@ -313,17 +322,6 @@ public class LocationManagerService extends ILocationManager.Stub { } }); }); - mActivityManager.addOnUidImportanceListener( - (uid, importance) -> { - // listener invoked on ui thread, move to our thread to reduce risk of - // blocking ui thread - mHandler.post(() -> { - synchronized (mLock) { - onUidImportanceChangedLocked(uid, importance); - } - }); - }, - FOREGROUND_IMPORTANCE_CUTOFF); localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION, state -> { @@ -337,26 +335,13 @@ public class LocationManagerService extends ILocationManager.Stub { }); mBatterySaverMode = mPowerManager.getLocationPowerSaveMode(); - mSettingsStore.addOnLocationEnabledChangedListener((userId) -> { - synchronized (mLock) { - onLocationModeChangedLocked(userId); - } - }); - mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> { - synchronized (mLock) { - onBackgroundThrottleIntervalChangedLocked(); - } - }); - mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> { - synchronized (mLock) { - onBackgroundThrottleWhitelistChangedLocked(); - } - }); - mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> { - synchronized (mLock) { - onIgnoreSettingsWhitelistChangedLocked(); - } - }); + mSettingsHelper.addOnLocationEnabledChangedListener(this::onLocationModeChanged); + mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener( + this::onBackgroundThrottleIntervalChanged); + mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener( + this::onBackgroundThrottleWhitelistChanged); + mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener( + this::onIgnoreSettingsWhitelistChanged); new PackageMonitor() { @Override @@ -367,11 +352,9 @@ public class LocationManagerService extends ILocationManager.Stub { } }.register(mContext, mHandler.getLooper(), true); - mUserInfoStore.addListener((oldUserId, newUserId) -> { - synchronized (mLock) { - onUserChangedLocked(oldUserId, newUserId); - } - }); + mUserInfoHelper.addListener(this::onUserChanged); + + mAppForegroundHelper.addListener(this::onAppForegroundChanged); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); @@ -398,7 +381,7 @@ public class LocationManagerService extends ILocationManager.Stub { // switching the user from null to current here performs the bulk of the initialization // work. the user being changed will cause a reload of all user specific settings, which // causes initialization, and propagates changes until a steady state is reached - onUserChangedLocked(UserHandle.USER_NULL, mUserInfoStore.getCurrentUserId()); + onUserChanged(UserHandle.USER_NULL, mUserInfoHelper.getCurrentUserId()); } } @@ -455,20 +438,23 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onLocationModeChangedLocked(int userId) { + private void onLocationModeChanged(int userId) { + boolean enabled = mSettingsHelper.isLocationEnabled(userId); + if (D) { - Log.d(TAG, "[u" + userId + "] location enabled = " + isLocationEnabledForUser(userId)); + Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); } - Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) - .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId)) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + synchronized (mLock) { + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); - for (LocationProviderManager manager : mProviderManagers) { + for (LocationProviderManager manager : mProviderManagers) { manager.onEnabledChangedLocked(userId); + } } } @@ -493,58 +479,55 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onUidImportanceChangedLocked(int uid, int importance) { - boolean foreground = LocationManagerServiceUtils.isImportanceForeground(importance); - HashSet affectedProviders = new HashSet<>(mRecordsByProvider.size()); - for (Entry> entry : mRecordsByProvider.entrySet()) { - String provider = entry.getKey(); - for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mCallerIdentity.mUid == uid - && record.mIsForegroundUid != foreground) { - if (D) { - Log.d(TAG, "request from uid " + uid + " is now " - + LocationManagerServiceUtils.foregroundAsString( - foreground)); - } - record.updateForeground(foreground); + private void onAppForegroundChanged(int uid, boolean foreground) { + synchronized (mLock) { + HashSet affectedProviders = new HashSet<>(mRecordsByProvider.size()); + for (Entry> entry : mRecordsByProvider.entrySet()) { + String provider = entry.getKey(); + for (UpdateRecord record : entry.getValue()) { + if (record.mReceiver.mCallerIdentity.mUid == uid + && record.mIsForegroundUid != foreground) { + record.updateForeground(foreground); - if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) { - affectedProviders.add(provider); + if (!isThrottlingExempt(record.mReceiver.mCallerIdentity)) { + affectedProviders.add(provider); + } } } } - } - for (String provider : affectedProviders) { - applyRequirementsLocked(provider); + for (String provider : affectedProviders) { + applyRequirementsLocked(provider); + } } } - @GuardedBy("mLock") - private void onBackgroundThrottleIntervalChangedLocked() { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + private void onBackgroundThrottleIntervalChanged() { + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); + } } } - @GuardedBy("mLock") - private void onBackgroundThrottleWhitelistChangedLocked() { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + private void onBackgroundThrottleWhitelistChanged() { + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); + } } } - @GuardedBy("lock") - private void onIgnoreSettingsWhitelistChangedLocked() { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + private void onIgnoreSettingsWhitelistChanged() { + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); + } } } @GuardedBy("mLock") private void initializeProvidersLocked() { - if (GnssManagerService.isGnssSupported()) { - mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger); + if (mGnssManagerService != null) { LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); mProviderManagers.add(gnssManager); gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); @@ -627,19 +610,20 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onUserChangedLocked(int oldUserId, int newUserId) { + private void onUserChanged(int oldUserId, int newUserId) { if (D) { Log.d(TAG, "foreground user is changing to " + newUserId); } - for (LocationProviderManager manager : mProviderManagers) { - // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - mSettingsStore.setLocationProviderAllowed(manager.getName(), - manager.isEnabled(newUserId), newUserId); + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility + mSettingsHelper.setLocationProviderAllowed(manager.getName(), + manager.isEnabled(newUserId), newUserId); - manager.onEnabledChangedLocked(oldUserId); - manager.onEnabledChangedLocked(newUserId); + manager.onEnabledChangedLocked(oldUserId); + manager.onEnabledChangedLocked(newUserId); + } } } @@ -778,7 +762,7 @@ public class LocationManagerService extends ILocationManager.Stub { // it would be more correct to call this for all users, but we know this can // only affect the current user since providers are disabled for non-current // users - onEnabledChangedLocked(mUserInfoStore.getCurrentUserId()); + onEnabledChangedLocked(mUserInfoHelper.getCurrentUserId()); } } @@ -787,14 +771,14 @@ public class LocationManagerService extends ILocationManager.Stub { } public boolean isEnabled() { - return isEnabled(mUserInfoStore.getCurrentUserId()); + return isEnabled(mUserInfoHelper.getCurrentUserId()); } public boolean isEnabled(int userId) { synchronized (mLock) { // normalize user id to always refer to parent since profile state is always the // same as parent state - userId = mUserInfoStore.getParentUserId(userId); + userId = mUserInfoHelper.getParentUserId(userId); return mEnabled.get(userId, Boolean.FALSE); } } @@ -808,13 +792,13 @@ public class LocationManagerService extends ILocationManager.Stub { // normalize user id to always refer to parent since profile state is always the same // as parent state - userId = mUserInfoStore.getParentUserId(userId); + userId = mUserInfoHelper.getParentUserId(userId); // if any property that contributes to "enabled" here changes state, it MUST result // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean enabled = (userId == mUserInfoStore.getCurrentUserId()) - && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().allowed; + boolean enabled = (userId == mUserInfoHelper.getCurrentUserId()) + && mSettingsHelper.isLocationEnabled(userId) && mProvider.getState().allowed; if (enabled == isEnabled(userId)) { return; @@ -829,7 +813,7 @@ public class LocationManagerService extends ILocationManager.Stub { // fused and passive provider never get public updates for legacy reasons if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - mSettingsStore.setLocationProviderAllowed(mName, enabled, userId); + mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId); Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION) .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName) @@ -1009,7 +993,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) { continue; } - if (!manager.isEnabled() && !isSettingsExemptLocked(updateRecord)) { + if (!manager.isEnabled() && !isSettingsExempt(updateRecord)) { continue; } @@ -1475,13 +1459,13 @@ public class LocationManagerService extends ILocationManager.Stub { ArrayList records = mRecordsByProvider.get(manager.getName()); if (records != null) { for (UpdateRecord record : records) { - if (!mUserInfoStore.isCurrentUserOrProfile( + if (!mUserInfoHelper.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { continue; } // requests that ignore location settings will never provide notifications - if (isSettingsExemptLocked(record)) { + if (isSettingsExempt(record)) { continue; } @@ -1520,14 +1504,7 @@ public class LocationManagerService extends ILocationManager.Stub { // if provider is not active, it should not respond to requests if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) { - long backgroundThrottleInterval; - - long identity = Binder.clearCallingIdentity(); - try { - backgroundThrottleInterval = mSettingsStore.getBackgroundThrottleIntervalMs(); - } finally { - Binder.restoreCallingIdentity(identity); - } + long backgroundThrottleInterval = mSettingsHelper.getBackgroundThrottleIntervalMs(); ArrayList requests = new ArrayList<>(records.size()); @@ -1540,7 +1517,7 @@ 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) { - if (!mUserInfoStore.isCurrentUserOrProfile( + if (!mUserInfoHelper.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { continue; } @@ -1554,7 +1531,7 @@ public class LocationManagerService extends ILocationManager.Stub { final boolean isBatterySaverDisablingLocation = shouldThrottleRequests || (isForegroundOnlyMode && !record.mIsForegroundUid); if (!manager.isEnabled() || isBatterySaverDisablingLocation) { - if (isSettingsExemptLocked(record)) { + if (isSettingsExempt(record)) { providerRequest.setLocationSettingsIgnored(true); providerRequest.setLowPowerMode(false); } else { @@ -1567,7 +1544,7 @@ public class LocationManagerService extends ILocationManager.Stub { // if we're forcing location, don't apply any throttling - if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExemptLocked( + if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExempt( record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); @@ -1599,7 +1576,7 @@ public class LocationManagerService extends ILocationManager.Stub { // TODO: overflow long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; for (UpdateRecord record : records) { - if (mUserInfoStore.isCurrentUserOrProfile( + if (mUserInfoHelper.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { LocationRequest locationRequest = record.mRequest; @@ -1648,41 +1625,39 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public String[] getBackgroundThrottlingWhitelist() { - return mSettingsStore.getBackgroundThrottlePackageWhitelist().toArray(new String[0]); + return mSettingsHelper.getBackgroundThrottlePackageWhitelist().toArray(new String[0]); } @Override public String[] getIgnoreSettingsWhitelist() { - return mSettingsStore.getIgnoreSettingsPackageWhitelist().toArray(new String[0]); + return mSettingsHelper.getIgnoreSettingsPackageWhitelist().toArray(new String[0]); } - @GuardedBy("mLock") - public boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) { + private boolean isThrottlingExempt(CallerIdentity callerIdentity) { if (callerIdentity.mUid == Process.SYSTEM_UID) { return true; } - if (mSettingsStore.getBackgroundThrottlePackageWhitelist().contains( + if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( callerIdentity.mPackageName)) { return true; } - return isProviderPackage(callerIdentity.mPackageName); + return mLocalService.isProviderPackage(callerIdentity.mPackageName); } - @GuardedBy("mLock") - private boolean isSettingsExemptLocked(UpdateRecord record) { + private boolean isSettingsExempt(UpdateRecord record) { if (!record.mRealRequest.isLocationSettingsIgnored()) { return false; } - if (mSettingsStore.getIgnoreSettingsPackageWhitelist().contains( + if (mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( record.mReceiver.mCallerIdentity.mPackageName)) { return true; } - return isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName); + return mLocalService.isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName); } @@ -1705,10 +1680,7 @@ public class LocationManagerService extends ILocationManager.Stub { mRealRequest = request; mRequest = request; mReceiver = receiver; - mIsForegroundUid = - LocationManagerServiceUtils.isImportanceForeground( - mActivityManager.getPackageImportance( - mReceiver.mCallerIdentity.mPackageName)); + mIsForegroundUid = mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.mUid); if (D && receiver.mCallerIdentity.mPid == Process.myPid()) { mStackTrace = new Throwable(); @@ -1753,7 +1725,7 @@ public class LocationManagerService extends ILocationManager.Stub { mReceiver.isListener(), mReceiver.isPendingIntent(), /* geofence= */ null, - mActivityManager.getPackageImportance(packageName)); + mAppForegroundHelper.getImportance(mReceiver.mCallerIdentity.mUid)); // remove from mRecordsByProvider ArrayList globalRecords = mRecordsByProvider.get(this.mProvider); @@ -1923,7 +1895,7 @@ public class LocationManagerService extends ILocationManager.Stub { LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, packageName, request, listener != null, intent != null, /* geofence= */ null, - mActivityManager.getPackageImportance(packageName)); + mAppForegroundHelper.getImportance(uid)); Receiver receiver; if (intent != null) { @@ -1961,7 +1933,7 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " + name + " " + request + " from " + packageName + "(" + uid + " " + (record.mIsForegroundUid ? "foreground" : "background") - + (isThrottlingExemptLocked(receiver.mCallerIdentity) + + (isThrottlingExempt(receiver.mCallerIdentity) ? " [whitelisted]" : "") + ")"); } @@ -1970,7 +1942,7 @@ public class LocationManagerService extends ILocationManager.Stub { oldRecord.disposeLocked(false); } - if (!manager.isEnabled() && !isSettingsExemptLocked(record)) { + if (!manager.isEnabled() && !isSettingsExempt(record)) { // Notify the listener that updates are currently disabled - but only if the request // does not ignore location settings receiver.callProviderEnabledLocked(name, false); @@ -2060,7 +2032,7 @@ public class LocationManagerService extends ILocationManager.Stub { final int uid = Binder.getCallingUid(); final long identity = Binder.clearCallingIdentity(); try { - if (mSettingsStore.isLocationPackageBlacklisted(UserHandle.getUserId(uid), + if (mSettingsHelper.isLocationPackageBlacklisted(UserHandle.getUserId(uid), packageName)) { if (D) { Log.d(TAG, "not returning last loc for blacklisted app: " @@ -2077,8 +2049,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) return null; // only the current user or location providers may get location this way - if (!mUserInfoStore.isCurrentUserOrProfile(UserHandle.getUserId(uid)) - && !isProviderPackage(packageName)) { + if (!mUserInfoHelper.isCurrentUserOrProfile(UserHandle.getUserId(uid)) + && !mLocalService.isProviderPackage(packageName)) { return null; } @@ -2102,7 +2074,7 @@ public class LocationManagerService extends ILocationManager.Stub { String op = resolutionLevelToOpStr(allowedResolutionLevel); long locationAgeMs = TimeUnit.NANOSECONDS.toMillis( SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos()); - if (locationAgeMs > mSettingsStore.getMaxLastLocationAgeMs() + if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs() && (mAppOps.unsafeCheckOp(op, uid, packageName) == AppOpsManager.MODE_FOREGROUND)) { return null; @@ -2145,29 +2117,21 @@ public class LocationManagerService extends ILocationManager.Stub { long locationAgeMs = TimeUnit.NANOSECONDS.toMillis( SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos()); - long identity = Binder.clearCallingIdentity(); - try { - if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { - try { - listener.onLocationChanged(lastLocation); - return true; - } catch (RemoteException e) { - Log.w(TAG, e); - return false; - } + if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { + try { + listener.onLocationChanged(lastLocation); + return true; + } catch (RemoteException e) { + Log.w(TAG, e); + return false; } + } - // packageName already validated by getLastLocation() call above - boolean foreground = LocationManagerServiceUtils.isImportanceForeground( - mActivityManager.getPackageImportance(packageName)); - if (!foreground) { - if (locationAgeMs < mSettingsStore.getBackgroundThrottleIntervalMs()) { - // not allowed to request new locations, so we can't return anything - return false; - } + if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) { + if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) { + // not allowed to request new locations, so we can't return anything + return false; } - } finally { - Binder.restoreCallingIdentity(identity); } } @@ -2252,20 +2216,19 @@ public class LocationManagerService extends ILocationManager.Stub { Log.w(TAG, "proximity alerts are currently available only to the primary user"); return; } + + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + LocationStatsEnums.API_REQUEST_GEOFENCE, + packageName, + request, + /* hasListener= */ false, + true, + geofence, + mAppForegroundHelper.getImportance(uid)); + long identity = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - LocationStatsEnums.API_REQUEST_GEOFENCE, - packageName, - request, - /* hasListener= */ false, - true, - geofence, - mActivityManager.getPackageImportance(packageName)); - } - mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel, uid, packageName, featureId, listenerIdentifier); } finally { @@ -2282,20 +2245,19 @@ public class LocationManagerService extends ILocationManager.Stub { if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_ENDED, + LocationStatsEnums.API_REQUEST_GEOFENCE, + packageName, + /* LocationRequest= */ null, + /* hasListener= */ false, + true, + geofence, + mAppForegroundHelper.getImportance(Binder.getCallingUid())); + // geo-fence manager uses the public location API, need to clear identity long identity = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_ENDED, - LocationStatsEnums.API_REQUEST_GEOFENCE, - packageName, - /* LocationRequest= */ null, - /* hasListener= */ false, - true, - geofence, - mActivityManager.getPackageImportance(packageName)); - } mGeofenceManager.removeFence(geofence, intent); } finally { Binder.restoreCallingIdentity(identity); @@ -2391,12 +2353,6 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - @Override - public boolean sendNiResponse(int notifId, int userResponse) { - return mGnssManagerService != null && mGnssManagerService.sendNiResponse(notifId, - userResponse); - } - @Override public ProviderProperties getProviderProperties(String providerName) { LocationProviderManager manager = getLocationProviderManager(providerName); @@ -2408,20 +2364,13 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean isProviderPackage(String packageName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, - Manifest.permission.READ_DEVICE_CONFIG + " permission required"); - for (LocationProviderManager manager : mProviderManagers) { - if (manager.getPackages().contains(packageName)) { - return true; - } - } - return false; + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null); + return mLocalService.isProviderPackage(packageName); } @Override public List getProviderPackages(String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, - Manifest.permission.READ_DEVICE_CONFIG + " permission required"); + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null); LocationProviderManager manager = getLocationProviderManager(providerName); return manager == null ? Collections.emptyList() : new ArrayList<>(manager.getPackages()); } @@ -2461,28 +2410,19 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean isLocationEnabledForUser(int userId) { - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.INTERACT_ACROSS_USERS, - "Requires INTERACT_ACROSS_USERS permission"); + mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, + null); } - long identity = Binder.clearCallingIdentity(); - try { - return mSettingsStore.isLocationEnabled(userId); - } finally { - Binder.restoreCallingIdentity(identity); - } + return mSettingsHelper.isLocationEnabled(userId); } @Override public boolean isProviderEnabledForUser(String providerName, int userId) { - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.INTERACT_ACROSS_USERS, - "Requires INTERACT_ACROSS_USERS permission"); + mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, + null); } // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, @@ -2593,12 +2533,12 @@ public class LocationManagerService extends ILocationManager.Stub { Receiver receiver = r.mReceiver; boolean receiverDead = false; - if (!manager.isEnabled() && !isSettingsExemptLocked(r)) { + if (!manager.isEnabled() && !isSettingsExempt(r)) { continue; } int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!mUserInfoStore.isCurrentUserOrProfile(receiverUserId) + if (!mUserInfoHelper.isCurrentUserOrProfile(receiverUserId) && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) { if (D) { Log.d(TAG, "skipping loc update for background user " + receiverUserId + @@ -2607,7 +2547,7 @@ public class LocationManagerService extends ILocationManager.Stub { continue; } - if (mSettingsStore.isLocationPackageBlacklisted(receiverUserId, + if (mSettingsHelper.isLocationPackageBlacklisted(receiverUserId, receiver.mCallerIdentity.mPackageName)) { if (D) { Log.d(TAG, "skipping loc update for blacklisted app: " + @@ -2855,12 +2795,12 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("User Info:"); ipw.increaseIndent(); - mUserInfoStore.dump(fd, ipw, args); + mUserInfoHelper.dump(fd, ipw, args); ipw.decreaseIndent(); ipw.println("Location Settings:"); ipw.increaseIndent(); - mSettingsStore.dump(fd, ipw, args); + mSettingsHelper.dump(fd, ipw, args); ipw.decreaseIndent(); ipw.println("Battery Saver Location Mode: " @@ -2960,5 +2900,22 @@ public class LocationManagerService extends ILocationManager.Stub { } } } + + @Override + public boolean isProviderPackage(String packageName) { + for (LocationProviderManager manager : mProviderManagers) { + if (manager.getPackages().contains(packageName)) { + return true; + } + } + return false; + } + + @Override + public void sendNiResponse(int notifId, int userResponse) { + if (mGnssManagerService != null) { + mGnssManagerService.sendNiResponse(notifId, userResponse); + } + } } } diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java index 9c8ac19cc591b..372e91e417720 100644 --- a/services/core/java/com/android/server/LocationManagerServiceUtils.java +++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java @@ -17,8 +17,6 @@ package com.android.server; import android.annotation.NonNull; -import android.app.ActivityManager; -import android.content.Context; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -91,12 +89,8 @@ public class LocationManagerServiceUtils { /** * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death. - * - * @param binder that calls listener upon death - * @return true if listener is successfully linked to binder, false otherwise */ - public boolean linkToListenerDeathNotificationLocked( - IBinder binder) { + public boolean linkToListenerDeathNotificationLocked(IBinder binder) { try { binder.linkToDeath(this, 0 /* flags */); return true; @@ -110,54 +104,13 @@ public class LocationManagerServiceUtils { /** * Unlink death listener (i.e. callback) from binder. - * - * @param binder that calls listener upon death - * @return true if binder is successfully unlinked from binder, false otherwise */ - public boolean unlinkFromListenerDeathNotificationLocked( - IBinder binder) { + public void unlinkFromListenerDeathNotificationLocked(IBinder binder) { try { binder.unlinkToDeath(this, 0 /* flags */); - return true; } catch (NoSuchElementException e) { - // if the death callback isn't connected (it should be...), log error, - // swallow the exception and return Log.w(TAG, "Could not unlink " + mListenerName + " death callback.", e); - return false; } } - - } - - /** - * Convert boolean foreground into "foreground" or "background" string. - * - * @param foreground boolean indicating foreground - * @return "foreground" string if true, false otherwise - */ - public static String foregroundAsString(boolean foreground) { - return foreground ? "foreground" : "background"; - } - - - /** - * Classifies importance level as foreground or not. - * - * @param importance level as int - * @return boolean indicating if importance level is foreground or greater - */ - public static boolean isImportanceForeground(int importance) { - return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; - } - - /** - * Get package importance level. - * - * @param packageName package name - * @return package importance level as int - */ - public static int getPackageImportance(String packageName, Context context) { - return ((ActivityManager) context.getSystemService( - Context.ACTIVITY_SERVICE)).getPackageImportance(packageName); } } diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index 5afa48a2b34d3..64bca78f294d1 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -21,6 +21,7 @@ import android.content.Context; import android.location.Location; import android.os.Binder; import android.os.Bundle; +import android.util.ArraySet; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; @@ -120,7 +121,8 @@ public abstract class AbstractLocationProvider { if (providerPackageNames.equals(this.providerPackageNames)) { return this; } else { - return new State(allowed, properties, providerPackageNames); + return new State(allowed, properties, + Collections.unmodifiableSet(new ArraySet<>(providerPackageNames))); } } diff --git a/services/core/java/com/android/server/location/AppForegroundHelper.java b/services/core/java/com/android/server/location/AppForegroundHelper.java new file mode 100644 index 0000000000000..8ddfc652d333c --- /dev/null +++ b/services/core/java/com/android/server/location/AppForegroundHelper.java @@ -0,0 +1,131 @@ +/* + * 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.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.RunningAppProcessInfo.Importance; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.content.Context; +import android.os.Binder; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; +import com.android.server.FgThread; + +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Provides accessors and listeners for all application foreground status. An application is + * considered foreground if it's uid's importance level is at or more important than + * {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_FOREGROUND_SERVICE}. + */ +public class AppForegroundHelper { + + /** + * Listener for application foreground state changes. + */ + public interface AppForegroundListener { + /** + * Called when an application's foreground state changes. + */ + void onAppForegroundChanged(int uid, boolean foreground); + } + + // importance constants decrement with increasing importance - this is our limit for an + // importance level we consider foreground. + private static final int FOREGROUND_IMPORTANCE_CUTOFF = IMPORTANCE_FOREGROUND_SERVICE; + + private static boolean isForeground(@Importance int importance) { + return importance <= FOREGROUND_IMPORTANCE_CUTOFF; + } + + private final Context mContext; + private final CopyOnWriteArrayList mListeners; + + @GuardedBy("this") + @Nullable private ActivityManager mActivityManager; + + public AppForegroundHelper(Context context) { + mContext = context; + mListeners = new CopyOnWriteArrayList<>(); + } + + /** Called when system is ready. */ + public synchronized void onSystemReady() { + if (mActivityManager != null) { + return; + } + + mActivityManager = Objects.requireNonNull(mContext.getSystemService(ActivityManager.class)); + mActivityManager.addOnUidImportanceListener(this::onAppForegroundChanged, + FOREGROUND_IMPORTANCE_CUTOFF); + } + + /** + * Adds a listener for app foreground changed events. Callbacks occur on an unspecified thread. + */ + public void addListener(AppForegroundListener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener for app foreground changed events. + */ + public void removeListener(AppForegroundListener listener) { + mListeners.remove(listener); + } + + private void onAppForegroundChanged(int uid, @Importance int importance) { + // invoked on ui thread, move to fg thread so we don't block the ui thread + boolean foreground = isForeground(importance); + FgThread.getHandler().post(() -> { + for (AppForegroundListener listener : mListeners) { + listener.onAppForegroundChanged(uid, foreground); + } + }); + } + + /** + * Whether the given uid is currently foreground. + */ + public boolean isAppForeground(int uid) { + return isForeground(getImportance(uid)); + } + + /** + * Retrieves the current importance of the given uid. + * + * @deprecated Prefer {@link #isAppForeground(int)}. + */ + @Deprecated + @Importance + public int getImportance(int uid) { + synchronized (this) { + Preconditions.checkState(mActivityManager != null); + } + + long identity = Binder.clearCallingIdentity(); + try { + return mActivityManager.getUidImportance(uid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } +} diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java index 81c06d7125f92..4e9c06761e548 100644 --- a/services/core/java/com/android/server/location/GeofenceManager.java +++ b/services/core/java/com/android/server/location/GeofenceManager.java @@ -77,7 +77,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish private final AppOpsManager mAppOps; private final PowerManager.WakeLock mWakeLock; - private final LocationSettingsStore mSettingsStore; + private final SettingsHelper mSettingsStore; private final Object mLock = new Object(); @@ -111,7 +111,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish */ private boolean mPendingUpdate; - public GeofenceManager(Context context, LocationSettingsStore settingsStore) { + public GeofenceManager(Context context, SettingsHelper settingsStore) { mContext = context; mHandler = new GeofenceHandler(FgThread.getHandler().getLooper()); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 306e1e3afcd7b..62fa5ec82aea0 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -43,7 +43,6 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; @@ -76,6 +75,7 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.server.DeviceIdleInternal; +import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; @@ -624,12 +624,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } - public GnssLocationProvider(Context context, Handler handler) { - super(context, new HandlerExecutor(handler)); + public GnssLocationProvider(Context context) { + super(context, FgThread.getExecutor()); ensureInitialized(); - mLooper = handler.getLooper(); + mLooper = FgThread.getHandler().getLooper(); // Create a wake lock mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/SettingsHelper.java similarity index 88% rename from services/core/java/com/android/server/location/LocationSettingsStore.java rename to services/core/java/com/android/server/location/SettingsHelper.java index 0e8720ebb08ff..9163490fa777c 100644 --- a/services/core/java/com/android/server/location/LocationSettingsStore.java +++ b/services/core/java/com/android/server/location/SettingsHelper.java @@ -54,7 +54,7 @@ import java.util.function.Supplier; /** * Provides accessors and listeners for all location related settings. */ -public class LocationSettingsStore { +public class SettingsHelper { /** * Listener for user-specific settings changes. @@ -99,7 +99,7 @@ public class LocationSettingsStore { private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist; // TODO: get rid of handler - public LocationSettingsStore(Context context, Handler handler) { + public SettingsHelper(Context context, Handler handler) { mContext = context; mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler); @@ -118,7 +118,7 @@ public class LocationSettingsStore { } /** Called when system is ready. */ - public synchronized void onSystemReady() { + public void onSystemReady() { mLocationMode.register(); mBackgroundThrottleIntervalMs.register(); mLocationPackageBlacklist.register(); @@ -135,7 +135,8 @@ public class LocationSettingsStore { } /** - * Add a listener for changes to the location enabled setting. + * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified + * thread. */ public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) { mLocationMode.addListener(listener); @@ -156,7 +157,8 @@ public class LocationSettingsStore { } /** - * Add a listener for changes to the background throttle interval. + * Add a listener for changes to the background throttle interval. Callbacks occur on an + * unspecified thread. */ public void addOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener) { @@ -204,7 +206,8 @@ public class LocationSettingsStore { } /** - * Add a listener for changes to the background throttle package whitelist. + * Add a listener for changes to the background throttle package whitelist. Callbacks occur on + * an unspecified thread. */ public void addOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener) { @@ -227,7 +230,8 @@ public class LocationSettingsStore { } /** - * Add a listener for changes to the ignore settings package whitelist. + * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an + * unspecified thread. */ public void addOnIgnoreSettingsPackageWhitelistChangedListener( GlobalSettingChangedListener listener) { @@ -340,6 +344,8 @@ public class LocationSettingsStore { private abstract static class ObservingSetting extends ContentObserver { private final CopyOnWriteArrayList mListeners; + + @GuardedBy("this") private boolean mRegistered; private ObservingSetting(Handler handler) { @@ -347,11 +353,11 @@ public class LocationSettingsStore { mListeners = new CopyOnWriteArrayList<>(); } - protected boolean isRegistered() { + protected synchronized boolean isRegistered() { return mRegistered; } - protected void register(Context context, Uri uri) { + protected synchronized void register(Context context, Uri uri) { if (mRegistered) { return; } @@ -393,8 +399,13 @@ public class LocationSettingsStore { } public int getValueForUser(int defaultValue, int userId) { - return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName, - defaultValue, userId); + long identity = Binder.clearCallingIdentity(); + try { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName, + defaultValue, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -417,7 +428,7 @@ public class LocationSettingsStore { mCachedUserId = UserHandle.USER_NULL; } - public synchronized void register() { + public void register() { register(mContext, Settings.Secure.getUriFor(mSettingName)); } @@ -426,12 +437,17 @@ public class LocationSettingsStore { List value = mCachedValue; if (userId != mCachedUserId) { - String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), - mSettingName, userId); - if (TextUtils.isEmpty(setting)) { - value = Collections.emptyList(); - } else { - value = Arrays.asList(setting.split(",")); + long identity = Binder.clearCallingIdentity(); + try { + String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), + mSettingName, userId); + if (TextUtils.isEmpty(setting)) { + value = Collections.emptyList(); + } else { + value = Arrays.asList(setting.split(",")); + } + } finally { + Binder.restoreCallingIdentity(identity); } if (isRegistered()) { @@ -473,8 +489,13 @@ public class LocationSettingsStore { } public long getValue(long defaultValue) { - return Settings.Global.getLong(mContext.getContentResolver(), mSettingName, - defaultValue); + long identity = Binder.clearCallingIdentity(); + try { + return Settings.Global.getLong(mContext.getContentResolver(), mSettingName, + defaultValue); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -499,18 +520,23 @@ public class LocationSettingsStore { mValid = false; } - public synchronized void register() { + public void register() { register(mContext, Settings.Global.getUriFor(mSettingName)); } public synchronized Set getValue() { ArraySet value = mCachedValue; if (!mValid) { - value = new ArraySet<>(mBaseValuesSupplier.get()); - String setting = Settings.Global.getString(mContext.getContentResolver(), - mSettingName); - if (!TextUtils.isEmpty(setting)) { - value.addAll(Arrays.asList(setting.split(","))); + long identity = Binder.clearCallingIdentity(); + try { + value = new ArraySet<>(mBaseValuesSupplier.get()); + String setting = Settings.Global.getString(mContext.getContentResolver(), + mSettingName); + if (!TextUtils.isEmpty(setting)) { + value.addAll(Arrays.asList(setting.split(","))); + } + } finally { + Binder.restoreCallingIdentity(identity); } if (isRegistered()) { diff --git a/services/core/java/com/android/server/location/UserInfoStore.java b/services/core/java/com/android/server/location/UserInfoHelper.java similarity index 70% rename from services/core/java/com/android/server/location/UserInfoStore.java rename to services/core/java/com/android/server/location/UserInfoHelper.java index f282ed26a8319..94f3a88cb8596 100644 --- a/services/core/java/com/android/server/location/UserInfoStore.java +++ b/services/core/java/com/android/server/location/UserInfoHelper.java @@ -42,7 +42,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * Provides accessors and listeners for all user info. */ -public class UserInfoStore { +public class UserInfoHelper { /** * Listener for current user changes. @@ -58,20 +58,16 @@ public class UserInfoStore { private final CopyOnWriteArrayList mListeners; @GuardedBy("this") - @Nullable - private UserManager mUserManager; + @Nullable private UserManager mUserManager; + + @UserIdInt private volatile int mCurrentUserId; @GuardedBy("this") - @UserIdInt - private int mCurrentUserId; - - @GuardedBy("this") - @UserIdInt - private int mCachedParentUserId; + @UserIdInt private int mCachedParentUserId; @GuardedBy("this") private int[] mCachedProfileUserIds; - public UserInfoStore(Context context) { + public UserInfoHelper(Context context) { mContext = context; mListeners = new CopyOnWriteArrayList<>(); @@ -120,7 +116,7 @@ public class UserInfoStore { } /** - * Adds a listener for user changed events. + * Adds a listener for user changed events. Callbacks occur on an unspecified thread. */ public void addListener(UserChangedListener listener) { mListeners.add(listener); @@ -134,16 +130,13 @@ public class UserInfoStore { } private void onUserChanged(@UserIdInt int newUserId) { - int oldUserId; - synchronized (this) { - if (newUserId == mCurrentUserId) { - return; - } - - oldUserId = mCurrentUserId; - mCurrentUserId = newUserId; + if (newUserId == mCurrentUserId) { + return; } + int oldUserId = mCurrentUserId; + mCurrentUserId = newUserId; + for (UserChangedListener listener : mListeners) { listener.onUserChanged(oldUserId, newUserId); } @@ -161,7 +154,7 @@ public class UserInfoStore { * Returns the user id of the current user. */ @UserIdInt - public synchronized int getCurrentUserId() { + public int getCurrentUserId() { return mCurrentUserId; } @@ -169,9 +162,10 @@ public class UserInfoStore { * Returns true if the given user id is either the current user or a profile of the current * user. */ - public synchronized boolean isCurrentUserOrProfile(@UserIdInt int userId) { - return userId == mCurrentUserId || ArrayUtils.contains( - getProfileUserIdsForParentUser(mCurrentUserId), userId); + public boolean isCurrentUserOrProfile(@UserIdInt int userId) { + int currentUserId = mCurrentUserId; + return userId == currentUserId || ArrayUtils.contains( + getProfileUserIdsForParentUser(currentUserId), userId); } /** @@ -179,50 +173,44 @@ public class UserInfoStore { * is a parent or has no profiles. */ @UserIdInt - public synchronized int getParentUserId(@UserIdInt int userId) { - int parentUserId; - if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, userId)) { - parentUserId = mCachedParentUserId; - } else { - Preconditions.checkState(mUserManager != null); - - long identity = Binder.clearCallingIdentity(); - try { - UserInfo userInfo = mUserManager.getProfileParent(userId); - if (userInfo != null) { - parentUserId = userInfo.id; - } else { - // getProfileParent() returns null if the userId is already the parent... - parentUserId = userId; - } - - // force profiles into cache - getProfileUserIdsForParentUser(parentUserId); - } finally { - Binder.restoreCallingIdentity(identity); + public int getParentUserId(@UserIdInt int userId) { + synchronized (this) { + if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, + userId)) { + return mCachedParentUserId; } + + Preconditions.checkState(mUserManager != null); } + int parentUserId; + + long identity = Binder.clearCallingIdentity(); + try { + UserInfo userInfo = mUserManager.getProfileParent(userId); + parentUserId = userInfo != null ? userInfo.id : userId; + } finally { + Binder.restoreCallingIdentity(identity); + } + + // force profiles into cache + getProfileUserIdsForParentUser(parentUserId); return parentUserId; } @GuardedBy("this") - private int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) { - Preconditions.checkState(mUserManager != null); - - // only assert on debug builds as this is a more expensive check - if (Build.IS_DEBUGGABLE) { - long identity = Binder.clearCallingIdentity(); - try { - Preconditions.checkArgument(mUserManager.getProfileParent(parentUserId) == null); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - + private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) { if (parentUserId != mCachedParentUserId) { long identity = Binder.clearCallingIdentity(); try { + Preconditions.checkState(mUserManager != null); + + // more expensive check - check that argument really is a parent user id + if (Build.IS_DEBUGGABLE) { + Preconditions.checkArgument( + mUserManager.getProfileParent(parentUserId) == null); + } + mCachedParentUserId = parentUserId; mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId); } finally { @@ -236,8 +224,9 @@ public class UserInfoStore { /** * Dump info for debugging. */ - public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Current User: " + mCurrentUserId + " " + Arrays.toString( - getProfileUserIdsForParentUser(mCurrentUserId))); + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + int currentUserId = mCurrentUserId; + pw.println("Current User: " + currentUserId + " " + Arrays.toString( + getProfileUserIdsForParentUser(currentUserId))); } } 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 1eb2c525ff226..2bab9fa67eb00 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -16,10 +16,11 @@ package com.android.server.location.gnss; +import static android.app.AppOpsManager.OP_FINE_LOCATION; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.location.GnssCapabilities; @@ -31,8 +32,8 @@ import android.location.IGnssStatusListener; import android.location.IGpsGeofenceHardware; import android.location.INetInitiatedListener; import android.location.Location; +import android.location.LocationManagerInternal; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Process; @@ -43,13 +44,12 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.FgThread; -import com.android.server.LocationManagerService; -import com.android.server.LocationManagerServiceUtils; +import com.android.internal.util.Preconditions; +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.CallerIdentity; import com.android.server.location.GnssBatchingProvider; import com.android.server.location.GnssCapabilitiesProvider; @@ -60,6 +60,7 @@ import com.android.server.location.GnssNavigationMessageProvider; import com.android.server.location.GnssStatusListenerHelper; import com.android.server.location.LocationUsageLogger; import com.android.server.location.RemoteListenerHelper; +import com.android.server.location.SettingsHelper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -70,10 +71,18 @@ import java.util.function.Function; /** Manages Gnss providers and related Gnss functions for LocationManagerService. */ public class GnssManagerService { - private static final String TAG = "GnssManagerService"; - private static final boolean D = Log.isLoggable(TAG, Log.DEBUG); - // Providers + private static final String TAG = "GnssManagerService"; + + public static boolean isGnssSupported() { + return GnssLocationProvider.isSupported(); + } + + private final Context mContext; + private final SettingsHelper mSettingsHelper; + private final AppForegroundHelper mAppForegroundHelper; + private final LocationUsageLogger mLocationUsageLogger; + private final GnssLocationProvider mGnssLocationProvider; private final GnssStatusListenerHelper mGnssStatusProvider; private final GnssMeasurementsProvider mGnssMeasurementsProvider; @@ -83,58 +92,59 @@ public class GnssManagerService { private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider; private final GnssCapabilitiesProvider mGnssCapabilitiesProvider; private final GnssBatchingProvider mGnssBatchingProvider; - private final INetInitiatedListener mNetInitiatedListener; private final IGpsGeofenceHardware mGpsGeofenceProxy; - private final LocationManagerService mLocationManagerService; - private final LocationUsageLogger mLocationUsageLogger; @GuardedBy("mGnssMeasurementsListeners") - private final ArrayMap> + private final ArrayMap> mGnssMeasurementsListeners = new ArrayMap<>(); @GuardedBy("mGnssNavigationMessageListeners") - private final ArrayMap< - IBinder, LinkedListener> + private final ArrayMap> mGnssNavigationMessageListeners = new ArrayMap<>(); @GuardedBy("mGnssStatusListeners") private final ArrayMap> mGnssStatusListeners = new ArrayMap<>(); - @GuardedBy("mGnssBatchingLock") - private IBatchedLocationCallback mGnssBatchingCallback; + @GuardedBy("this") + @Nullable private LocationManagerInternal mLocationManagerInternal; + @GuardedBy("this") + @Nullable private AppOpsManager mAppOpsManager; + + private final Object mGnssBatchingLock = new Object(); @GuardedBy("mGnssBatchingLock") - private LinkedListener - mGnssBatchingDeathCallback; + @Nullable private IBatchedLocationCallback mGnssBatchingCallback; + + @GuardedBy("mGnssBatchingLock") + @Nullable private LinkedListener mGnssBatchingDeathCallback; @GuardedBy("mGnssBatchingLock") private boolean mGnssBatchingInProgress = false; - private final Object mGnssBatchingLock = new Object(); - private final Context mContext; - private final Handler mHandler; - - public GnssManagerService(LocationManagerService locationManagerService, - Context context, LocationUsageLogger locationUsageLogger) { - this(locationManagerService, context, - new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger); + public GnssManagerService(Context context, SettingsHelper settingsHelper, + AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger) { + this(context, settingsHelper, appForegroundHelper, locationUsageLogger, null); } // Can use this constructor to inject GnssLocationProvider for testing @VisibleForTesting - public GnssManagerService(LocationManagerService locationManagerService, - Context context, - GnssLocationProvider gnssLocationProvider, - LocationUsageLogger locationUsageLogger) { + GnssManagerService(Context context, SettingsHelper settingsHelper, + AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger, + GnssLocationProvider gnssLocationProvider) { + Preconditions.checkState(isGnssSupported()); + mContext = context; - mHandler = FgThread.getHandler(); + mSettingsHelper = settingsHelper; + mAppForegroundHelper = appForegroundHelper; + mLocationUsageLogger = locationUsageLogger; - mGnssLocationProvider = - gnssLocationProvider; + if (gnssLocationProvider == null) { + gnssLocationProvider = new GnssLocationProvider(mContext); + } + mGnssLocationProvider = gnssLocationProvider; mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider(); mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider(); mGnssMeasurementCorrectionsProvider = @@ -144,108 +154,73 @@ public class GnssManagerService { mGnssMetricsProvider = mGnssLocationProvider.getGnssMetricsProvider(); mGnssCapabilitiesProvider = mGnssLocationProvider.getGnssCapabilitiesProvider(); mGnssBatchingProvider = mGnssLocationProvider.getGnssBatchingProvider(); - mNetInitiatedListener = mGnssLocationProvider.getNetInitiatedListener(); mGpsGeofenceProxy = mGnssLocationProvider.getGpsGeofenceProxy(); - mLocationManagerService = locationManagerService; - mLocationUsageLogger = locationUsageLogger; - - registerUidListener(); } - public static boolean isGnssSupported() { - return GnssLocationProvider.isSupported(); - } - - private boolean hasGnssPermissions(String packageName) { - mContext.enforceCallingPermission( - Manifest.permission.ACCESS_FINE_LOCATION, - "Fine location permission not granted."); - - int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - try { - return mContext.getSystemService( - AppOpsManager.class).checkOp(AppOpsManager.OP_FINE_LOCATION, uid, packageName) - == AppOpsManager.MODE_ALLOWED; - } finally { - Binder.restoreCallingIdentity(identity); + /** Called when system is ready. */ + public synchronized void onSystemReady() { + if (mLocationManagerInternal != null) { + return; } + + mSettingsHelper.onSystemReady(); + mAppForegroundHelper.onSystemReady(); + + mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + + mAppForegroundHelper.addListener(this::onAppForegroundChanged); } + /** Retrieve the GnssLocationProvider. */ public GnssLocationProvider getGnssLocationProvider() { return mGnssLocationProvider; } + /** Retrieve the IGpsGeofenceHardware. */ public IGpsGeofenceHardware getGpsGeofenceProxy() { return mGpsGeofenceProxy; } /** * Get year of GNSS hardware. - * - * @return year of GNSS hardware as an int if possible, otherwise zero */ public int getGnssYearOfHardware() { - if (mGnssSystemInfoProvider != null) { - return mGnssSystemInfoProvider.getGnssYearOfHardware(); - } else { - return 0; - } + return mGnssSystemInfoProvider.getGnssYearOfHardware(); } /** * Get model name of GNSS hardware. - * - * @return GNSS hardware model name as a string if possible, otherwise null */ + @Nullable public String getGnssHardwareModelName() { - if (mGnssSystemInfoProvider != null) { - return mGnssSystemInfoProvider.getGnssHardwareModelName(); - } else { - return null; - } + return mGnssSystemInfoProvider.getGnssHardwareModelName(); } /** - * Get GNSS hardware capabilities. The capabilities are described in {@link - * android.location.GnssCapabilities} and their integer values correspond to the - * bit positions in the returned {@code long} value. - * - * @param packageName name of requesting package - * @return capabilities supported by the GNSS chipset + * Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in + * {@link android.location.GnssCapabilities}. */ public long getGnssCapabilities(String packageName) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to obtain GNSS chipset capabilities."); - if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) { + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); + + if (!checkLocationAppOp(packageName)) { return GnssCapabilities.INVALID_CAPABILITIES; } + return mGnssCapabilitiesProvider.getGnssCapabilities(); } /** * Get size of GNSS batch (GNSS location results are batched together for power savings). - * Requires LOCATION_HARDWARE and GNSS permissions. - * - * @param packageName name of requesting package - * @return size of the GNSS batch collection */ public int getGnssBatchSize(String packageName) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "getGnssBatchSize called without GNSS permissions"); - return 0; - } - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not get GNSS batch size. GNSS batching provider " - + "not available."); + if (!checkLocationAppOp(packageName)) { return 0; } @@ -257,27 +232,12 @@ public class GnssManagerService { /** * Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered * as a collection. - * - * @param periodNanos duration over which to collect GPS positions before delivering as a - * batch - * @param wakeOnFifoFull specifying whether to wake on full queue - * @param packageName name of requesting package - * @return true of batch started successfully, false otherwise */ public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "startGnssBatch called without GNSS permissions"); - return false; - } - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not start GNSS batching. GNSS batching provider " - + "not available."); + if (!checkLocationAppOp(packageName)) { return false; } @@ -285,7 +245,6 @@ public class GnssManagerService { if (mGnssBatchingInProgress) { // Current design does not expect multiple starts to be called repeatedly Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch"); - // Try to clean up anyway, and continue stopGnssBatch(); } @@ -296,26 +255,13 @@ public class GnssManagerService { /** * Adds a GNSS batching callback for delivering GNSS location batch results. - * - * @param callback called when batching operation is complete to deliver GPS positions - * @param packageName name of requesting package - * @return true if callback is successfully added, false otherwise */ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName, @Nullable String featureId, @NonNull String listenerIdentity) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "addGnssBatchingCallback called without GNSS permissions"); - return false; - } - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not add GNSS batching callback. GNSS batching provider " - + "not available."); + if (!checkLocationAppOp(packageName)) { return false; } @@ -333,11 +279,9 @@ public class GnssManagerService { stopGnssBatch(); removeGnssBatchingCallback(); }); - if (!mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked( - callback.asBinder())) { - return false; - } - return true; + + return mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked( + callback.asBinder()); } } @@ -347,27 +291,14 @@ public class GnssManagerService { * @param packageName name of requesting package */ public void flushGnssBatch(String packageName) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "flushGnssBatch called without GNSS permissions"); - return; - } - - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not flush GNSS batch. GNSS batching provider " - + "not available."); + if (!checkLocationAppOp(packageName)) { return; } synchronized (mGnssBatchingLock) { - if (!mGnssBatchingInProgress) { - Log.w(TAG, "flushGnssBatch called with no batch in progress"); - } mGnssBatchingProvider.flush(); } } @@ -376,17 +307,7 @@ public class GnssManagerService { * Removes GNSS batching callback. */ public void removeGnssBatchingCallback() { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); - - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not add GNSS batching callback. GNSS batching provider " - + "not available."); - return; - } + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null); synchronized (mGnssBatchingLock) { mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked( @@ -398,44 +319,17 @@ public class GnssManagerService { /** * Stop GNSS batch collection. - * - * @return true if GNSS batch successfully stopped, false otherwise */ public boolean stopGnssBatch() { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware batching"); + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null); - if (mGnssBatchingProvider == null) { - Log.e( - TAG, - "Can not stop GNSS batch. GNSS batching provider " - + "not available."); - return false; - } synchronized (mGnssBatchingLock) { mGnssBatchingInProgress = false; return mGnssBatchingProvider.stop(); } } - private void registerUidListener() { - mContext.getSystemService( - ActivityManager.class).addOnUidImportanceListener( - (uid, importance) -> { - // listener invoked on ui thread, move to our thread to reduce risk - // of blocking ui thread - mHandler.post( - () -> { - onForegroundChanged(uid, - LocationManagerServiceUtils.isImportanceForeground( - importance)); - }); - }, - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); - } - - private void onForegroundChanged(int uid, boolean foreground) { + private void onAppForegroundChanged(int uid, boolean foreground) { synchronized (mGnssMeasurementsListeners) { updateListenersOnForegroundChangedLocked( mGnssMeasurementsListeners, @@ -463,8 +357,7 @@ public class GnssManagerService { } private void updateListenersOnForegroundChangedLocked( - ArrayMap - gnssDataListeners, + Map gnssDataListeners, RemoteListenerHelper gnssDataProvider, Function mapBinderToListener, int uid, @@ -477,18 +370,8 @@ public class GnssManagerService { continue; } - if (D) { - Log.d( - TAG, - linkedListener.getListenerName() - + " from uid " - + uid - + " is now " - + LocationManagerServiceUtils.foregroundAsString(foreground)); - } - TListener listener = mapBinderToListener.apply(entry.getKey()); - if (foreground || mLocationManagerService.isThrottlingExemptLocked(callerIdentity)) { + if (foreground || isThrottlingExempt(callerIdentity)) { gnssDataProvider.addListener(listener, callerIdentity); } else { gnssDataProvider.removeListener(listener); @@ -502,67 +385,49 @@ public class GnssManagerService { @Nullable String featureId, @NonNull String listenerIdentifier, RemoteListenerHelper gnssDataProvider, - ArrayMap> gnssDataListeners, + ArrayMap> gnssDataListeners, Consumer binderDeathCallback) { - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "addGnssDataListenerLocked called without GNSS permissions"); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); + + if (!checkLocationAppOp(packageName)) { return false; } - if (gnssDataProvider == null) { - Log.e( - TAG, - "Can not add GNSS data listener. GNSS data provider " - + "not available."); - return false; - } - - CallerIdentity callerIdentity = - new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, - featureId, listenerIdentifier); - LinkedListener linkedListener = - new LocationManagerServiceUtils.LinkedListener<>( - listener, listenerIdentifier, callerIdentity, binderDeathCallback); + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName, featureId, listenerIdentifier); + LinkedListener linkedListener = new LinkedListener<>(listener, + listenerIdentifier, callerIdentity, binderDeathCallback); IBinder binder = listener.asBinder(); if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) { return false; } gnssDataListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (gnssDataProvider == mGnssMeasurementsProvider - || gnssDataProvider == mGnssStatusProvider) { - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - gnssDataProvider == mGnssMeasurementsProvider - ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER - : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, - packageName, - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, - LocationManagerServiceUtils.getPackageImportance(packageName, - mContext)); - } - if (mLocationManagerService.isThrottlingExemptLocked(callerIdentity) - || LocationManagerServiceUtils.isImportanceForeground( - LocationManagerServiceUtils.getPackageImportance(packageName, mContext))) { - gnssDataProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); + if (gnssDataProvider == mGnssMeasurementsProvider + || gnssDataProvider == mGnssStatusProvider) { + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + gnssDataProvider == mGnssMeasurementsProvider + ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER + : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, + packageName, + /* LocationRequest= */ null, + /* hasListener= */ true, + /* hasIntent= */ false, + /* geofence= */ null, + mAppForegroundHelper.getImportance(callerIdentity.mUid)); } + if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid) + || isThrottlingExempt(callerIdentity)) { + gnssDataProvider.addListener(listener, callerIdentity); + } + return true; } - private void removeGnssDataListener( + private void removeGnssDataListenerLocked( TListener listener, RemoteListenerHelper gnssDataProvider, - ArrayMap> gnssDataListeners) { + ArrayMap> gnssDataListeners) { if (gnssDataProvider == null) { Log.e( TAG, @@ -577,25 +442,19 @@ public class GnssManagerService { if (linkedListener == null) { return; } - long identity = Binder.clearCallingIdentity(); - try { - if (gnssDataProvider == mGnssMeasurementsProvider - || gnssDataProvider == mGnssStatusProvider) { - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_ENDED, - gnssDataProvider == mGnssMeasurementsProvider - ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER - : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, - linkedListener.getCallerIdentity().mPackageName, - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, - LocationManagerServiceUtils.getPackageImportance( - linkedListener.getCallerIdentity().mPackageName, mContext)); - } - } finally { - Binder.restoreCallingIdentity(identity); + if (gnssDataProvider == mGnssMeasurementsProvider + || gnssDataProvider == mGnssStatusProvider) { + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_ENDED, + gnssDataProvider == mGnssMeasurementsProvider + ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER + : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, + linkedListener.getCallerIdentity().mPackageName, + /* LocationRequest= */ null, + /* hasListener= */ true, + /* hasIntent= */ false, + /* geofence= */ null, + mAppForegroundHelper.getImportance(Binder.getCallingUid())); } linkedListener.unlinkFromListenerDeathNotificationLocked(binder); gnssDataProvider.removeListener(listener); @@ -603,10 +462,6 @@ public class GnssManagerService { /** * Registers listener for GNSS status changes. - * - * @param listener called when GNSS status changes - * @param packageName name of requesting package - * @return true if listener is successfully registered, false otherwise */ public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName, @Nullable String featureId) { @@ -624,21 +479,15 @@ public class GnssManagerService { /** * Unregisters listener for GNSS status changes. - * - * @param listener called when GNSS status changes */ public void unregisterGnssStatusCallback(IGnssStatusListener listener) { synchronized (mGnssStatusListeners) { - removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners); + removeGnssDataListenerLocked(listener, mGnssStatusProvider, mGnssStatusListeners); } } /** * Adds a GNSS measurements listener. - * - * @param listener called when GNSS measurements are received - * @param packageName name of requesting package - * @return true if listener is successfully added, false otherwise */ public boolean addGnssMeasurementsListener( IGnssMeasurementsListener listener, String packageName, @Nullable String featureId, @@ -657,47 +506,32 @@ public class GnssManagerService { /** * Injects GNSS measurement corrections. - * - * @param measurementCorrections GNSS measurement corrections - * @param packageName name of requesting package */ public void injectGnssMeasurementCorrections( GnssMeasurementCorrections measurementCorrections, String packageName) { - mContext.enforceCallingPermission( - android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to inject GNSS measurement corrections."); - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "Can not inject GNSS corrections due to no permission."); - return; - } - if (mGnssMeasurementCorrectionsProvider == null) { - Log.e( - TAG, - "Can not inject GNSS corrections. GNSS measurement corrections provider " - + "not available."); + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); + + if (!checkLocationAppOp(packageName)) { return; } + mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections( measurementCorrections); } /** * Removes a GNSS measurements listener. - * - * @param listener called when GNSS measurements are received */ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { synchronized (mGnssMeasurementsListeners) { - removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners); + removeGnssDataListenerLocked(listener, mGnssMeasurementsProvider, + mGnssMeasurementsListeners); } } /** * Adds a GNSS navigation message listener. - * - * @param listener called when navigation message is received - * @param packageName name of requesting package - * @return true if listener is successfully added, false otherwise */ public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName, @@ -716,12 +550,10 @@ public class GnssManagerService { /** * Removes a GNSS navigation message listener. - * - * @param listener called when navigation message is received */ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { synchronized (mGnssNavigationMessageListeners) { - removeGnssDataListener( + removeGnssDataListenerLocked( listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners); } } @@ -729,43 +561,62 @@ public class GnssManagerService { /** * Send Ni Response, indicating a location request initiated by a network carrier. */ - public boolean sendNiResponse(int notifId, int userResponse) { - if (Binder.getCallingUid() != Process.myUid()) { - throw new SecurityException( - "calling sendNiResponse from outside of the system is not allowed"); - } + public void sendNiResponse(int notifId, int userResponse) { try { - return mNetInitiatedListener.sendNiResponse(notifId, userResponse); + mNetInitiatedListener.sendNiResponse(notifId, userResponse); } catch (RemoteException e) { Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse"); - return false; } } /** * Report location results to GNSS batching listener. - * - * @param locations batch of locations to report to GNSS batching callback */ public void onReportLocation(List locations) { - if (mGnssBatchingCallback == null) { - Log.e(TAG, "reportLocationBatch() called without active Callback"); + IBatchedLocationCallback gnssBatchingCallback; + synchronized (mGnssBatchingLock) { + gnssBatchingCallback = mGnssBatchingCallback; + } + + if (gnssBatchingCallback == null) { return; } try { - mGnssBatchingCallback.onLocationBatch(locations); + gnssBatchingCallback.onLocationBatch(locations); } catch (RemoteException e) { Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e); } } + private boolean isThrottlingExempt(CallerIdentity callerIdentity) { + if (callerIdentity.mUid == Process.SYSTEM_UID) { + return true; + } + + if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( + callerIdentity.mPackageName)) { + 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; + } + /** - * Dump for debugging. + * Dump info for debugging. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); if (args.length > 0 && args[0].equals("--gnssmetrics")) { @@ -778,11 +629,8 @@ public class GnssManagerService { ipw.println("GnssMeasurement Listeners:"); ipw.increaseIndent(); synchronized (mGnssMeasurementsListeners) { - for (LinkedListenerBase listener : - mGnssMeasurementsListeners - .values()) { - ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked( - listener.getCallerIdentity())); + for (LinkedListenerBase listener : mGnssMeasurementsListeners.values()) { + ipw.println(listener); } } ipw.decreaseIndent(); @@ -790,10 +638,8 @@ public class GnssManagerService { ipw.println("GnssNavigationMessage Listeners:"); ipw.increaseIndent(); synchronized (mGnssNavigationMessageListeners) { - for (LinkedListenerBase listener : - mGnssNavigationMessageListeners.values()) { - ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked( - listener.getCallerIdentity())); + for (LinkedListenerBase listener : mGnssNavigationMessageListeners.values()) { + ipw.println(listener); } } ipw.decreaseIndent(); @@ -801,10 +647,8 @@ public class GnssManagerService { ipw.println("GnssStatus Listeners:"); ipw.increaseIndent(); synchronized (mGnssStatusListeners) { - for (LinkedListenerBase listener : - mGnssStatusListeners.values()) { - ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked( - listener.getCallerIdentity())); + for (LinkedListenerBase listener : mGnssStatusListeners.values()) { + ipw.println(listener); } } ipw.decreaseIndent(); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java new file mode 100644 index 0000000000000..38ec4ce3e8270 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java @@ -0,0 +1,112 @@ +/* + * 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.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +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.ActivityManager; +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 AppForegroundHelperTest { + + private static final long TIMEOUT_MS = 5000; + + @Mock private Context mContext; + @Mock private ActivityManager mActivityManager; + + private List mListeners = new ArrayList<>(); + + private AppForegroundHelper mHelper; + + @Before + public void setUp() { + initMocks(this); + + doReturn(mActivityManager).when(mContext).getSystemService(ActivityManager.class); + doAnswer(invocation -> { + mListeners.add(invocation.getArgument(0)); + return null; + }).when(mActivityManager).addOnUidImportanceListener(any( + ActivityManager.OnUidImportanceListener.class), eq(IMPORTANCE_FOREGROUND_SERVICE)); + + mHelper = new AppForegroundHelper(mContext); + mHelper.onSystemReady(); + } + + private void setImportance(int uid, int importance) { + doReturn(importance).when(mActivityManager).getUidImportance(uid); + for (ActivityManager.OnUidImportanceListener listener : mListeners) { + listener.onUidImportance(uid, importance); + } + } + + @Test + public void testListeners() { + AppForegroundHelper.AppForegroundListener listener = mock( + AppForegroundHelper.AppForegroundListener.class); + mHelper.addListener(listener); + + setImportance(0, IMPORTANCE_FOREGROUND); + verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(0, true); + + setImportance(1, IMPORTANCE_FOREGROUND_SERVICE); + verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(1, true); + + setImportance(2, IMPORTANCE_VISIBLE); + verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(2, false); + } + + @Test + public void testIsAppForeground() { + setImportance(0, IMPORTANCE_FOREGROUND); + assertThat(mHelper.isAppForeground(0)).isEqualTo(true); + + setImportance(0, IMPORTANCE_FOREGROUND_SERVICE); + assertThat(mHelper.isAppForeground(0)).isEqualTo(true); + + setImportance(0, IMPORTANCE_VISIBLE); + assertThat(mHelper.isAppForeground(0)).isEqualTo(false); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java similarity index 73% rename from services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java rename to services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java index 06fb10257a379..389fdf9b0abc0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java @@ -57,7 +57,7 @@ import java.util.List; @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) -public class UserInfoStoreTest { +public class UserInfoHelperTest { private static final int USER1_ID = 1; private static final int USER1_MANAGED_ID = 11; @@ -72,7 +72,7 @@ public class UserInfoStoreTest { private StaticMockitoSession mMockingSession; private List mBroadcastReceivers = new ArrayList<>(); - private UserInfoStore mStore; + private UserInfoHelper mHelper; @Before public void setUp() { @@ -97,8 +97,8 @@ public class UserInfoStoreTest { doReturn(USER1_ID).when(ActivityManager::getCurrentUser); - mStore = new UserInfoStore(mContext); - mStore.onSystemReady(); + mHelper = new UserInfoHelper(mContext); + mHelper.onSystemReady(); } @After @@ -119,8 +119,9 @@ public class UserInfoStoreTest { @Test public void testListeners() { - UserInfoStore.UserChangedListener listener = mock(UserInfoStore.UserChangedListener.class); - mStore.addListener(listener); + UserInfoHelper.UserChangedListener listener = mock( + UserInfoHelper.UserChangedListener.class); + mHelper.addListener(listener); switchUser(USER1_ID); verify(listener, never()).onUserChanged(anyInt(), anyInt()); @@ -134,44 +135,44 @@ public class UserInfoStoreTest { @Test public void testCurrentUser() { - assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID); + assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID); switchUser(USER2_ID); - assertThat(mStore.getCurrentUserId()).isEqualTo(USER2_ID); + assertThat(mHelper.getCurrentUserId()).isEqualTo(USER2_ID); switchUser(USER1_ID); - assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID); + assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID); } @Test public void testIsCurrentUserOrProfile() { - assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isTrue(); - assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue(); - assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isFalse(); - assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse(); + assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isTrue(); + assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue(); + assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isFalse(); + assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse(); switchUser(USER2_ID); - assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isFalse(); - assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isTrue(); - assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse(); - assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue(); + assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isFalse(); + assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isTrue(); + assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse(); + assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue(); } @Test public void testGetParentUserId() { - assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); - assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); - assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); - assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); + assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); + assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); + assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); switchUser(USER2_ID); - assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); - assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); - assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); - assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); + assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); + assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); + assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); } } 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 f262733bb8828..f16cf35bfb34c 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 @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -31,7 +32,6 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.Manifest; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -46,13 +46,15 @@ import android.location.IGnssNavigationMessageListener; import android.location.IGnssStatusListener; import android.location.INetInitiatedListener; import android.location.Location; +import android.location.LocationManagerInternal; import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Message; import android.os.RemoteException; -import com.android.server.LocationManagerService; +import com.android.server.LocalServices; +import com.android.server.location.AppForegroundHelper; import com.android.server.location.GnssBatchingProvider; import com.android.server.location.GnssCapabilitiesProvider; import com.android.server.location.GnssLocationProvider; @@ -63,7 +65,9 @@ import com.android.server.location.GnssNavigationMessageProvider; import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative; import com.android.server.location.GnssStatusListenerHelper; import com.android.server.location.LocationUsageLogger; +import com.android.server.location.SettingsHelper; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.AdditionalMatchers; @@ -93,18 +97,20 @@ public class GnssManagerServiceTest { @Mock private GnssMeasurementCorrectionsProvider mMockGnssMeasurementCorrectionsProvider; @Mock - private INetInitiatedListener mMockNetInitiatedListener; + private INetInitiatedListener mNetInitiatedListener; private GnssMeasurementsProvider mTestGnssMeasurementsProvider; private GnssStatusListenerHelper mTestGnssStatusProvider; private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider; // Managers and services @Mock - private AppOpsManager mMockAppOpsManager; + private AppOpsManager mAppOpsManager; @Mock - private ActivityManager mMockActivityManager; + private SettingsHelper mSettingsHelper; @Mock - private LocationManagerService mMockLocationManagerService; + private AppForegroundHelper mAppForegroundHelper; + @Mock + private LocationManagerInternal mLocationManagerInternal; // Context and handler @Mock @@ -113,25 +119,23 @@ public class GnssManagerServiceTest { private Context mMockContext; // Class under test - private com.android.server.location.gnss.GnssManagerService mGnssManagerService; + private GnssManagerService mGnssManagerService; @Before public void setUp() { MockitoAnnotations.initMocks(this); GnssLocationProvider.setIsSupportedForTest(true); - // Set up mock context when(mMockContext.getSystemServiceName(AppOpsManager.class)).thenReturn( Context.APP_OPS_SERVICE); - when(mMockContext.getSystemServiceName(ActivityManager.class)).thenReturn( - Context.ACTIVITY_SERVICE); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn( - mMockAppOpsManager); - when(mMockContext.getSystemService( - eq(Context.ACTIVITY_SERVICE))).thenReturn( - mMockActivityManager); + mAppOpsManager); enableLocationPermissions(); + when(mAppForegroundHelper.isAppForeground(anyInt())).thenReturn(true); + + LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal); + // Mock Handler will execute posted runnables immediately when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer( (InvocationOnMock invocation) -> { @@ -164,15 +168,22 @@ public class GnssManagerServiceTest { when(mMockGnssLocationProvider.getGnssNavigationMessageProvider()).thenReturn( mTestGnssNavigationMessageProvider); when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn( - mMockNetInitiatedListener); + mNetInitiatedListener); // Setup GnssBatching provider when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true); when(mMockGnssBatchingProvider.stop()).thenReturn(true); // Create GnssManagerService - mGnssManagerService = new GnssManagerService(mMockLocationManagerService, mMockContext, - mMockGnssLocationProvider, new LocationUsageLogger()); + mGnssManagerService = new GnssManagerService(mMockContext, mSettingsHelper, + mAppForegroundHelper, new LocationUsageLogger(), + mMockGnssLocationProvider); + mGnssManagerService.onSystemReady(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(LocationManagerInternal.class); } private void overrideAsBinder(IInterface mockListener) { @@ -225,7 +236,7 @@ public class GnssManagerServiceTest { PackageManager.PERMISSION_GRANTED); // AppOpsManager will return true if OP_FINE_LOCATION is checked - when(mMockAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer( + when(mAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer( (InvocationOnMock invocation) -> { int code = (int) (invocation.getArguments()[0]); if (code == AppOpsManager.OP_FINE_LOCATION) { @@ -237,11 +248,11 @@ public class GnssManagerServiceTest { private void disableLocationPermissions() { Mockito.doThrow(new SecurityException()).when( - mMockContext).enforceCallingPermission(anyString(), anyString()); + mMockContext).enforceCallingPermission(anyString(), nullable(String.class)); Mockito.doThrow(new SecurityException()).when( mMockContext).checkPermission(anyString(), anyInt(), anyInt()); - when(mMockAppOpsManager.checkOp(anyInt(), anyInt(), + when(mAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenReturn(AppOpsManager.MODE_ERRORED); } @@ -733,14 +744,12 @@ public class GnssManagerServiceTest { @Test public void sendNiResponseWithPermissionsTest() throws RemoteException { - when(mMockNetInitiatedListener.sendNiResponse(anyInt(), anyInt())).thenReturn(true); - int notifId = 0; int userResponse = 0; enableLocationPermissions(); - assertThat(mGnssManagerService.sendNiResponse(notifId, userResponse)).isEqualTo(true); + mGnssManagerService.sendNiResponse(notifId, userResponse); - verify(mMockNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse); + verify(mNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse); } }