diff options
16 files changed, 796 insertions, 774 deletions
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 89aa770d7f1c..92e9fe492442 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 79bec920c10e..6a5c0ec9457a 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<Address> 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 0c5fe787bbbc..197787e5b6e6 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 44d9d2372665..69162bab3167 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 f1f2d2abea3f..3bc93cc13023 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<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); - for (Entry<String, ArrayList<UpdateRecord>> 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<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); + for (Entry<String, ArrayList<UpdateRecord>> 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<UpdateRecord> 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<LocationRequest> 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<UpdateRecord> 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); @@ -2392,12 +2354,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @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); if (manager == null) { @@ -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<String> 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 9c8ac19cc591..372e91e41772 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 5afa48a2b34d..64bca78f294d 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 000000000000..8ddfc652d333 --- /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<AppForegroundListener> 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 81c06d7125f9..4e9c06761e54 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 306e1e3afcd7..62fa5ec82aea 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 index 0e8720ebb08f..9163490fa777 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<UserSettingChangedListener> 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<String> 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<String> getValue() { ArraySet<String> 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 index f282ed26a831..94f3a88cb859 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<UserChangedListener> mListeners; @GuardedBy("this") - @Nullable - private UserManager mUserManager; + @Nullable private UserManager mUserManager; - @GuardedBy("this") - @UserIdInt - private int mCurrentUserId; + @UserIdInt private volatile 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 { + public int getParentUserId(@UserIdInt int userId) { + synchronized (this) { + if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, + userId)) { + return mCachedParentUserId; + } + 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; - } + int parentUserId; - // force profiles into cache - getProfileUserIdsForParentUser(parentUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } + 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 1eb2c525ff22..2bab9fa67eb0 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 + 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<IBinder, - LinkedListener<IGnssMeasurementsListener>> + private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>> mGnssMeasurementsListeners = new ArrayMap<>(); @GuardedBy("mGnssNavigationMessageListeners") - private final ArrayMap< - IBinder, LinkedListener<IGnssNavigationMessageListener>> + private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>> mGnssNavigationMessageListeners = new ArrayMap<>(); @GuardedBy("mGnssStatusListeners") private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>> mGnssStatusListeners = new ArrayMap<>(); + @GuardedBy("this") + @Nullable private LocationManagerInternal mLocationManagerInternal; + @GuardedBy("this") + @Nullable private AppOpsManager mAppOpsManager; + + private final Object mGnssBatchingLock = new Object(); + @GuardedBy("mGnssBatchingLock") - private IBatchedLocationCallback mGnssBatchingCallback; + @Nullable private IBatchedLocationCallback mGnssBatchingCallback; @GuardedBy("mGnssBatchingLock") - private LinkedListener<IBatchedLocationCallback> - mGnssBatchingDeathCallback; + @Nullable private LinkedListener<IBatchedLocationCallback> 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(); - } + /** Called when system is ready. */ + public synchronized void onSystemReady() { + if (mLocationManagerInternal != null) { + return; + } - private boolean hasGnssPermissions(String packageName) { - mContext.enforceCallingPermission( - Manifest.permission.ACCESS_FINE_LOCATION, - "Fine location permission not granted."); + mSettingsHelper.onSystemReady(); + mAppForegroundHelper.onSystemReady(); - 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); - } + 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"); - - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "flushGnssBatch called without GNSS permissions"); - return; - } + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - 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 <TListener extends IInterface> void updateListenersOnForegroundChangedLocked( - ArrayMap<IBinder, ? extends LinkedListenerBase> - gnssDataListeners, + Map<IBinder, ? extends LinkedListenerBase> gnssDataListeners, RemoteListenerHelper<TListener> gnssDataProvider, Function<IBinder, TListener> 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<TListener> gnssDataProvider, - ArrayMap<IBinder, - LinkedListener<TListener>> gnssDataListeners, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners, Consumer<TListener> binderDeathCallback) { - if (!hasGnssPermissions(packageName)) { - Log.e(TAG, "addGnssDataListenerLocked called without GNSS permissions"); - return false; - } + mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - if (gnssDataProvider == null) { - Log.e( - TAG, - "Can not add GNSS data listener. GNSS data provider " - + "not available."); + if (!checkLocationAppOp(packageName)) { return false; } - CallerIdentity callerIdentity = - new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, - featureId, listenerIdentifier); - LinkedListener<TListener> linkedListener = - new LocationManagerServiceUtils.LinkedListener<>( - listener, listenerIdentifier, callerIdentity, binderDeathCallback); + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName, featureId, listenerIdentifier); + LinkedListener<TListener> 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 <TListener extends IInterface> void removeGnssDataListener( + private <TListener extends IInterface> void removeGnssDataListenerLocked( TListener listener, RemoteListenerHelper<TListener> gnssDataProvider, - ArrayMap<IBinder, - LinkedListener<TListener>> gnssDataListeners) { + ArrayMap<IBinder, LinkedListener<TListener>> 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<Location> 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 000000000000..38ec4ce3e827 --- /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<ActivityManager.OnUidImportanceListener> 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 index 06fb10257a37..389fdf9b0abc 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<BroadcastReceiver> 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 f262733bb882..f16cf35bfb34 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); } } |