Create AppForegroundHelper

Create a new helper to deal with application foreground state changes.

1) Rename various Helpers.
2) Move GnssManagerService into location dir.
3) Use new AppForegroundHelper everywhere.
4) Move sendNiRequest to internal API
5) Some refactoring of NetInitiatedActivity

Test: atest AppForegroundHelperTest
Change-Id: Ifffe0e6a34452a18d017c6b190547b904d77b430
This commit is contained in:
Soonil Nagarkar
2020-01-29 15:23:06 -08:00
parent 24e6e408b0
commit b6375a4f7f
16 changed files with 803 additions and 781 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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()) {

View File

@@ -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);
}

View File

@@ -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);
@@ -2391,12 +2353,6 @@ public class LocationManagerService extends ILocationManager.Stub {
return true;
}
@Override
public boolean sendNiResponse(int notifId, int userResponse) {
return mGnssManagerService != null && mGnssManagerService.sendNiResponse(notifId,
userResponse);
}
@Override
public ProviderProperties getProviderProperties(String providerName) {
LocationProviderManager manager = getLocationProviderManager(providerName);
@@ -2408,20 +2364,13 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean isProviderPackage(String packageName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.READ_DEVICE_CONFIG + " permission required");
for (LocationProviderManager manager : mProviderManagers) {
if (manager.getPackages().contains(packageName)) {
return true;
}
}
return false;
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null);
return mLocalService.isProviderPackage(packageName);
}
@Override
public List<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);
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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)));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());

View File

@@ -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);

View File

