Merge "Assorted cleanup and refactoring"
This commit is contained in:
committed by
Android (Google) Code Review
commit
e76bbe2da2
@@ -40,7 +40,7 @@ public interface ListenerExecutor {
|
||||
/**
|
||||
* Called before this operation is to be run. Some operations may be canceled before they
|
||||
* are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
|
||||
* will only be run if this method was run.
|
||||
* will only be run if this method was run. This callback is invoked on the calling thread.
|
||||
*/
|
||||
default void onPreExecute() {}
|
||||
|
||||
@@ -49,7 +49,7 @@ public interface ListenerExecutor {
|
||||
* RuntimeException, which will propagate normally. Implementations of
|
||||
* {@link ListenerExecutor} have the option to override
|
||||
* {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
|
||||
* intercept failures at the class level.
|
||||
* intercept failures at the class level. This callback is invoked on the executor thread.
|
||||
*/
|
||||
default void onFailure(Exception e) {
|
||||
// implementations should handle any exceptions that may be thrown
|
||||
@@ -59,21 +59,24 @@ public interface ListenerExecutor {
|
||||
/**
|
||||
* Called after the operation is run. This method will always be called if
|
||||
* {@link #onPreExecute()} is called. Success implies that the operation was run to
|
||||
* completion with no failures.
|
||||
* completion with no failures. This callback may be invoked on the calling thread or
|
||||
* executor thread.
|
||||
*/
|
||||
default void onPostExecute(boolean success) {}
|
||||
|
||||
/**
|
||||
* Called after this operation is complete (which does not imply that it was necessarily
|
||||
* run). Will always be called once per operation, no matter if the operation was run or
|
||||
* not. Success implies that the operation was run to completion with no failures.
|
||||
* not. Success implies that the operation was run to completion with no failures. This
|
||||
* callback may be invoked on the calling thread or executor thread.
|
||||
*/
|
||||
default void onComplete(boolean success) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* May be override to handle operation failures at a class level. Will not be invoked in the
|
||||
* event of a RuntimeException, which will propagate normally.
|
||||
* event of a RuntimeException, which will propagate normally. This callback is invoked on the
|
||||
* executor thread.
|
||||
*/
|
||||
default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
|
||||
Exception exception) {
|
||||
@@ -83,7 +86,8 @@ public interface ListenerExecutor {
|
||||
/**
|
||||
* Executes the given listener operation on the given executor, using the provided listener
|
||||
* supplier. If the supplier returns a null value, or a value during the operation that does not
|
||||
* match the value prior to the operation, then the operation is considered canceled.
|
||||
* match the value prior to the operation, then the operation is considered canceled. If a null
|
||||
* operation is supplied, nothing happens.
|
||||
*/
|
||||
default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
|
||||
@Nullable ListenerOperation<TListener> operation) {
|
||||
|
||||
@@ -151,6 +151,16 @@ public final class GpsStatus {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an empty GpsStatus. Should only be used for legacy reasons.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
static GpsStatus createEmpty() {
|
||||
return new GpsStatus();
|
||||
}
|
||||
|
||||
private GpsStatus() {
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@ import android.os.IRemoteCallback;
|
||||
*/
|
||||
oneway interface ILocationListener
|
||||
{
|
||||
void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
|
||||
void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
|
||||
void onProviderEnabledChanged(String provider, boolean enabled);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ interface ILocationManager
|
||||
List<LocationRequest> getTestProviderCurrentRequests(String provider);
|
||||
LocationTime getGnssTimeMillis();
|
||||
|
||||
boolean sendExtraCommand(String provider, String command, inout Bundle extras);
|
||||
void sendExtraCommand(String provider, String command, inout Bundle extras);
|
||||
|
||||
// used by gts tests to verify whitelists
|
||||
String[] getBackgroundThrottlingWhitelist();
|
||||
|
||||
@@ -36,7 +36,9 @@ import android.os.Bundle;
|
||||
public interface LocationListener {
|
||||
|
||||
/**
|
||||
* Called when the location has changed.
|
||||
* Called when the location has changed. A wakelock is held on behalf on the listener for some
|
||||
* brief amount of time as this callback executes. If this callback performs long running
|
||||
* operations, it is the client's responsibility to obtain their own wakelock.
|
||||
*
|
||||
* @param location the updated location
|
||||
*/
|
||||
@@ -52,18 +54,17 @@ public interface LocationListener {
|
||||
default void onStatusChanged(String provider, int status, Bundle extras) {}
|
||||
|
||||
/**
|
||||
* Called when the provider is enabled by the user.
|
||||
* Called when a provider this listener is registered with becomes enabled.
|
||||
*
|
||||
* @param provider the name of the location provider that has become enabled
|
||||
* @param provider the name of the location provider
|
||||
*/
|
||||
default void onProviderEnabled(@NonNull String provider) {}
|
||||
|
||||
/**
|
||||
* Called when the provider is disabled by the user. If requestLocationUpdates
|
||||
* is called on an already disabled provider, this method is called
|
||||
* immediately.
|
||||
* Called when the provider this listener is registered with becomes disabled. If a provider is
|
||||
* disabled when this listener is registered, this callback will be invoked immediately.
|
||||
*
|
||||
* @param provider the name of the location provider that has become disabled
|
||||
* @param provider the name of the location provider
|
||||
*/
|
||||
default void onProviderDisabled(@NonNull String provider) {}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,6 @@ public class LocationManager {
|
||||
public static final String METADATA_SETTINGS_FOOTER_STRING =
|
||||
"com.android.settings.location.FOOTER_STRING";
|
||||
|
||||
|
||||
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
|
||||
|
||||
@GuardedBy("sLocationListeners")
|
||||
@@ -311,7 +310,9 @@ public class LocationManager {
|
||||
sLocationListeners = new WeakHashMap<>();
|
||||
|
||||
final Context mContext;
|
||||
@UnsupportedAppUsage
|
||||
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
|
||||
+ "LocationManager}")
|
||||
final ILocationManager mService;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@@ -421,8 +422,7 @@ public class LocationManager {
|
||||
try {
|
||||
return mService.getExtraLocationControllerPackage();
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
return null;
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ public class LocationManager {
|
||||
try {
|
||||
mService.setExtraLocationControllerPackage(packageName);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ public class LocationManager {
|
||||
try {
|
||||
mService.setExtraLocationControllerPackageEnabled(enabled);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,8 +466,7 @@ public class LocationManager {
|
||||
try {
|
||||
return mService.isExtraLocationControllerPackageEnabled();
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
return false;
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +484,7 @@ public class LocationManager {
|
||||
try {
|
||||
mService.setExtraLocationControllerPackage(packageName);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,7 +502,7 @@ public class LocationManager {
|
||||
try {
|
||||
mService.setExtraLocationControllerPackageEnabled(enabled);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1199,7 +1198,7 @@ public class LocationManager {
|
||||
mContext.getPackageName(), mContext.getAttributionTag(),
|
||||
AppOpsManager.toReceiverId(listener));
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1301,7 +1300,7 @@ public class LocationManager {
|
||||
// unregistration is complete.
|
||||
mService.unregisterLocationListener(transport);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1517,7 +1516,8 @@ public class LocationManager {
|
||||
Preconditions.checkArgument(command != null, "invalid null command");
|
||||
|
||||
try {
|
||||
return mService.sendExtraCommand(provider, command, extras);
|
||||
mService.sendExtraCommand(provider, command, extras);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -1835,6 +1835,10 @@ public class LocationManager {
|
||||
} else {
|
||||
status.setStatus(gnssStatus, ttff);
|
||||
}
|
||||
} else if (status == null) {
|
||||
// even though this method is marked as nullable, legacy behavior was to never return
|
||||
// a null result, and there are applications that rely on this behavior.
|
||||
status = GpsStatus.createEmpty();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -2424,7 +2428,7 @@ public class LocationManager {
|
||||
try {
|
||||
cancellationSignal.cancel();
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2464,7 +2468,8 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
|
||||
public void onLocationChanged(Location location,
|
||||
@Nullable IRemoteCallback onCompleteCallback) {
|
||||
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
|
||||
@Override
|
||||
public void operate(LocationListener listener) {
|
||||
@@ -2473,7 +2478,13 @@ public class LocationManager {
|
||||
|
||||
@Override
|
||||
public void onComplete(boolean success) {
|
||||
markComplete(onCompleteCallback);
|
||||
if (onCompleteCallback != null) {
|
||||
try {
|
||||
onCompleteCallback.sendResult(null);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2488,14 +2499,6 @@ public class LocationManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void markComplete(IRemoteCallback onCompleteCallback) {
|
||||
try {
|
||||
onCompleteCallback.sendResult(null);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.android.server.location;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
||||
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
@@ -120,10 +123,16 @@ import com.android.server.location.util.AppForegroundHelper;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.LocationAttributionHelper;
|
||||
import com.android.server.location.util.LocationPermissionsHelper;
|
||||
import com.android.server.location.util.LocationPowerSaveModeHelper;
|
||||
import com.android.server.location.util.LocationUsageLogger;
|
||||
import com.android.server.location.util.ScreenInteractiveHelper;
|
||||
import com.android.server.location.util.SettingsHelper;
|
||||
import com.android.server.location.util.SystemAppForegroundHelper;
|
||||
import com.android.server.location.util.SystemAppOpsHelper;
|
||||
import com.android.server.location.util.SystemLocationPermissionsHelper;
|
||||
import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
|
||||
import com.android.server.location.util.SystemScreenInteractiveHelper;
|
||||
import com.android.server.location.util.SystemSettingsHelper;
|
||||
import com.android.server.location.util.SystemUserInfoHelper;
|
||||
import com.android.server.location.util.UserInfoHelper;
|
||||
@@ -173,7 +182,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
publishBinderService(Context.LOCATION_SERVICE, mService);
|
||||
|
||||
// client caching behavior is only enabled after seeing the first invalidate
|
||||
invalidateLocalLocationEnabledCaches();
|
||||
LocationManager.invalidateLocalLocationEnabledCaches();
|
||||
// disable caching for our own process
|
||||
Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
|
||||
.disableLocalLocationEnabledCaches();
|
||||
@@ -486,7 +495,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
private void onLocationModeChanged(int userId) {
|
||||
boolean enabled = mSettingsHelper.isLocationEnabled(userId);
|
||||
invalidateLocalLocationEnabledCaches();
|
||||
LocationManager.invalidateLocalLocationEnabledCaches();
|
||||
|
||||
if (D) {
|
||||
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
|
||||
@@ -1232,19 +1241,20 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
if (!currentlyMonitoring) {
|
||||
if (allowMonitoring) {
|
||||
if (!highPower) {
|
||||
return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
|
||||
return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
|
||||
} else {
|
||||
return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
|
||||
return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
|
||||
mCallerIdentity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
|
||||
if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
|
||||
LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
|
||||
mCallerIdentity.getPid()))) {
|
||||
mCallerIdentity.getPid())), mCallerIdentity)) {
|
||||
if (!highPower) {
|
||||
mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
|
||||
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
|
||||
} else {
|
||||
mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
|
||||
mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1589,8 +1599,9 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mAppOpsHelper.checkLocationAccess(identity,
|
||||
record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
|
||||
if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
|
||||
record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
|
||||
identity)) {
|
||||
continue;
|
||||
}
|
||||
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
|
||||
@@ -2118,7 +2129,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
// appops check should always be right before delivery
|
||||
if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
|
||||
if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
|
||||
identity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2179,7 +2191,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
|
||||
// appops check should always be right before delivery
|
||||
if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
|
||||
if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
|
||||
identity)) {
|
||||
transport.deliverResult(lastLocation);
|
||||
} else {
|
||||
transport.deliverResult(null);
|
||||
@@ -2329,7 +2342,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
|
||||
public void sendExtraCommand(String provider, String command, Bundle extras) {
|
||||
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
|
||||
@@ -2350,8 +2363,6 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
LocationStatsEnums.USAGE_ENDED,
|
||||
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
|
||||
provider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2553,7 +2564,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
r.mLastFixBroadcast = location;
|
||||
|
||||
// appops check should always be right before delivery
|
||||
if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
|
||||
if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
|
||||
receiver.mCallerIdentity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2644,7 +2656,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// unsafe is ok because app ops will verify the package name
|
||||
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
|
||||
attributionTag);
|
||||
if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
|
||||
if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2664,7 +2676,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// unsafe is ok because app ops will verify the package name
|
||||
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
|
||||
attributionTag);
|
||||
if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
|
||||
if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2687,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// unsafe is ok because app ops will verify the package name
|
||||
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
|
||||
attributionTag);
|
||||
if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
|
||||
if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2708,7 +2720,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// unsafe is ok because app ops will verify the package name
|
||||
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
|
||||
attributionTag);
|
||||
if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
|
||||
if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2926,24 +2938,36 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
private final UserInfoHelper mUserInfoHelper;
|
||||
private final SystemAppOpsHelper mAppOpsHelper;
|
||||
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
|
||||
private final SystemSettingsHelper mSettingsHelper;
|
||||
private final SystemAppForegroundHelper mAppForegroundHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
|
||||
private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
|
||||
private final LocationAttributionHelper mLocationAttributionHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
private final LocationRequestStatistics mLocationRequestStatistics;
|
||||
|
||||
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
|
||||
mUserInfoHelper = userInfoHelper;
|
||||
mAppOpsHelper = new SystemAppOpsHelper(context);
|
||||
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
|
||||
mAppOpsHelper);
|
||||
mSettingsHelper = new SystemSettingsHelper(context);
|
||||
mAppForegroundHelper = new SystemAppForegroundHelper(context);
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
|
||||
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
|
||||
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
mLocationRequestStatistics = new LocationRequestStatistics();
|
||||
}
|
||||
|
||||
void onSystemReady() {
|
||||
mAppOpsHelper.onSystemReady();
|
||||
mLocationPermissionsHelper.onSystemReady();
|
||||
mSettingsHelper.onSystemReady();
|
||||
mAppForegroundHelper.onSystemReady();
|
||||
mLocationPowerSaveModeHelper.onSystemReady();
|
||||
mScreenInteractiveHelper.onSystemReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2956,6 +2980,11 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
return mAppOpsHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationPermissionsHelper getLocationPermissionsHelper() {
|
||||
return mLocationPermissionsHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingsHelper getSettingsHelper() {
|
||||
return mSettingsHelper;
|
||||
@@ -2971,9 +3000,24 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
return mLocationUsageLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
|
||||
return mLocationPowerSaveModeHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScreenInteractiveHelper getScreenInteractiveHelper() {
|
||||
return mScreenInteractiveHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationAttributionHelper getLocationAttributionHelper() {
|
||||
return mLocationAttributionHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationRequestStatistics getLocationRequestStatistics() {
|
||||
return mLocationRequestStatistics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,14 +50,10 @@ public class PassiveProvider extends AbstractLocationProvider {
|
||||
Criteria.POWER_LOW,
|
||||
Criteria.ACCURACY_COARSE);
|
||||
|
||||
private volatile boolean mReportLocation;
|
||||
|
||||
public PassiveProvider(Context context) {
|
||||
// using a direct executor is ok because this class has no locks that could deadlock
|
||||
super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
|
||||
|
||||
mReportLocation = false;
|
||||
|
||||
setProperties(PROPERTIES);
|
||||
setAllowed(true);
|
||||
}
|
||||
@@ -66,15 +62,11 @@ public class PassiveProvider extends AbstractLocationProvider {
|
||||
* Pass a location into the passive provider.
|
||||
*/
|
||||
public void updateLocation(Location location) {
|
||||
if (mReportLocation) {
|
||||
reportLocation(location);
|
||||
}
|
||||
reportLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
mReportLocation = request.reportLocation;
|
||||
}
|
||||
public void onSetRequest(ProviderRequest request) {}
|
||||
|
||||
@Override
|
||||
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.location.geofence;
|
||||
|
||||
import static android.Manifest.permission;
|
||||
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
|
||||
|
||||
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
|
||||
@@ -44,8 +43,8 @@ import com.android.server.PendingIntentUtils;
|
||||
import com.android.server.location.LocationPermissions;
|
||||
import com.android.server.location.listeners.ListenerMultiplexer;
|
||||
import com.android.server.location.listeners.PendingIntentListenerRegistration;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.LocationPermissionsHelper;
|
||||
import com.android.server.location.util.LocationUsageLogger;
|
||||
import com.android.server.location.util.SettingsHelper;
|
||||
import com.android.server.location.util.UserInfoHelper;
|
||||
@@ -86,7 +85,7 @@ public class GeofenceManager extends
|
||||
|
||||
// we store these values because we don't trust the listeners not to give us dupes, not to
|
||||
// spam us, and because checking the values may be more expensive
|
||||
private boolean mAppOpsAllowed;
|
||||
private boolean mPermitted;
|
||||
|
||||
private @Nullable Location mCachedLocation;
|
||||
private float mCachedLocationDistanceM;
|
||||
@@ -101,7 +100,7 @@ public class GeofenceManager extends
|
||||
|
||||
mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
TAG + ":" + identity.getPackageName());
|
||||
TAG + ":" + identity.getPackageName());
|
||||
mWakeLock.setReferenceCounted(true);
|
||||
mWakeLock.setWorkSource(identity.addToWorkSource(null));
|
||||
}
|
||||
@@ -114,7 +113,8 @@ public class GeofenceManager extends
|
||||
@Override
|
||||
protected void onPendingIntentListenerRegister() {
|
||||
mGeofenceState = STATE_UNKNOWN;
|
||||
mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
|
||||
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
|
||||
getIdentity());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,18 +127,32 @@ public class GeofenceManager extends
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAppOpsAllowed() {
|
||||
return mAppOpsAllowed;
|
||||
boolean isPermitted() {
|
||||
return mPermitted;
|
||||
}
|
||||
|
||||
boolean onAppOpsChanged(String packageName) {
|
||||
boolean onLocationPermissionsChanged(String packageName) {
|
||||
if (getIdentity().getPackageName().equals(packageName)) {
|
||||
boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
|
||||
PERMISSION_FINE);
|
||||
if (appOpsAllowed != mAppOpsAllowed) {
|
||||
mAppOpsAllowed = appOpsAllowed;
|
||||
return true;
|
||||
}
|
||||
return onLocationPermissionsChanged();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean onLocationPermissionsChanged(int uid) {
|
||||
if (getIdentity().getUid() == uid) {
|
||||
return onLocationPermissionsChanged();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onLocationPermissionsChanged() {
|
||||
boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
|
||||
getIdentity());
|
||||
if (permitted != mPermitted) {
|
||||
mPermitted = permitted;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -186,10 +200,10 @@ public class GeofenceManager extends
|
||||
|
||||
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
|
||||
try {
|
||||
pendingIntent.send(mContext, 0, intent,
|
||||
(pI, i, rC, rD, rE) -> mWakeLock.release(),
|
||||
null, permission.ACCESS_FINE_LOCATION,
|
||||
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
|
||||
// send() only enforces permissions for broadcast intents, but since clients can
|
||||
// select any kind of pending intent we do not rely on send() to enforce permissions
|
||||
pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
|
||||
null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
mWakeLock.release();
|
||||
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
|
||||
@@ -202,7 +216,7 @@ public class GeofenceManager extends
|
||||
builder.append(getIdentity());
|
||||
|
||||
ArraySet<String> flags = new ArraySet<>(1);
|
||||
if (!mAppOpsAllowed) {
|
||||
if (!mPermitted) {
|
||||
flags.add("na");
|
||||
}
|
||||
if (!flags.isEmpty()) {
|
||||
@@ -224,10 +238,22 @@ public class GeofenceManager extends
|
||||
private final SettingsHelper.UserSettingChangedListener
|
||||
mLocationPackageBlacklistChangedListener =
|
||||
this::onLocationPackageBlacklistChanged;
|
||||
private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
|
||||
private final LocationPermissionsHelper.LocationPermissionsListener
|
||||
mLocationPermissionsListener =
|
||||
new LocationPermissionsHelper.LocationPermissionsListener() {
|
||||
@Override
|
||||
public void onLocationPermissionsChanged(String packageName) {
|
||||
GeofenceManager.this.onLocationPermissionsChanged(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionsChanged(int uid) {
|
||||
GeofenceManager.this.onLocationPermissionsChanged(uid);
|
||||
}
|
||||
};
|
||||
|
||||
protected final UserInfoHelper mUserInfoHelper;
|
||||
protected final AppOpsHelper mAppOpsHelper;
|
||||
protected final LocationPermissionsHelper mLocationPermissionsHelper;
|
||||
protected final SettingsHelper mSettingsHelper;
|
||||
protected final LocationUsageLogger mLocationUsageLogger;
|
||||
|
||||
@@ -241,7 +267,7 @@ public class GeofenceManager extends
|
||||
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
|
||||
mUserInfoHelper = injector.getUserInfoHelper();
|
||||
mSettingsHelper = injector.getSettingsHelper();
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
|
||||
mLocationUsageLogger = injector.getLocationUsageLogger();
|
||||
}
|
||||
|
||||
@@ -281,7 +307,7 @@ public class GeofenceManager extends
|
||||
@Override
|
||||
protected boolean isActive(GeofenceRegistration registration) {
|
||||
CallerIdentity identity = registration.getIdentity();
|
||||
return registration.isAppOpsAllowed()
|
||||
return registration.isPermitted()
|
||||
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
|
||||
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
|
||||
&& !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
|
||||
@@ -294,7 +320,7 @@ public class GeofenceManager extends
|
||||
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
|
||||
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
|
||||
mLocationPackageBlacklistChangedListener);
|
||||
mAppOpsHelper.addListener(mAppOpsChangedListener);
|
||||
mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,7 +329,7 @@ public class GeofenceManager extends
|
||||
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
|
||||
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
|
||||
mLocationPackageBlacklistChangedListener);
|
||||
mAppOpsHelper.removeListener(mAppOpsChangedListener);
|
||||
mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -434,7 +460,11 @@ public class GeofenceManager extends
|
||||
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
|
||||
}
|
||||
|
||||
private void onAppOpsChanged(String packageName) {
|
||||
updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
|
||||
private void onLocationPermissionsChanged(String packageName) {
|
||||
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
|
||||
}
|
||||
|
||||
private void onLocationPermissionsChanged(int uid) {
|
||||
updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.android.server.LocalServices;
|
||||
import com.android.server.location.listeners.BinderListenerRegistration;
|
||||
import com.android.server.location.listeners.ListenerMultiplexer;
|
||||
import com.android.server.location.util.AppForegroundHelper;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.LocationPermissionsHelper;
|
||||
import com.android.server.location.util.SettingsHelper;
|
||||
import com.android.server.location.util.UserInfoHelper;
|
||||
import com.android.server.location.util.UserInfoHelper.UserListener;
|
||||
@@ -65,7 +65,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
// we store these values because we don't trust the listeners not to give us dupes, not to
|
||||
// spam us, and because checking the values may be more expensive
|
||||
private boolean mForeground;
|
||||
private boolean mAppOpsAllowed;
|
||||
private boolean mPermitted;
|
||||
|
||||
protected GnssListenerRegistration(@Nullable TRequest request,
|
||||
CallerIdentity callerIdentity, TListener listener) {
|
||||
@@ -84,24 +84,39 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
return mForeground;
|
||||
}
|
||||
|
||||
boolean isAppOpsAllowed() {
|
||||
return mAppOpsAllowed;
|
||||
boolean isPermitted() {
|
||||
return mPermitted;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBinderListenerRegister() {
|
||||
mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
|
||||
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
|
||||
getIdentity());
|
||||
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
|
||||
}
|
||||
|
||||
boolean onAppOpsChanged(String packageName) {
|
||||
boolean onLocationPermissionsChanged(String packageName) {
|
||||
if (getIdentity().getPackageName().equals(packageName)) {
|
||||
boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
|
||||
PERMISSION_FINE);
|
||||
if (appOpsAllowed != mAppOpsAllowed) {
|
||||
mAppOpsAllowed = appOpsAllowed;
|
||||
return true;
|
||||
}
|
||||
return onLocationPermissionsChanged();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean onLocationPermissionsChanged(int uid) {
|
||||
if (getIdentity().getUid() == uid) {
|
||||
return onLocationPermissionsChanged();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onLocationPermissionsChanged() {
|
||||
boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
|
||||
getIdentity());
|
||||
if (permitted != mPermitted) {
|
||||
mPermitted = permitted;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -125,7 +140,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
if (!mForeground) {
|
||||
flags.add("bg");
|
||||
}
|
||||
if (!mAppOpsAllowed) {
|
||||
if (!mPermitted) {
|
||||
flags.add("na");
|
||||
}
|
||||
if (!flags.isEmpty()) {
|
||||
@@ -141,7 +156,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
|
||||
protected final UserInfoHelper mUserInfoHelper;
|
||||
protected final SettingsHelper mSettingsHelper;
|
||||
protected final AppOpsHelper mAppOpsHelper;
|
||||
protected final LocationPermissionsHelper mLocationPermissionsHelper;
|
||||
protected final AppForegroundHelper mAppForegroundHelper;
|
||||
protected final LocationManagerInternal mLocationManagerInternal;
|
||||
|
||||
@@ -154,14 +169,26 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
private final SettingsHelper.UserSettingChangedListener
|
||||
mLocationPackageBlacklistChangedListener =
|
||||
this::onLocationPackageBlacklistChanged;
|
||||
private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
|
||||
private final LocationPermissionsHelper.LocationPermissionsListener
|
||||
mLocationPermissionsListener =
|
||||
new LocationPermissionsHelper.LocationPermissionsListener() {
|
||||
@Override
|
||||
public void onLocationPermissionsChanged(String packageName) {
|
||||
GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionsChanged(int uid) {
|
||||
GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
|
||||
}
|
||||
};
|
||||
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
|
||||
this::onAppForegroundChanged;
|
||||
|
||||
protected GnssListenerMultiplexer(Injector injector) {
|
||||
mUserInfoHelper = injector.getUserInfoHelper();
|
||||
mSettingsHelper = injector.getSettingsHelper();
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
|
||||
mAppForegroundHelper = injector.getAppForegroundHelper();
|
||||
mLocationManagerInternal = Objects.requireNonNull(
|
||||
LocalServices.getService(LocationManagerInternal.class));
|
||||
@@ -208,7 +235,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
CallerIdentity identity = registration.getIdentity();
|
||||
// TODO: this should be checking if the gps provider is enabled, not if location is enabled,
|
||||
// but this is the same for now.
|
||||
return registration.isAppOpsAllowed()
|
||||
return registration.isPermitted()
|
||||
&& (registration.isForeground() || isBackgroundRestrictionExempt(identity))
|
||||
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
|
||||
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
|
||||
@@ -241,7 +268,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
mBackgroundThrottlePackageWhitelistChangedListener);
|
||||
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
|
||||
mLocationPackageBlacklistChangedListener);
|
||||
mAppOpsHelper.addListener(mAppOpsChangedListener);
|
||||
mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
|
||||
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
|
||||
}
|
||||
|
||||
@@ -257,7 +284,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
mBackgroundThrottlePackageWhitelistChangedListener);
|
||||
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
|
||||
mLocationPackageBlacklistChangedListener);
|
||||
mAppOpsHelper.removeListener(mAppOpsChangedListener);
|
||||
mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
|
||||
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
|
||||
}
|
||||
|
||||
@@ -279,8 +306,12 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
|
||||
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
|
||||
}
|
||||
|
||||
private void onAppOpsChanged(String packageName) {
|
||||
updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
|
||||
private void onLocationPermissionsChanged(String packageName) {
|
||||
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
|
||||
}
|
||||
|
||||
private void onLocationPermissionsChanged(int uid) {
|
||||
updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
|
||||
}
|
||||
|
||||
private void onAppForegroundChanged(int uid, boolean foreground) {
|
||||
|
||||
@@ -18,10 +18,9 @@ package com.android.server.location.gnss;
|
||||
|
||||
import static android.location.LocationManager.GPS_PROVIDER;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.location.GnssAntennaInfo;
|
||||
import android.location.GnssMeasurementCorrections;
|
||||
@@ -47,10 +46,8 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.location.util.AppForegroundHelper;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.SettingsHelper;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.List;
|
||||
@@ -68,9 +65,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final SettingsHelper mSettingsHelper;
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final AppForegroundHelper mAppForegroundHelper;
|
||||
private final LocationManagerInternal mLocationManagerInternal;
|
||||
|
||||
private final GnssLocationProvider mGnssLocationProvider;
|
||||
@@ -109,9 +104,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
|
||||
GnssNative.initialize();
|
||||
|
||||
mContext = context.createAttributionContext(ATTRIBUTION_ID);
|
||||
mSettingsHelper = injector.getSettingsHelper();
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mAppForegroundHelper = injector.getAppForegroundHelper();
|
||||
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
|
||||
|
||||
if (gnssLocationProvider == null) {
|
||||
@@ -192,9 +185,10 @@ public class GnssManagerService implements GnssNative.Callbacks {
|
||||
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
|
||||
String attributionTag) {
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
|
||||
if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
|
||||
if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package com.android.server.location.gnss;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
|
||||
import static com.android.server.location.gnss.GnssManagerService.D;
|
||||
import static com.android.server.location.gnss.GnssManagerService.TAG;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.location.GnssMeasurementsEvent;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
@@ -30,6 +30,7 @@ import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.LocationUsageLogger;
|
||||
import com.android.server.location.util.SettingsHelper;
|
||||
@@ -47,6 +48,7 @@ public class GnssMeasurementsProvider extends
|
||||
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
|
||||
SettingsHelper.GlobalSettingChangedListener {
|
||||
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final LocationUsageLogger mLogger;
|
||||
private final GnssMeasurementProviderNative mNative;
|
||||
|
||||
@@ -57,6 +59,7 @@ public class GnssMeasurementsProvider extends
|
||||
@VisibleForTesting
|
||||
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
|
||||
super(injector);
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mLogger = injector.getLocationUsageLogger();
|
||||
mNative = aNative;
|
||||
}
|
||||
@@ -163,7 +166,8 @@ public class GnssMeasurementsProvider extends
|
||||
*/
|
||||
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
|
||||
deliverToListeners(registration -> {
|
||||
if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
|
||||
if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
|
||||
registration.getIdentity())) {
|
||||
return listener -> listener.onGnssMeasurementsReceived(event);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package com.android.server.location.gnss;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
|
||||
import static com.android.server.location.gnss.GnssManagerService.D;
|
||||
import static com.android.server.location.gnss.GnssManagerService.TAG;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.location.GnssNavigationMessage;
|
||||
import android.location.IGnssNavigationMessageListener;
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
@@ -27,6 +27,7 @@ import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,7 @@ import com.android.server.location.util.Injector;
|
||||
public class GnssNavigationMessageProvider extends
|
||||
GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
|
||||
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final GnssNavigationMessageProviderNative mNative;
|
||||
|
||||
public GnssNavigationMessageProvider(Injector injector) {
|
||||
@@ -49,6 +51,7 @@ public class GnssNavigationMessageProvider extends
|
||||
public GnssNavigationMessageProvider(Injector injector,
|
||||
GnssNavigationMessageProviderNative aNative) {
|
||||
super(injector);
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mNative = aNative;
|
||||
}
|
||||
|
||||
@@ -90,7 +93,8 @@ public class GnssNavigationMessageProvider extends
|
||||
*/
|
||||
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
|
||||
deliverToListeners(registration -> {
|
||||
if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
|
||||
if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
|
||||
registration.getIdentity())) {
|
||||
return listener -> listener.onGnssNavigationMessageReceived(event);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package com.android.server.location.gnss;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
|
||||
import static com.android.server.location.gnss.GnssManagerService.D;
|
||||
import static com.android.server.location.gnss.GnssManagerService.TAG;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.location.GnssStatus;
|
||||
import android.location.IGnssStatusListener;
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
@@ -27,6 +27,7 @@ import android.os.IBinder;
|
||||
import android.stats.location.LocationStatsEnums;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.server.location.util.AppOpsHelper;
|
||||
import com.android.server.location.util.Injector;
|
||||
import com.android.server.location.util.LocationUsageLogger;
|
||||
|
||||
@@ -35,10 +36,12 @@ import com.android.server.location.util.LocationUsageLogger;
|
||||
*/
|
||||
public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
|
||||
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final LocationUsageLogger mLogger;
|
||||
|
||||
public GnssStatusProvider(Injector injector) {
|
||||
super(injector);
|
||||
mAppOpsHelper = injector.getAppOpsHelper();
|
||||
mLogger = injector.getLocationUsageLogger();
|
||||
}
|
||||
|
||||
@@ -113,7 +116,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
|
||||
*/
|
||||
public void onSvStatusChanged(GnssStatus gnssStatus) {
|
||||
deliverToListeners(registration -> {
|
||||
if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
|
||||
if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
|
||||
registration.getIdentity())) {
|
||||
return listener -> listener.onSvStatusChanged(gnssStatus);
|
||||
} else {
|
||||
return null;
|
||||
@@ -126,7 +130,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
|
||||
*/
|
||||
public void onNmeaReceived(long timestamp, String nmea) {
|
||||
deliverToListeners(registration -> {
|
||||
if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
|
||||
if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
|
||||
registration.getIdentity())) {
|
||||
return listener -> listener.onNmeaReceived(timestamp, nmea);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Pair;
|
||||
|
||||
@@ -40,8 +41,8 @@ import java.util.function.Predicate;
|
||||
* A base class to multiplex client listener registrations within system server. Registrations are
|
||||
* divided into two categories, active registrations and inactive registrations, as defined by
|
||||
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
|
||||
* {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
|
||||
* invoked and return true for any registration whose active state may have changed.
|
||||
* {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
|
||||
* whose active state may have changed.
|
||||
*
|
||||
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
|
||||
*
|
||||
@@ -217,7 +218,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
mRegistrations.put(key, registration);
|
||||
}
|
||||
|
||||
|
||||
if (wasEmpty) {
|
||||
onRegister();
|
||||
}
|
||||
@@ -268,7 +268,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
|
||||
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
|
||||
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TKey key = mRegistrations.keyAt(i);
|
||||
if (predicate.test(key)) {
|
||||
removeRegistration(key, mRegistrations.valueAt(i));
|
||||
@@ -287,7 +288,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
* completely at some later time.
|
||||
*/
|
||||
protected final void removeRegistration(@NonNull Object key,
|
||||
@NonNull ListenerRegistration registration) {
|
||||
@NonNull ListenerRegistration<?, ?> registration) {
|
||||
synchronized (mRegistrations) {
|
||||
int index = mRegistrations.indexOfKey(key);
|
||||
if (index < 0) {
|
||||
@@ -353,7 +354,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
}
|
||||
|
||||
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (registration.isActive()) {
|
||||
actives.add(registration);
|
||||
@@ -395,7 +397,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
protected final void updateService(Predicate<TRegistration> predicate) {
|
||||
synchronized (mRegistrations) {
|
||||
boolean updateService = false;
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (predicate.test(registration) && registration.isActive()) {
|
||||
updateService = true;
|
||||
@@ -434,7 +437,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
|
||||
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
|
||||
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (predicate.test(registration)) {
|
||||
onRegistrationActiveChanged(registration);
|
||||
@@ -446,33 +450,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the predicate on the registration with the given key. The predicate should return
|
||||
* true if the active state of the registration may have changed as a result. Any
|
||||
* {@link #updateService()} invocations made while this method is executing will be deferred
|
||||
* until after the method is complete so as to avoid redundant work.
|
||||
*/
|
||||
protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
|
||||
synchronized (mRegistrations) {
|
||||
// since updating a registration can invoke a variety of callbacks, we need to ensure
|
||||
// those callbacks themselves do not re-enter, as this could lead to out-of-order
|
||||
// callbacks. note that try-with-resources ordering is meaningful here as well. we want
|
||||
// to close the reentrancy guard first, as this may generate additional service updates,
|
||||
// then close the update service buffer.
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
|
||||
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
|
||||
|
||||
TRegistration registration = mRegistrations.get(key);
|
||||
if (registration != null && predicate.test(registration)) {
|
||||
onRegistrationActiveChanged(registration);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private void onRegistrationActiveChanged(TRegistration registration) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
@@ -511,7 +488,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
synchronized (mRegistrations) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (registration.isActive()) {
|
||||
ListenerOperation<TListener> operation = function.apply(registration);
|
||||
@@ -537,7 +515,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
synchronized (mRegistrations) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (registration.isActive()) {
|
||||
execute(registration, operation);
|
||||
@@ -571,7 +550,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
ipw.println("listeners:");
|
||||
|
||||
ipw.increaseIndent();
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
final int size = mRegistrations.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
ipw.print(registration);
|
||||
if (!registration.isActive()) {
|
||||
@@ -612,23 +592,33 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
*/
|
||||
private final class ReentrancyGuard implements AutoCloseable {
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private int mGuardCount;
|
||||
private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
|
||||
@GuardedBy("mRegistrations")
|
||||
private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
|
||||
|
||||
ReentrancyGuard() {
|
||||
mGuardCount = 0;
|
||||
mScheduledRemovals = null;
|
||||
}
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
boolean isReentrant() {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Preconditions.checkState(Thread.holdsLock(mRegistrations));
|
||||
}
|
||||
return mGuardCount != 0;
|
||||
}
|
||||
|
||||
void markForRemoval(Object key, ListenerRegistration registration) {
|
||||
@GuardedBy("mRegistrations")
|
||||
void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Preconditions.checkState(Thread.holdsLock(mRegistrations));
|
||||
}
|
||||
Preconditions.checkState(isReentrant());
|
||||
|
||||
if (mScheduledRemovals == null) {
|
||||
mScheduledRemovals = new ArrayList<>(mRegistrations.size());
|
||||
mScheduledRemovals = new ArraySet<>(mRegistrations.size());
|
||||
}
|
||||
mScheduledRemovals.add(new Pair<>(key, registration));
|
||||
}
|
||||
@@ -640,7 +630,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
|
||||
ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
|
||||
|
||||
Preconditions.checkState(mGuardCount > 0);
|
||||
if (--mGuardCount == 0) {
|
||||
@@ -650,8 +640,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
|
||||
|
||||
if (scheduledRemovals != null) {
|
||||
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
|
||||
for (int i = 0; i < scheduledRemovals.size(); i++) {
|
||||
Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
|
||||
final int size = scheduledRemovals.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
|
||||
i);
|
||||
removeRegistration(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
|
||||
* Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
|
||||
* after {@link #onUnregister()}. It is safe to invoke this from within either function.
|
||||
*/
|
||||
public void remove() {
|
||||
public final void remove() {
|
||||
Object key = mKey;
|
||||
if (key != null) {
|
||||
getOwner().removeRegistration(key, this);
|
||||
|
||||
@@ -16,15 +16,8 @@
|
||||
|
||||
package com.android.server.location.util;
|
||||
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
|
||||
import com.android.server.location.LocationPermissions;
|
||||
import com.android.server.location.LocationPermissions.PermissionLevel;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
@@ -70,84 +63,27 @@ public abstract class AppOpsHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given identity may have locations delivered without noting that a location is
|
||||
* being delivered. This is a looser guarantee than
|
||||
* {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
|
||||
* arguments and so should not be used with unvalidated arguments or before actually delivering
|
||||
* locations.
|
||||
*
|
||||
* @see AppOpsManager#checkOpNoThrow(int, int, String)
|
||||
* Starts the given appop.
|
||||
*/
|
||||
public final boolean checkLocationAccess(CallerIdentity callerIdentity,
|
||||
@PermissionLevel int permissionLevel) {
|
||||
if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
|
||||
}
|
||||
public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
/**
|
||||
* Notes location access to the given identity, ie, location delivery. This method should be
|
||||
* called right before a location is delivered, and if it returns false, the location should not
|
||||
* be delivered.
|
||||
* Finishes the given appop.
|
||||
*/
|
||||
public final boolean noteLocationAccess(CallerIdentity identity,
|
||||
@PermissionLevel int permissionLevel) {
|
||||
if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
|
||||
}
|
||||
public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
/**
|
||||
* Notifies app ops that the given identity is using location at normal/low power levels. If
|
||||
* this function returns false, do not later call
|
||||
* {@link #stopLocationMonitoring(CallerIdentity)}.
|
||||
* Checks the given appop.
|
||||
*/
|
||||
public final boolean startLocationMonitoring(CallerIdentity identity) {
|
||||
return startOpNoThrow(OP_MONITOR_LOCATION, identity);
|
||||
}
|
||||
public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
/**
|
||||
* Notifies app ops that the given identity is no longer using location at normal/low power
|
||||
* levels.
|
||||
* Notes the given appop (and may throw a security exception).
|
||||
*/
|
||||
public final void stopLocationMonitoring(CallerIdentity identity) {
|
||||
finishOp(OP_MONITOR_LOCATION, identity);
|
||||
}
|
||||
public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
/**
|
||||
* Notifies app ops that the given identity is using location at high levels. If this function
|
||||
* returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
|
||||
* Notes the given appop.
|
||||
*/
|
||||
public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
|
||||
return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies app ops that the given identity is no longer using location at high power levels.
|
||||
*/
|
||||
public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
|
||||
finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes access to any mock location APIs. If this call returns false, access to the APIs should
|
||||
* silently fail.
|
||||
*/
|
||||
public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
|
||||
return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
|
||||
}
|
||||
|
||||
protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
|
||||
|
||||
protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.location.util;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.location.LocationRequestStatistics;
|
||||
|
||||
/**
|
||||
* Injects various location dependencies so that they may be controlled by tests.
|
||||
@@ -30,15 +31,27 @@ public interface Injector {
|
||||
/** Returns an AppOpsHelper. */
|
||||
AppOpsHelper getAppOpsHelper();
|
||||
|
||||
/** Returns a LocationPermissionsHelper. */
|
||||
LocationPermissionsHelper getLocationPermissionsHelper();
|
||||
|
||||
/** Returns a SettingsHelper. */
|
||||
SettingsHelper getSettingsHelper();
|
||||
|
||||
/** Returns an AppForegroundHelper. */
|
||||
AppForegroundHelper getAppForegroundHelper();
|
||||
|
||||
/** Returns a LocationUsageLogger. */
|
||||
LocationUsageLogger getLocationUsageLogger();
|
||||
/** Returns a LocationPowerSaveModeHelper. */
|
||||
LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
|
||||
|
||||
/** Returns a ScreenInteractiveHelper. */
|
||||
ScreenInteractiveHelper getScreenInteractiveHelper();
|
||||
|
||||
/** Returns a LocationAttributionHelper. */
|
||||
LocationAttributionHelper getLocationAttributionHelper();
|
||||
|
||||
/** Returns a LocationUsageLogger. */
|
||||
LocationUsageLogger getLocationUsageLogger();
|
||||
|
||||
/** Returns a LocationRequestStatistics. */
|
||||
LocationRequestStatistics getLocationRequestStatistics();
|
||||
}
|
||||
|
||||
@@ -16,9 +16,16 @@
|
||||
|
||||
package com.android.server.location.util;
|
||||
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
|
||||
import static com.android.server.location.LocationManagerService.D;
|
||||
import static com.android.server.location.LocationManagerService.TAG;
|
||||
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
@@ -83,7 +90,7 @@ public class LocationAttributionHelper {
|
||||
i -> new ArraySet<>());
|
||||
boolean empty = keySet.isEmpty();
|
||||
if (keySet.add(new ProviderListener(provider, key)) && empty) {
|
||||
if (!mAppOpsHelper.startLocationMonitoring(identity)) {
|
||||
if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
|
||||
mAttributions.remove(identity);
|
||||
}
|
||||
}
|
||||
@@ -99,7 +106,7 @@ public class LocationAttributionHelper {
|
||||
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
|
||||
&& keySet.isEmpty()) {
|
||||
mAttributions.remove(identity);
|
||||
mAppOpsHelper.stopLocationMonitoring(identity);
|
||||
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +120,18 @@ public class LocationAttributionHelper {
|
||||
i -> new ArraySet<>());
|
||||
boolean empty = keySet.isEmpty();
|
||||
if (keySet.add(new ProviderListener(provider, key)) && empty) {
|
||||
if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
|
||||
if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
|
||||
if (D) {
|
||||
Log.v(TAG, "starting high power location attribution for " + identity);
|
||||
}
|
||||
} else {
|
||||
mHighPowerAttributions.remove(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report high power location usage has stopped for the given caller on the given provider,
|
||||
* Report high power location usage has stopped for the given caller on the given provider,
|
||||
* with a unique key.
|
||||
*/
|
||||
public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
|
||||
@@ -128,8 +139,11 @@ public class LocationAttributionHelper {
|
||||
Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
|
||||
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
|
||||
&& keySet.isEmpty()) {
|
||||
if (D) {
|
||||
Log.v(TAG, "stopping high power location attribution for " + identity);
|
||||
}
|
||||
mHighPowerAttributions.remove(identity);
|
||||
mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
|
||||
mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
|
||||
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
|
||||
import com.android.server.location.LocationPermissions;
|
||||
import com.android.server.location.LocationPermissions.PermissionLevel;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Provides helpers and listeners for appops.
|
||||
*/
|
||||
public abstract class LocationPermissionsHelper {
|
||||
|
||||
/**
|
||||
* Listener for current user changes.
|
||||
*/
|
||||
public interface LocationPermissionsListener {
|
||||
|
||||
/**
|
||||
* Called when something has changed about location permissions for the given package.
|
||||
*/
|
||||
void onLocationPermissionsChanged(String packageName);
|
||||
|
||||
/**
|
||||
* Called when something has changed about location permissions for the given uid.
|
||||
*/
|
||||
void onLocationPermissionsChanged(int uid);
|
||||
}
|
||||
|
||||
private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
|
||||
private final AppOpsHelper mAppOps;
|
||||
|
||||
public LocationPermissionsHelper(AppOpsHelper appOps) {
|
||||
mListeners = new CopyOnWriteArrayList<>();
|
||||
mAppOps = appOps;
|
||||
|
||||
mAppOps.addListener(this::onAppOpsChanged);
|
||||
}
|
||||
|
||||
protected final void notifyLocationPermissionsChanged(String packageName) {
|
||||
for (LocationPermissionsListener listener : mListeners) {
|
||||
listener.onLocationPermissionsChanged(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void notifyLocationPermissionsChanged(int uid) {
|
||||
for (LocationPermissionsListener listener : mListeners) {
|
||||
listener.onLocationPermissionsChanged(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void onAppOpsChanged(String packageName) {
|
||||
notifyLocationPermissionsChanged(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
|
||||
*/
|
||||
public final void addListener(LocationPermissionsListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for location permissions events.
|
||||
*/
|
||||
public final void removeListener(LocationPermissionsListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given identity may access location at the given permissions level, taking
|
||||
* into account both permissions and appops.
|
||||
*/
|
||||
public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
|
||||
CallerIdentity identity) {
|
||||
if (permissionLevel == PERMISSION_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mAppOps.checkOpNoThrow(permissionLevel, identity);
|
||||
}
|
||||
|
||||
protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.os.PowerManager.LocationPowerSaveMode;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for location power save mode.
|
||||
*/
|
||||
public abstract class LocationPowerSaveModeHelper {
|
||||
|
||||
/**
|
||||
* Listener for location power save mode changes.
|
||||
*/
|
||||
public interface LocationPowerSaveModeChangedListener {
|
||||
/**
|
||||
* Called when the location power save mode changes.
|
||||
*/
|
||||
void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
|
||||
}
|
||||
|
||||
private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
|
||||
|
||||
public LocationPowerSaveModeHelper() {
|
||||
mListeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes to location power save mode. Callbacks occur on an unspecified
|
||||
* thread.
|
||||
*/
|
||||
public final void addListener(LocationPowerSaveModeChangedListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for changes to location power save mode.
|
||||
*/
|
||||
public final void removeListener(LocationPowerSaveModeChangedListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
protected final void notifyLocationPowerSaveModeChanged(
|
||||
@LocationPowerSaveMode int locationPowerSaveMode) {
|
||||
for (LocationPowerSaveModeChangedListener listener : mListeners) {
|
||||
listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current location power save mode.
|
||||
*/
|
||||
@LocationPowerSaveMode
|
||||
public abstract int getLocationPowerSaveMode();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for screen interactive state (screen on/off).
|
||||
*/
|
||||
public abstract class ScreenInteractiveHelper {
|
||||
|
||||
/**
|
||||
* Listener for screen interactive changes.
|
||||
*/
|
||||
public interface ScreenInteractiveChangedListener {
|
||||
/**
|
||||
* Called when the screen interative state changes.
|
||||
*/
|
||||
void onScreenInteractiveChanged(boolean isInteractive);
|
||||
}
|
||||
|
||||
private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
|
||||
|
||||
public ScreenInteractiveHelper() {
|
||||
mListeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
|
||||
* thread.
|
||||
*/
|
||||
public final void addListener(ScreenInteractiveChangedListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for changes to screen interactive state.
|
||||
*/
|
||||
public final void removeListener(ScreenInteractiveChangedListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
protected final void notifyScreenInteractiveChanged(boolean interactive) {
|
||||
for (ScreenInteractiveChangedListener listener : mListeners) {
|
||||
listener.onScreenInteractiveChanged(interactive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the screen is currently interactive, and false otherwise.
|
||||
*/
|
||||
public abstract boolean isInteractive();
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
Preconditions.checkState(mAppOps != null);
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -75,7 +75,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finishOp(int appOp, CallerIdentity callerIdentity) {
|
||||
public void finishOp(int appOp, CallerIdentity callerIdentity) {
|
||||
Preconditions.checkState(mAppOps != null);
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -91,7 +91,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
Preconditions.checkState(mAppOps != null);
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -106,7 +106,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
|
||||
Preconditions.checkState(mAppOps != null);
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -123,7 +123,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
Preconditions.checkState(mAppOps != null);
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
import android.os.Binder;
|
||||
|
||||
import com.android.server.FgThread;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for location permissions, including appops.
|
||||
*/
|
||||
public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private boolean mInited;
|
||||
|
||||
public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
|
||||
super(appOps);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/** Called when system is ready. */
|
||||
public void onSystemReady() {
|
||||
if (mInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
mContext.getPackageManager().addOnPermissionsChangeListener(
|
||||
uid -> {
|
||||
// invoked on ui thread, move to fg thread so ui thread isn't blocked
|
||||
FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
|
||||
});
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mContext.checkPermission(permission, callerIdentity.getPid(),
|
||||
callerIdentity.getUid()) == PERMISSION_GRANTED;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.LocationPowerSaveMode;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for location power save mode.
|
||||
*/
|
||||
public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
|
||||
Consumer<PowerSaveState> {
|
||||
|
||||
private final Context mContext;
|
||||
private boolean mReady;
|
||||
|
||||
@LocationPowerSaveMode
|
||||
private volatile int mLocationPowerSaveMode;
|
||||
|
||||
public SystemLocationPowerSaveModeHelper(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/** Called when system is ready. */
|
||||
public void onSystemReady() {
|
||||
if (mReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
|
||||
PowerManager.ServiceType.LOCATION, this);
|
||||
mLocationPowerSaveMode = Objects.requireNonNull(
|
||||
mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
|
||||
|
||||
mReady = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PowerSaveState powerSaveState) {
|
||||
int locationPowerSaveMode;
|
||||
if (!powerSaveState.batterySaverEnabled) {
|
||||
locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
|
||||
} else {
|
||||
locationPowerSaveMode = powerSaveState.locationMode;
|
||||
}
|
||||
|
||||
if (locationPowerSaveMode == mLocationPowerSaveMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLocationPowerSaveMode = locationPowerSaveMode;
|
||||
|
||||
// invoked on ui thread, move to fg thread so we don't block the ui thread
|
||||
FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
|
||||
}
|
||||
|
||||
@LocationPowerSaveMode
|
||||
@Override
|
||||
public int getLocationPowerSaveMode() {
|
||||
Preconditions.checkState(mReady);
|
||||
return mLocationPowerSaveMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.FgThread;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for screen interactive state (screen on/off).
|
||||
*/
|
||||
public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private boolean mReady;
|
||||
|
||||
private volatile boolean mIsInteractive;
|
||||
|
||||
public SystemScreenInteractiveHelper(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/** Called when system is ready. */
|
||||
public void onSystemReady() {
|
||||
if (mReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
IntentFilter screenIntentFilter = new IntentFilter();
|
||||
screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
mContext.registerReceiverAsUser(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
boolean interactive;
|
||||
if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
|
||||
interactive = true;
|
||||
} else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
|
||||
interactive = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
onScreenInteractiveChanged(interactive);
|
||||
}
|
||||
}, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
|
||||
|
||||
mReady = true;
|
||||
}
|
||||
|
||||
private void onScreenInteractiveChanged(boolean interactive) {
|
||||
if (interactive == mIsInteractive) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsInteractive = interactive;
|
||||
notifyScreenInteractiveChanged(interactive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInteractive() {
|
||||
Preconditions.checkState(mReady);
|
||||
return mIsInteractive;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
|
||||
if (!myAppOp.mAllowed) {
|
||||
return false;
|
||||
@@ -68,20 +68,20 @@ public class FakeAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finishOp(int appOp, CallerIdentity callerIdentity) {
|
||||
public void finishOp(int appOp, CallerIdentity callerIdentity) {
|
||||
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
|
||||
Preconditions.checkState(myAppOp.mStarted);
|
||||
myAppOp.mStarted = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
|
||||
return myAppOp.mAllowed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
|
||||
if (!noteOpNoThrow(appOp, callerIdentity)) {
|
||||
throw new SecurityException(
|
||||
"noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
|
||||
@@ -91,7 +91,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
|
||||
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
|
||||
if (!myAppOp.mAllowed) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.location.util.identity.CallerIdentity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
|
||||
* otherwise.
|
||||
*/
|
||||
public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
|
||||
|
||||
private final HashMap<String, Set<String>> mRevokedPermissions;
|
||||
|
||||
public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
|
||||
super(appOps);
|
||||
mRevokedPermissions = new HashMap<>();
|
||||
}
|
||||
|
||||
public void grantPermission(String packageName, String permission) {
|
||||
getRevokedPermissionsList(packageName).remove(permission);
|
||||
notifyLocationPermissionsChanged(packageName);
|
||||
}
|
||||
|
||||
public void revokePermission(String packageName, String permission) {
|
||||
getRevokedPermissionsList(packageName).add(permission);
|
||||
notifyLocationPermissionsChanged(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(String permission, CallerIdentity identity) {
|
||||
return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
|
||||
}
|
||||
|
||||
private Set<String> getRevokedPermissionsList(String packageName) {
|
||||
return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.os.IPowerManager;
|
||||
import android.os.PowerManager.LocationPowerSaveMode;
|
||||
|
||||
/**
|
||||
* Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
|
||||
* change".
|
||||
*/
|
||||
public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
|
||||
|
||||
@LocationPowerSaveMode
|
||||
private int mLocationPowerSaveMode;
|
||||
|
||||
public FakeLocationPowerSaveModeHelper() {
|
||||
mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
|
||||
}
|
||||
|
||||
public void setLocationPowerSaveMode(int locationPowerSaveMode) {
|
||||
if (locationPowerSaveMode == mLocationPowerSaveMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLocationPowerSaveMode = locationPowerSaveMode;
|
||||
notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
|
||||
}
|
||||
|
||||
@LocationPowerSaveMode
|
||||
@Override
|
||||
public int getLocationPowerSaveMode() {
|
||||
return mLocationPowerSaveMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
/**
|
||||
* Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
|
||||
*/
|
||||
public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
|
||||
|
||||
private boolean mIsInteractive;
|
||||
|
||||
public FakeScreenInteractiveHelper() {
|
||||
mIsInteractive = true;
|
||||
}
|
||||
|
||||
public void setScreenInteractive(boolean interactive) {
|
||||
if (interactive == mIsInteractive) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsInteractive = interactive;
|
||||
notifyScreenInteractiveChanged(interactive);
|
||||
}
|
||||
|
||||
public boolean isInteractive() {
|
||||
return mIsInteractive;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class FakeSettingsHelper extends SettingsHelper {
|
||||
|
||||
@Override
|
||||
public boolean isLocationEnabled(int userId) {
|
||||
return mLocationEnabledSetting.getValue(Boolean.class);
|
||||
return mLocationEnabledSetting.getValue(userId, Boolean.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.location.util;
|
||||
|
||||
import android.os.Process;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.IntArray;
|
||||
import android.util.SparseArray;
|
||||
@@ -27,19 +26,21 @@ import com.android.internal.util.Preconditions;
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
/**
|
||||
* Version of UserInfoHelper for testing. The user this code is running under is set as the current
|
||||
* user by default, with no profiles.
|
||||
* Version of UserInfoHelper for testing. By default there is one user that starts in a running
|
||||
* state with a userId of 0;
|
||||
*/
|
||||
public class FakeUserInfoHelper extends UserInfoHelper {
|
||||
|
||||
public static final int DEFAULT_USERID = 0;
|
||||
|
||||
private final IntArray mRunningUserIds;
|
||||
private final SparseArray<IntArray> mProfiles;
|
||||
|
||||
private int mCurrentUserId;
|
||||
|
||||
public FakeUserInfoHelper() {
|
||||
mCurrentUserId = Process.myUserHandle().getIdentifier();
|
||||
mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
|
||||
mCurrentUserId = DEFAULT_USERID;
|
||||
mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
|
||||
mProfiles = new SparseArray<>();
|
||||
}
|
||||
|
||||
@@ -67,6 +68,10 @@ public class FakeUserInfoHelper extends UserInfoHelper {
|
||||
dispatchOnUserStopped(userId);
|
||||
}
|
||||
|
||||
public void setCurrentUserId(int parentUser) {
|
||||
setCurrentUserIds(parentUser, new int[]{parentUser});
|
||||
}
|
||||
|
||||
public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
|
||||
Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
|
||||
int oldUserId = mCurrentUserId;
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package com.android.server.location.util;
|
||||
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -46,9 +50,7 @@ public class LocationAttributionHelperTest {
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
|
||||
when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
|
||||
when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
|
||||
true);
|
||||
when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
|
||||
|
||||
mHelper = new LocationAttributionHelper(mAppOpsHelper);
|
||||
}
|
||||
@@ -63,30 +65,30 @@ public class LocationAttributionHelperTest {
|
||||
Object key4 = new Object();
|
||||
|
||||
mHelper.reportLocationStart(caller1, "gps", key1);
|
||||
verify(mAppOpsHelper).startLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
|
||||
|
||||
mHelper.reportLocationStart(caller1, "gps", key2);
|
||||
verify(mAppOpsHelper).startLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
|
||||
|
||||
mHelper.reportLocationStart(caller2, "gps", key3);
|
||||
verify(mAppOpsHelper).startLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
|
||||
|
||||
mHelper.reportLocationStart(caller2, "gps", key4);
|
||||
verify(mAppOpsHelper).startLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
|
||||
|
||||
mHelper.reportLocationStop(caller1, "gps", key2);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
|
||||
mHelper.reportLocationStop(caller1, "gps", key1);
|
||||
verify(mAppOpsHelper).stopLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
|
||||
|
||||
mHelper.reportLocationStop(caller2, "gps", key3);
|
||||
verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
|
||||
mHelper.reportLocationStop(caller2, "gps", key4);
|
||||
verify(mAppOpsHelper).stopLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,29 +101,29 @@ public class LocationAttributionHelperTest {
|
||||
Object key4 = new Object();
|
||||
|
||||
mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
|
||||
verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
|
||||
mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
|
||||
verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
|
||||
mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
|
||||
verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
|
||||
mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
|
||||
verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
|
||||
mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
|
||||
verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
|
||||
verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
|
||||
|
||||
mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
|
||||
verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
|
||||
verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
|
||||
verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,8 @@ import static android.app.AppOpsManager.MODE_IGNORED;
|
||||
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_FINE_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
|
||||
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -34,10 +30,12 @@ import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
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 static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
@@ -105,41 +103,41 @@ public class SystemAppOpsHelperTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckLocationAccess() {
|
||||
public void testCheckOp() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
|
||||
assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
|
||||
assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
|
||||
assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
|
||||
assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
|
||||
|
||||
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
|
||||
assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
|
||||
assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
|
||||
assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
|
||||
assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoteLocationAccess() {
|
||||
public void testNoteOpNoThrow() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
|
||||
assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
|
||||
assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
|
||||
|
||||
|
||||
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
@@ -147,74 +145,55 @@ public class SystemAppOpsHelperTest {
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
|
||||
assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
|
||||
assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartLocationMonitoring() {
|
||||
public void testStartOp() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq(false), eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
|
||||
assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
|
||||
eq(false), eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
|
||||
assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartHighPowerLocationMonitoring() {
|
||||
public void testFinishOp() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
|
||||
eq("mypackage"),
|
||||
eq(false), eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
|
||||
eq("mypackage"),
|
||||
eq(false), eq("myfeature"), nullable(String.class));
|
||||
assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopLocationMonitoring() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
mHelper.stopLocationMonitoring(identity);
|
||||
mHelper.finishOp(OP_MONITOR_LOCATION, identity);
|
||||
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopHighPowerLocationMonitoring() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
mHelper.stopHighPowerLocationMonitoring(identity);
|
||||
verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoteMockLocationAccess() {
|
||||
public void testNoteOp() {
|
||||
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
|
||||
|
||||
doReturn(MODE_ALLOWED).when(
|
||||
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
|
||||
nullable(String.class));
|
||||
assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
|
||||
assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
|
||||
|
||||
doReturn(MODE_IGNORED).when(
|
||||
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
|
||||
nullable(String.class));
|
||||
assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
|
||||
assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
|
||||
|
||||
|
||||
doThrow(new SecurityException()).when(
|
||||
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
|
||||
nullable(String.class));
|
||||
assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
|
||||
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
|
||||
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
|
||||
import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
|
||||
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.after;
|
||||
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.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
|
||||
|
||||
import org.junit.After;
|
||||
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;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SystemLocationPowerSaveModeHelperTest {
|
||||
|
||||
private static final long TIMEOUT_MS = 5000;
|
||||
private static final long FAILURE_TIMEOUT_MS = 200;
|
||||
|
||||
@Mock
|
||||
private PowerManagerInternal mPowerManagerInternal;
|
||||
|
||||
private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
|
||||
|
||||
private SystemLocationPowerSaveModeHelper mHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
|
||||
LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
|
||||
|
||||
doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
|
||||
mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
|
||||
|
||||
PowerManager powerManager = mock(PowerManager.class);
|
||||
doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
|
||||
Context context = mock(Context.class);
|
||||
doReturn(powerManager).when(context).getSystemService(PowerManager.class);
|
||||
|
||||
mHelper = new SystemLocationPowerSaveModeHelper(context);
|
||||
mHelper.onSystemReady();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
LocalServices.removeServiceForTest(PowerManagerInternal.class);
|
||||
}
|
||||
|
||||
private void sendPowerSaveState(PowerSaveState powerSaveState) {
|
||||
for (Consumer<PowerSaveState> listener : mListeners) {
|
||||
listener.accept(powerSaveState);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener() {
|
||||
LocationPowerSaveModeChangedListener listener = mock(
|
||||
LocationPowerSaveModeChangedListener.class);
|
||||
mHelper.addListener(listener);
|
||||
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
|
||||
false).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
|
||||
verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
|
||||
anyInt());
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
|
||||
verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
|
||||
LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
|
||||
LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
|
||||
verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
|
||||
LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
|
||||
LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
|
||||
verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
|
||||
LOCATION_MODE_FOREGROUND_ONLY);
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
|
||||
true).build());
|
||||
verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
|
||||
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
|
||||
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
|
||||
sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
|
||||
LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
|
||||
verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
|
||||
LOCATION_MODE_NO_CHANGE);
|
||||
assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,32 @@
|
||||
|
||||
package com.android.server.location.util;
|
||||
|
||||
import com.android.server.location.LocationRequestStatistics;
|
||||
|
||||
public class TestInjector implements Injector {
|
||||
|
||||
private final FakeUserInfoHelper mUserInfoHelper;
|
||||
private final FakeAppOpsHelper mAppOpsHelper;
|
||||
private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
|
||||
private final FakeSettingsHelper mSettingsHelper;
|
||||
private final FakeAppForegroundHelper mAppForegroundHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
|
||||
private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
|
||||
private final LocationAttributionHelper mLocationAttributionHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
private final LocationRequestStatistics mLocationRequestStatistics;
|
||||
|
||||
public TestInjector() {
|
||||
mUserInfoHelper = new FakeUserInfoHelper();
|
||||
mAppOpsHelper = new FakeAppOpsHelper();
|
||||
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
|
||||
mSettingsHelper = new FakeSettingsHelper();
|
||||
mAppForegroundHelper = new FakeAppForegroundHelper();
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
|
||||
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
|
||||
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
mLocationRequestStatistics = new LocationRequestStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,6 +54,11 @@ public class TestInjector implements Injector {
|
||||
return mAppOpsHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
|
||||
return mLocationPermissionsHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeSettingsHelper getSettingsHelper() {
|
||||
return mSettingsHelper;
|
||||
@@ -55,12 +70,27 @@ public class TestInjector implements Injector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationUsageLogger getLocationUsageLogger() {
|
||||
return mLocationUsageLogger;
|
||||
public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
|
||||
return mLocationPowerSaveModeHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
|
||||
return mScreenInteractiveHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationAttributionHelper getLocationAttributionHelper() {
|
||||
return mLocationAttributionHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationUsageLogger getLocationUsageLogger() {
|
||||
return mLocationUsageLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationRequestStatistics getLocationRequestStatistics() {
|
||||
return mLocationRequestStatistics;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user