Merge "Create AppForegroundHelper"

This commit is contained in:
TreeHugger Robot
2020-01-30 01:22:36 +00:00
committed by Android (Google) Code Review
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);
}
}