@@ -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()) {

View File

@@ -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;
@UserIdInt private volatile int mCurrentUserId;
@GuardedBy("this")
@UserIdInt
private int mCurrentUserId;
@GuardedBy("this")
@UserIdInt
private int mCachedParentUserId;
@UserIdInt private int mCachedParentUserId;
@GuardedBy("this")
private int[] mCachedProfileUserIds;
public UserInfoStore(Context context) {
public UserInfoHelper(Context context) {
mContext = context;
mListeners = new CopyOnWriteArrayList<>();
@@ -120,7 +116,7 @@ public class UserInfoStore {
}
/**
* Adds a listener for user changed events.
* Adds a listener for user changed events. Callbacks occur on an unspecified thread.
*/
public void addListener(UserChangedListener listener) {
mListeners.add(listener);
@@ -134,16 +130,13 @@ public class UserInfoStore {
}
private void onUserChanged(@UserIdInt int newUserId) {
int oldUserId;
synchronized (this) {
if (newUserId == mCurrentUserId) {
return;
}
oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
if (newUserId == mCurrentUserId) {
return;
}
int oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
for (UserChangedListener listener : mListeners) {
listener.onUserChanged(oldUserId, newUserId);
}
@@ -161,7 +154,7 @@ public class UserInfoStore {
* Returns the user id of the current user.
*/
@UserIdInt
public synchronized int getCurrentUserId() {
public int getCurrentUserId() {
return mCurrentUserId;
}
@@ -169,9 +162,10 @@ public class UserInfoStore {
* Returns true if the given user id is either the current user or a profile of the current
* user.
*/
public synchronized boolean isCurrentUserOrProfile(@UserIdInt int userId) {
return userId == mCurrentUserId || ArrayUtils.contains(
getProfileUserIdsForParentUser(mCurrentUserId), userId);
public boolean isCurrentUserOrProfile(@UserIdInt int userId) {
int currentUserId = mCurrentUserId;
return userId == currentUserId || ArrayUtils.contains(
getProfileUserIdsForParentUser(currentUserId), userId);
}
/**
@@ -179,50 +173,44 @@ public class UserInfoStore {
* is a parent or has no profiles.
*/
@UserIdInt
public synchronized int getParentUserId(@UserIdInt int userId) {
int parentUserId;
if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, userId)) {
parentUserId = mCachedParentUserId;
} else {
Preconditions.checkState(mUserManager != null);
long identity = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUserManager.getProfileParent(userId);
if (userInfo != null) {
parentUserId = userInfo.id;
} else {
// getProfileParent() returns null if the userId is already the parent...
parentUserId = userId;
}
// force profiles into cache
getProfileUserIdsForParentUser(parentUserId);
} finally {
Binder.restoreCallingIdentity(identity);
public int getParentUserId(@UserIdInt int userId) {
synchronized (this) {
if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds,
userId)) {
return mCachedParentUserId;
}
Preconditions.checkState(mUserManager != null);
}
int parentUserId;
long identity = Binder.clearCallingIdentity();
try {
UserInfo userInfo = mUserManager.getProfileParent(userId);
parentUserId = userInfo != null ? userInfo.id : userId;
} finally {
Binder.restoreCallingIdentity(identity);
}
// force profiles into cache
getProfileUserIdsForParentUser(parentUserId);
return parentUserId;
}
@GuardedBy("this")
private int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
Preconditions.checkState(mUserManager != null);
// only assert on debug builds as this is a more expensive check
if (Build.IS_DEBUGGABLE) {
long identity = Binder.clearCallingIdentity();
try {
Preconditions.checkArgument(mUserManager.getProfileParent(parentUserId) == null);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
if (parentUserId != mCachedParentUserId) {
long identity = Binder.clearCallingIdentity();
try {
Preconditions.checkState(mUserManager != null);
// more expensive check - check that argument really is a parent user id
if (Build.IS_DEBUGGABLE) {
Preconditions.checkArgument(
mUserManager.getProfileParent(parentUserId) == null);
}
mCachedParentUserId = parentUserId;
mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
} finally {
@@ -236,8 +224,9 @@ public class UserInfoStore {
/**
* Dump info for debugging.
*/
public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Current User: " + mCurrentUserId + " " + Arrays.toString(
getProfileUserIdsForParentUser(mCurrentUserId)));
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
int currentUserId = mCurrentUserId;
pw.println("Current User: " + currentUserId + " " + Arrays.toString(
getProfileUserIdsForParentUser(currentUserId)));
}
}

View File

@@ -16,10 +16,11 @@
package com.android.server.location.gnss;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssCapabilities;
@@ -31,8 +32,8 @@ import android.location.IGnssStatusListener;
import android.location.IGpsGeofenceHardware;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
@@ -43,13 +44,12 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
import com.android.server.LocationManagerService;
import com.android.server.LocationManagerServiceUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.LocationManagerServiceUtils.LinkedListener;
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.AppForegroundHelper;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GnssBatchingProvider;
import com.android.server.location.GnssCapabilitiesProvider;
@@ -60,6 +60,7 @@ import com.android.server.location.GnssNavigationMessageProvider;
import com.android.server.location.GnssStatusListenerHelper;
import com.android.server.location.LocationUsageLogger;
import com.android.server.location.RemoteListenerHelper;
import com.android.server.location.SettingsHelper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -70,10 +71,18 @@ import java.util.function.Function;
/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
public class GnssManagerService {
private static final String TAG = "GnssManagerService";
private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
// Providers
private static final String TAG = "GnssManagerService";
public static boolean isGnssSupported() {
return GnssLocationProvider.isSupported();
}
private final Context mContext;
private final SettingsHelper mSettingsHelper;
private final AppForegroundHelper mAppForegroundHelper;
private final LocationUsageLogger mLocationUsageLogger;
private final GnssLocationProvider mGnssLocationProvider;
private final GnssStatusListenerHelper mGnssStatusProvider;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -83,58 +92,59 @@ public class GnssManagerService {
private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
private final GnssBatchingProvider mGnssBatchingProvider;
private final INetInitiatedListener mNetInitiatedListener;
private final IGpsGeofenceHardware mGpsGeofenceProxy;
private final LocationManagerService mLocationManagerService;
private final LocationUsageLogger mLocationUsageLogger;
@GuardedBy("mGnssMeasurementsListeners")
private final ArrayMap<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("mGnssBatchingLock")
private IBatchedLocationCallback mGnssBatchingCallback;
@GuardedBy("this")
@Nullable private LocationManagerInternal mLocationManagerInternal;
@GuardedBy("this")
@Nullable private AppOpsManager mAppOpsManager;
private final Object mGnssBatchingLock = new Object();
@GuardedBy("mGnssBatchingLock")
private LinkedListener<IBatchedLocationCallback>
mGnssBatchingDeathCallback;
@Nullable private IBatchedLocationCallback mGnssBatchingCallback;
@GuardedBy("mGnssBatchingLock")
@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();
}
private boolean hasGnssPermissions(String packageName) {
mContext.enforceCallingPermission(
Manifest.permission.ACCESS_FINE_LOCATION,
"Fine location permission not granted.");
int uid = Binder.getCallingUid();
long identity = Binder.clearCallingIdentity();
try {
return mContext.getSystemService(
AppOpsManager.class).checkOp(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
== AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
/** Called when system is ready. */
public synchronized void onSystemReady() {
if (mLocationManagerInternal != null) {
return;
}
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mAppForegroundHelper.addListener(this::onAppForegroundChanged);
}
/** Retrieve the GnssLocationProvider. */
public GnssLocationProvider getGnssLocationProvider() {
return mGnssLocationProvider;
}
/** Retrieve the IGpsGeofenceHardware. */
public IGpsGeofenceHardware getGpsGeofenceProxy() {
return mGpsGeofenceProxy;
}
/**
* Get year of GNSS hardware.
*
* @return year of GNSS hardware as an int if possible, otherwise zero
*/
public int getGnssYearOfHardware() {
if (mGnssSystemInfoProvider != null) {
return mGnssSystemInfoProvider.getGnssYearOfHardware();
} else {
return 0;
}
return mGnssSystemInfoProvider.getGnssYearOfHardware();
}
/**
* Get model name of GNSS hardware.
*
* @return GNSS hardware model name as a string if possible, otherwise null
*/
@Nullable
public String getGnssHardwareModelName() {
if (mGnssSystemInfoProvider != null) {
return mGnssSystemInfoProvider.getGnssHardwareModelName();
} else {
return null;
}
return mGnssSystemInfoProvider.getGnssHardwareModelName();
}
/**
* Get GNSS hardware capabilities. The capabilities are described in {@link
* android.location.GnssCapabilities} and their integer values correspond to the
* bit positions in the returned {@code long} value.
*
* @param packageName name of requesting package
* @return capabilities supported by the GNSS chipset
* Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in
* {@link android.location.GnssCapabilities}.
*/
public long getGnssCapabilities(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to obtain GNSS chipset capabilities.");
if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return GnssCapabilities.INVALID_CAPABILITIES;
}
return mGnssCapabilitiesProvider.getGnssCapabilities();
}
/**
* Get size of GNSS batch (GNSS location results are batched together for power savings).
* Requires LOCATION_HARDWARE and GNSS permissions.
*
* @param packageName name of requesting package
* @return size of the GNSS batch collection
*/
public int getGnssBatchSize(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "getGnssBatchSize called without GNSS permissions");
return 0;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not get GNSS batch size. GNSS batching provider "
+ "not available.");
if (!checkLocationAppOp(packageName)) {
return 0;
}
@@ -257,27 +232,12 @@ public class GnssManagerService {
/**
* Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered
* as a collection.
*
* @param periodNanos duration over which to collect GPS positions before delivering as a
* batch
* @param wakeOnFifoFull specifying whether to wake on full queue
* @param packageName name of requesting package
* @return true of batch started successfully, false otherwise
*/
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "startGnssBatch called without GNSS permissions");
return false;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not start GNSS batching. GNSS batching provider "
+ "not available.");
if (!checkLocationAppOp(packageName)) {
return false;
}
@@ -285,7 +245,6 @@ public class GnssManagerService {
if (mGnssBatchingInProgress) {
// Current design does not expect multiple starts to be called repeatedly
Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
// Try to clean up anyway, and continue
stopGnssBatch();
}
@@ -296,26 +255,13 @@ public class GnssManagerService {
/**
* Adds a GNSS batching callback for delivering GNSS location batch results.
*
* @param callback called when batching operation is complete to deliver GPS positions
* @param packageName name of requesting package
* @return true if callback is successfully added, false otherwise
*/
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
@Nullable String featureId, @NonNull String listenerIdentity) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "addGnssBatchingCallback called without GNSS permissions");
return false;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not add GNSS batching callback. GNSS batching provider "
+ "not available.");
if (!checkLocationAppOp(packageName)) {
return false;
}
@@ -333,11 +279,9 @@ public class GnssManagerService {
stopGnssBatch();
removeGnssBatchingCallback();
});
if (!mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
callback.asBinder())) {
return false;
}
return true;
return mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
callback.asBinder());
}
}
@@ -347,27 +291,14 @@ public class GnssManagerService {
* @param packageName name of requesting package
*/
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "flushGnssBatch called without GNSS permissions");
return;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not flush GNSS batch. GNSS batching provider "
+ "not available.");
if (!checkLocationAppOp(packageName)) {
return;
}
synchronized (mGnssBatchingLock) {
if (!mGnssBatchingInProgress) {
Log.w(TAG, "flushGnssBatch called with no batch in progress");
}
mGnssBatchingProvider.flush();
}
}
@@ -376,17 +307,7 @@ public class GnssManagerService {
* Removes GNSS batching callback.
*/
public void removeGnssBatchingCallback() {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not add GNSS batching callback. GNSS batching provider "
+ "not available.");
return;
}
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
synchronized (mGnssBatchingLock) {
mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked(
@@ -398,44 +319,17 @@ public class GnssManagerService {
/**
* Stop GNSS batch collection.
*
* @return true if GNSS batch successfully stopped, false otherwise
*/
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not stop GNSS batch. GNSS batching provider "
+ "not available.");
return false;
}
synchronized (mGnssBatchingLock) {
mGnssBatchingInProgress = false;
return mGnssBatchingProvider.stop();
}
}
private void registerUidListener() {
mContext.getSystemService(
ActivityManager.class).addOnUidImportanceListener(
(uid, importance) -> {
// listener invoked on ui thread, move to our thread to reduce risk
// of blocking ui thread
mHandler.post(
() -> {
onForegroundChanged(uid,
LocationManagerServiceUtils.isImportanceForeground(
importance));
});
},
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
}
private void onForegroundChanged(int uid, boolean foreground) {
private void onAppForegroundChanged(int uid, boolean foreground) {
synchronized (mGnssMeasurementsListeners) {
updateListenersOnForegroundChangedLocked(
mGnssMeasurementsListeners,
@@ -463,8 +357,7 @@ public class GnssManagerService {
}
private <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");
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return false;
}
if (gnssDataProvider == null) {
Log.e(
TAG,
"Can not add GNSS data listener. GNSS data provider "
+ "not available.");
return false;
}
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
featureId, listenerIdentifier);
LinkedListener<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();

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}