Refactor appops across location

-Add a new app ops helper to make testing easier.
-Consolidate app identity within CallerIdentity class.
-Remove location age restriction for coarse locations, was a bit
arbitrary.
-Remove listener identifiers from LM. These were not being properly
propagated and add a lot of binder overhead with what appears to be
little benefit since we have featureIds, which contain much better
information.
-Remove appops checks from some GNSS APIs that shouldn't require it.
-Move location fudger into location providers and reset them after mock
providers are used so that offset information cannot be leaked.

Test: presubmits + manual
Change-Id: I0ab6318093b1a7c7210ce9e39ccccf75d4f2eb66
This commit is contained in:
Soonil Nagarkar
2020-02-21 10:09:02 -08:00
parent 22ca4ddb6a
commit 6033344baa
20 changed files with 1152 additions and 847 deletions

View File

@@ -9624,14 +9624,6 @@ public final class Settings {
public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
"location_ignore_settings_package_whitelist";
/**
* Maximum staleness allowed for last location when returned to clients with only foreground
* location permissions.
* @hide
*/
public static final String LOCATION_LAST_LOCATION_MAX_AGE_MILLIS =
"location_last_location_max_age_millis";
/**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)

View File

@@ -47,16 +47,14 @@ interface ILocationManager
Location getLastLocation(in LocationRequest request, String packageName, String featureId);
boolean getCurrentLocation(in LocationRequest request,
in ICancellationSignal cancellationSignal, in ILocationListener listener,
String packageName, String featureId, String listenerIdentifier);
String packageName, String featureId);
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
in PendingIntent intent, String packageName, String featureId,
String listenerIdentifier);
void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
in PendingIntent intent, String packageName, String featureId);
void removeUpdates(in ILocationListener listener, in PendingIntent intent);
void requestGeofence(in LocationRequest request, in Geofence geofence,
in PendingIntent intent, String packageName, String featureId,
String listenerIdentifier);
in PendingIntent intent, String packageName, String featureId);
void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName,
@@ -73,34 +71,31 @@ interface ILocationManager
boolean addGnssMeasurementsListener(in GnssRequest request,
in IGnssMeasurementsListener listener,
String packageName, String featureId,
String listenerIdentifier);
String packageName, String featureId);
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
in String packageName);
long getGnssCapabilities(in String packageName);
long getGnssCapabilities();
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener,
String packageName, String featureId, String listenerIdentifier);
String packageName, String featureId);
void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
String packageName, String featureId, String listenerIdentifier);
String packageName, String featureId);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
int getGnssYearOfHardware();
String getGnssHardwareModelName();
int getGnssBatchSize(String packageName);
boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
String featureId, String listenerIdentifier);
boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId);
void removeGnssBatchingCallback();
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId);
void flushGnssBatch(String packageName);
boolean stopGnssBatch();
void injectLocation(in Location location);
@UnsupportedAppUsage
List<String> getAllProviders();
List<String> getProviders(in Criteria criteria, boolean enabledOnly);
String getBestProvider(in Criteria criteria, boolean enabledOnly);
@@ -116,11 +111,11 @@ interface ILocationManager
boolean isProviderEnabledForUser(String provider, int userId);
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
void setTestProviderLocation(String provider, in Location loc, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
List<LocationRequest> getTestProviderCurrentRequests(String provider, String opPackageName);
void addTestProvider(String name, in ProviderProperties properties, String packageName, String featureId);
void removeTestProvider(String provider, String packageName, String featureId);
void setTestProviderLocation(String provider, in Location location, String packageName, String featureId);
void setTestProviderEnabled(String provider, boolean enabled, String packageName, String featureId);
List<LocationRequest> getTestProviderCurrentRequests(String provider);
LocationTime getGnssTimeMillis();
boolean sendExtraCommand(String provider, String command, inout Bundle extras);

View File

@@ -585,6 +585,16 @@ public class Location implements Parcelable {
return mElapsedRealtimeNanos;
}
/** @hide */
public long getElapsedRealtimeAgeNanos(long referenceRealtimeNs) {
return referenceRealtimeNs - mElapsedRealtimeNanos;
}
/** @hide */
public long getElapsedRealtimeAgeNanos() {
return getElapsedRealtimeAgeNanos(SystemClock.elapsedRealtimeNanos());
}
/**
* Set the time of this fix, in elapsed real-time since system boot.
*

View File

@@ -641,19 +641,6 @@ public class LocationManager {
}
}
/**
* Create a string that allows an app to identify a listener
*
* @param listener The listener
*
* @return A identifying string
*/
private static String getListenerIdentifier(@NonNull Object listener) {
return listener.getClass().getName()
+ '@'
+ Integer.toHexString(System.identityHashCode(listener));
}
/**
* Asynchronously returns a single current location fix. This may activate sensors in order to
* compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return
@@ -732,8 +719,7 @@ public class LocationManager {
try {
if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
listenerTransport, mContext.getPackageName(), mContext.getFeatureId(),
getListenerIdentifier(consumer))) {
listenerTransport, mContext.getPackageName(), mContext.getFeatureId())) {
listenerTransport.register(mContext.getSystemService(AlarmManager.class),
remoteCancellationSignal);
if (cancellationSignal != null) {
@@ -1179,8 +1165,7 @@ public class LocationManager {
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
mContext.getPackageName(), mContext.getFeatureId(),
getListenerIdentifier(listener));
mContext.getPackageName(), mContext.getFeatureId());
registered = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1225,8 +1210,7 @@ public class LocationManager {
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
mContext.getPackageName(), mContext.getFeatureId(),
getListenerIdentifier(pendingIntent));
mContext.getPackageName(), mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1283,7 +1267,7 @@ public class LocationManager {
transport.unregister();
try {
mService.removeUpdates(transport, null, mContext.getPackageName());
mService.removeUpdates(transport, null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1302,7 +1286,7 @@ public class LocationManager {
Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
try {
mService.removeUpdates(null, pendingIntent, mContext.getPackageName());
mService.removeUpdates(null, pendingIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1489,7 +1473,8 @@ public class LocationManager {
requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
supportsBearing, powerRequirement, accuracy);
try {
mService.addTestProvider(provider, properties, mContext.getOpPackageName());
mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1510,7 +1495,8 @@ public class LocationManager {
Preconditions.checkArgument(provider != null, "invalid null provider");
try {
mService.removeTestProvider(provider, mContext.getOpPackageName());
mService.removeTestProvider(provider, mContext.getOpPackageName(),
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1544,7 +1530,8 @@ public class LocationManager {
}
try {
mService.setTestProviderLocation(provider, location, mContext.getOpPackageName());
mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1573,7 +1560,8 @@ public class LocationManager {
Preconditions.checkArgument(provider != null, "invalid null provider");
try {
mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1617,8 +1605,7 @@ public class LocationManager {
public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
Preconditions.checkArgument(providerName != null, "invalid null provider");
try {
return mService.getTestProviderCurrentRequests(providerName,
mContext.getOpPackageName());
return mService.getTestProviderCurrentRequests(providerName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1685,7 +1672,7 @@ public class LocationManager {
LocationRequest request = new LocationRequest().setExpireIn(expiration);
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
mContext.getFeatureId(), getListenerIdentifier(intent));
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1772,7 +1759,7 @@ public class LocationManager {
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
mContext.getFeatureId(), getListenerIdentifier(intent));
mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1841,7 +1828,7 @@ public class LocationManager {
*/
public @NonNull GnssCapabilities getGnssCapabilities() {
try {
long gnssCapabilities = mService.getGnssCapabilities(mContext.getPackageName());
long gnssCapabilities = mService.getGnssCapabilities();
if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
gnssCapabilities = 0L;
}
@@ -2467,7 +2454,7 @@ public class LocationManager {
try {
if (mBatchedLocationCallbackManager.addListener(callback, handler)) {
return mService.startGnssBatch(periodNanos, wakeOnFifoFull,
mContext.getPackageName());
mContext.getPackageName(), mContext.getFeatureId());
}
return false;
} catch (RemoteException e) {
@@ -2980,7 +2967,7 @@ public class LocationManager {
GnssMeasurementsListener transport = new GnssMeasurementsListener();
if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
mContext.getFeatureId(), "gnss measurement callback")) {
mContext.getFeatureId())) {
mListenerTransport = transport;
return true;
} else {
@@ -3033,7 +3020,7 @@ public class LocationManager {
GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
mContext.getFeatureId(), "gnss navigation callback")) {
mContext.getFeatureId())) {
mListenerTransport = transport;
return true;
} else {
@@ -3074,7 +3061,7 @@ public class LocationManager {
GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
mContext.getFeatureId(), "gnss antenna info callback")) {
mContext.getFeatureId())) {
mListenerTransport = transport;
return true;
} else {
@@ -3111,7 +3098,7 @@ public class LocationManager {
BatchedLocationCallback transport = new BatchedLocationCallback();
if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
mContext.getFeatureId(), "batched location callback")) {
mContext.getFeatureId())) {
mListenerTransport = transport;
return true;
} else {

View File

@@ -317,7 +317,6 @@ public class SettingsBackupTest {
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,

View File

@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.server.location.CallerIdentity;
@@ -32,9 +31,6 @@ import java.util.function.Consumer;
*/
public class LocationManagerServiceUtils {
private static final String TAG = "LocManagerServiceUtils";
private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
/**
* Listener that can be linked to a binder.
* @param <TListener> listener type
@@ -49,10 +45,9 @@ public class LocationManagerServiceUtils {
public LinkedListener(
@Nullable TRequest request,
@NonNull TListener listener,
String listenerName,
@NonNull CallerIdentity callerIdentity,
@NonNull Consumer<TListener> binderDeathCallback) {
super(callerIdentity, listenerName);
super(callerIdentity);
mListener = listener;
mRequest = request;
mBinderDeathCallback = binderDeathCallback;
@@ -65,7 +60,6 @@ public class LocationManagerServiceUtils {
@Override
public void binderDied() {
if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
mBinderDeathCallback.accept(mListener);
}
}
@@ -75,28 +69,20 @@ public class LocationManagerServiceUtils {
*/
public abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
protected final CallerIdentity mCallerIdentity;
protected final String mListenerName;
LinkedListenerBase(
@NonNull CallerIdentity callerIdentity, @NonNull String listenerName) {
LinkedListenerBase(@NonNull CallerIdentity callerIdentity) {
mCallerIdentity = callerIdentity;
mListenerName = listenerName;
}
@Override
public String toString() {
return mListenerName + "[" + mCallerIdentity.mPackageName + "(" + mCallerIdentity.mPid
+ ")]";
return mCallerIdentity.toString();
}
public CallerIdentity getCallerIdentity() {
return mCallerIdentity;
}
public String getListenerName() {
return mListenerName;
}
/**
* Link listener (i.e. callback) to a binder, so that it will be called upon binder's death.
*/
@@ -105,9 +91,6 @@ public class LocationManagerServiceUtils {
binder.linkToDeath(this, 0 /* flags */);
return true;
} catch (RemoteException e) {
// if the remote process registering the listener is already dead, just swallow the
// exception and return
Log.w(TAG, "Could not link " + mListenerName + " death callback.", e);
return false;
}
}
@@ -119,7 +102,7 @@ public class LocationManagerServiceUtils {
try {
binder.unlinkToDeath(this, 0 /* flags */);
} catch (NoSuchElementException e) {
Log.w(TAG, "Could not unlink " + mListenerName + " death callback.", e);
// ignore
}
}
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.location;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static com.android.server.LocationManagerService.D;
import static com.android.server.LocationManagerService.TAG;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Binder;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Provides helpers and listeners for appops.
*/
public class AppOpsHelper {
/**
* Listener for current user changes.
*/
public interface LocationAppOpListener {
/**
* Called when something has changed about a location appop for the given package.
*/
void onAppOpsChanged(String packageName);
}
private final Context mContext;
private final CopyOnWriteArrayList<LocationAppOpListener> mListeners;
@GuardedBy("this")
@Nullable
private AppOpsManager mAppOps;
public AppOpsHelper(Context context) {
mContext = context;
mListeners = new CopyOnWriteArrayList<>();
}
/** Called when system is ready. */
public synchronized void onSystemReady() {
if (mAppOps != null) {
return;
}
mAppOps = Objects.requireNonNull(mContext.getSystemService(AppOpsManager.class));
mAppOps.startWatchingMode(
AppOpsManager.OP_COARSE_LOCATION,
null,
AppOpsManager.WATCH_FOREGROUND_CHANGES,
new AppOpsManager.OnOpChangedInternalListener() {
public void onOpChanged(int op, String packageName) {
// invoked on ui thread, move to fg thread so ui thread isn't blocked
FgThread.getHandler().sendMessage(
PooledLambda.obtainMessage(AppOpsHelper::onAppOpChanged,
AppOpsHelper.this, packageName));
}
});
}
private void onAppOpChanged(String packageName) {
if (D) {
Log.v(TAG, "location appop changed for " + packageName);
}
for (LocationAppOpListener listener : mListeners) {
listener.onAppOpsChanged(packageName);
}
}
/**
* Adds a listener for app ops events. Callbacks occur on an unspecified thread.
*/
public void addListener(LocationAppOpListener listener) {
mListeners.add(listener);
}
/**
* Removes a listener for app ops events.
*/
public void removeListener(LocationAppOpListener listener) {
mListeners.remove(listener);
}
/**
* 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)},
* 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)
*/
public boolean checkLocationAccess(CallerIdentity callerIdentity) {
synchronized (this) {
Preconditions.checkState(mAppOps != null);
}
long identity = Binder.clearCallingIdentity();
try {
return mAppOps.checkOpNoThrow(
CallerIdentity.asAppOp(callerIdentity.permissionLevel),
callerIdentity.uid,
callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* 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.
*/
public boolean noteLocationAccess(CallerIdentity identity) {
return noteOpNoThrow(CallerIdentity.asAppOp(identity.permissionLevel), identity);
}
/**
* 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)}.
*/
public boolean startLocationMonitoring(CallerIdentity identity) {
return startLocationMonitoring(OP_MONITOR_LOCATION, identity);
}
/**
* Notifies app ops that the given identity is no longer using location at normal/low power
* levels.
*/
public void stopLocationMonitoring(CallerIdentity identity) {
stopLocationMonitoring(OP_MONITOR_LOCATION, identity);
}
/**
* 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)}.
*/
public boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
return startLocationMonitoring(OP_MONITOR_HIGH_POWER_LOCATION, identity);
}
/**
* Notifies app ops that the given identity is no longer using location at high power levels.
*/
public void stopHighPowerLocationMonitoring(CallerIdentity identity) {
stopLocationMonitoring(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 boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
synchronized (this) {
Preconditions.checkState(mAppOps != null);
}
long identity = Binder.clearCallingIdentity();
try {
// note that this is not the no throw version of noteOp, this call may throw exceptions
return mAppOps.noteOp(
AppOpsManager.OP_MOCK_LOCATION,
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
null) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private boolean startLocationMonitoring(int appOp, CallerIdentity callerIdentity) {
synchronized (this) {
Preconditions.checkState(mAppOps != null);
}
long identity = Binder.clearCallingIdentity();
try {
return mAppOps.startOpNoThrow(
appOp,
callerIdentity.uid,
callerIdentity.packageName,
false,
callerIdentity.featureId,
null) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private void stopLocationMonitoring(int appOp, CallerIdentity callerIdentity) {
synchronized (this) {
Preconditions.checkState(mAppOps != null);
}
long identity = Binder.clearCallingIdentity();
try {
mAppOps.finishOp(
appOp,
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
synchronized (this) {
Preconditions.checkState(mAppOps != null);
}
long identity = Binder.clearCallingIdentity();
try {
return mAppOps.noteOpNoThrow(
appOp,
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
null) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}

View File

@@ -16,25 +16,211 @@
package com.android.server.location;
import android.annotation.NonNull;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Binder;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Represents the calling process's uid, pid, and package name.
*/
public class CallerIdentity {
public final int mUid;
public final int mPid;
public final String mPackageName;
public final @Nullable String mFeatureId;
public final @NonNull String mListenerIdentifier;
public final class CallerIdentity {
public CallerIdentity(int uid, int pid, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
mUid = uid;
mPid = pid;
mPackageName = packageName;
mFeatureId = featureId;
mListenerIdentifier = listenerIdentifier;
public static final int PERMISSION_NONE = 0;
public static final int PERMISSION_COARSE = 1;
public static final int PERMISSION_FINE = 2;
@IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionLevel {}
/**
* Converts the given permission level to the corresponding permission.
*/
public static String asPermission(@PermissionLevel int permissionLevel) {
switch (permissionLevel) {
case PERMISSION_COARSE:
return ACCESS_COARSE_LOCATION;
case PERMISSION_FINE:
return ACCESS_FINE_LOCATION;
default:
throw new IllegalArgumentException();
}
}
/**
* Converts the given permission level to the corresponding appop.
*/
public static int asAppOp(@PermissionLevel int permissionLevel) {
switch (permissionLevel) {
case PERMISSION_COARSE:
return AppOpsManager.OP_COARSE_LOCATION;
case PERMISSION_FINE:
return AppOpsManager.OP_FINE_LOCATION;
default:
throw new IllegalArgumentException();
}
}
/**
* Creates a CallerIdentity from the current binder identity, using the given package and
* feature id. The package will be checked to enforce it belongs to the calling uid, and a
* security exception will be thrown if it is invalid.
*/
public static CallerIdentity fromBinder(Context context, String packageName,
@Nullable String featureId) {
int uid = Binder.getCallingUid();
if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) {
throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
}
return fromBinderUnsafe(context, packageName, featureId);
}
/**
* Creates a CallerIdentity from the current binder identity, using the given package and
* feature id. The package will not be checked to enforce that it belongs to the calling uid -
* this method should only be used if the package will be validated by some other means, such as
* an appops call.
*/
public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
@Nullable String featureId) {
return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId(), packageName, featureId,
getBinderPermissionLevel(context));
}
/**
* Throws a security exception if the caller does not hold a location permission.
*/
public static void enforceCallingOrSelfLocationPermission(Context context) {
enforceLocationPermission(Binder.getCallingUid(), getBinderPermissionLevel(context));
}
/**
* Returns false if the caller does not hold a location permission, true otherwise.
*/
public static boolean checkCallingOrSelfLocationPermission(Context context) {
return checkLocationPermission(getBinderPermissionLevel(context));
}
private static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel) {
if (checkLocationPermission(permissionLevel)) {
return;
}
throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION
+ " or " + ACCESS_FINE_LOCATION + ".");
}
private static boolean checkLocationPermission(@PermissionLevel int permissionLevel) {
return permissionLevel >= PERMISSION_COARSE;
}
private static @PermissionLevel int getBinderPermissionLevel(Context context) {
if (context.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) {
return PERMISSION_FINE;
}
if (context.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED) {
return PERMISSION_COARSE;
}
return PERMISSION_NONE;
}
/** The calling UID. */
public final int uid;
/** The calling PID. */
public final int pid;
/** The calling user. */
public final int userId;
/** The calling package name. */
public final String packageName;
/** The calling feature id. */
public final @Nullable String featureId;
/**
* The calling location permission level. This field should only be used for validating
* permissions for API access. It should not be used for validating permissions for location
* access - that must be done through appops.
*/
public final @PermissionLevel int permissionLevel;
@VisibleForTesting
public CallerIdentity(int uid, int pid, int userId, String packageName,
@Nullable String featureId, @PermissionLevel int permissionLevel) {
this.uid = uid;
this.pid = pid;
this.userId = userId;
this.packageName = Objects.requireNonNull(packageName);
this.featureId = featureId;
this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE,
PERMISSION_FINE, "permissionLevel");
}
/**
* Throws a security exception if the CallerIdentity does not hold a location permission.
*/
public void enforceLocationPermission() {
enforceLocationPermission(uid, permissionLevel);
}
@Override
public String toString() {
int length = 10 + packageName.length();
if (featureId != null) {
length += featureId.length();
}
StringBuilder builder = new StringBuilder(length);
builder.append(pid).append("/").append(packageName);
if (featureId != null) {
builder.append("[");
if (featureId.startsWith(packageName)) {
builder.append(featureId.substring(packageName.length()));
} else {
builder.append(featureId);
}
builder.append("]");
}
return builder.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CallerIdentity)) {
return false;
}
CallerIdentity that = (CallerIdentity) o;
return uid == that.uid
&& pid == that.pid
&& packageName.equals(that.packageName)
&& Objects.equals(featureId, that.featureId);
}
@Override
public int hashCode() {
return Objects.hash(uid, pid, packageName, featureId);
}
}

View File

@@ -16,9 +16,6 @@
package com.android.server.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
@@ -38,7 +35,6 @@ import android.util.Log;
import android.util.Slog;
import com.android.server.FgThread;
import com.android.server.LocationManagerService;
import com.android.server.PendingIntentUtils;
import java.io.PrintWriter;
@@ -125,16 +121,10 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
int allowedResolutionLevel, int uid, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
if (D) {
Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
+ ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
}
CallerIdentity identity) {
GeofenceState state = new GeofenceState(geofence,
request.getExpirationRealtimeMs(SystemClock.elapsedRealtime()),
allowedResolutionLevel, uid, packageName, featureId, listenerIdentifier, intent);
identity, intent);
synchronized (mLock) {
// first make sure it doesn't already exist
for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -182,26 +172,14 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
synchronized (mLock) {
Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
GeofenceState state = iter.next();
if (state.mPackageName.equals(packageName)) {
iter.remove();
}
}
mFences.removeIf(state -> state.mIdentity.packageName.equals(packageName));
scheduleUpdateFencesLocked();
}
}
private void removeExpiredFencesLocked() {
long time = SystemClock.elapsedRealtime();
Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
GeofenceState state = iter.next();
if (state.mExpireAt < time) {
iter.remove();
}
}
mFences.removeIf(state -> state.mExpireAt < time);
}
private void scheduleUpdateFencesLocked() {
@@ -266,24 +244,17 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
double minFenceDistance = Double.MAX_VALUE;
boolean needUpdates = false;
for (GeofenceState state : mFences) {
if (mSettingsStore.isLocationPackageBlacklisted(ActivityManager.getCurrentUser(),
state.mPackageName)) {
if (D) {
Slog.d(TAG, "skipping geofence processing for blacklisted app: "
+ state.mPackageName);
}
CallerIdentity identity = state.mIdentity;
if (mSettingsStore.isLocationPackageBlacklisted(identity.userId,
identity.packageName)) {
continue;
}
int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
int op = CallerIdentity.asAppOp(identity.permissionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
state.mPackageName, state.mFeatureId, state.mListenerIdentifier)
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid,
identity.packageName, identity.featureId, null)
!= AppOpsManager.MODE_ALLOWED) {
if (D) {
Slog.d(TAG, "skipping geofence processing for no op app: "
+ state.mPackageName);
}
continue;
}
}
@@ -429,7 +400,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
public void dump(PrintWriter pw) {
for (GeofenceState state : mFences) {
pw.println(state.mPackageName + " " + state.mFence);
pw.println(state.mIdentity + " " + state.mFence);
}
}

View File

@@ -17,8 +17,6 @@
package com.android.server.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.location.Geofence;
import android.location.Location;
@@ -37,29 +35,20 @@ public class GeofenceState {
public final Geofence mFence;
private final Location mLocation;
public final long mExpireAt;
public final int mAllowedResolutionLevel;
public final int mUid;
public final String mPackageName;
public final @Nullable String mFeatureId;
public final @NonNull String mListenerIdentifier;
public final CallerIdentity mIdentity;
public final PendingIntent mIntent;
int mState; // current state
double mDistanceToCenter; // current distance to center of fence
public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
String packageName, @Nullable String featureId, @NonNull String listenerIdentifier,
public GeofenceState(Geofence fence, long expireAt, CallerIdentity identity,
PendingIntent intent) {
mState = STATE_UNKNOWN;
mDistanceToCenter = Double.MAX_VALUE;
mFence = fence;
mExpireAt = expireAt;
mAllowedResolutionLevel = allowedResolutionLevel;
mUid = uid;
mPackageName = packageName;
mFeatureId = featureId;
mListenerIdentifier = listenerIdentifier;
mIdentity = identity;
mIntent = intent;
mLocation = new Location("");

View File

@@ -110,7 +110,7 @@ public abstract class GnssAntennaInfoProvider
foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> {
if (!hasPermission(mContext, callerIdentity)) {
logPermissionDisabledEventNotReported(
TAG, callerIdentity.mPackageName, "GNSS antenna info");
TAG, callerIdentity.packageName, "GNSS antenna info");
return;
}
listener.onGnssAntennaInfoReceived(gnssAntennaInfos);

View File

@@ -115,7 +115,7 @@ public abstract class GnssMeasurementsProvider
foreach((IGnssMeasurementsListener listener, CallerIdentity callerIdentity) -> {
if (!hasPermission(mContext, callerIdentity)) {
logPermissionDisabledEventNotReported(
TAG, callerIdentity.mPackageName, "GNSS measurements");
TAG, callerIdentity.packageName, "GNSS measurements");
return;
}
listener.onGnssMeasurementsReceived(event);

View File

@@ -76,7 +76,7 @@ public abstract class GnssStatusListenerHelper extends
final float[] basebandCn0s) {
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
if (!hasPermission(mContext, callerIdentity)) {
logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName,
logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName,
"GNSS status");
return;
}
@@ -88,7 +88,7 @@ public abstract class GnssStatusListenerHelper extends
public void onNmeaReceived(final long timestamp, final String nmea) {
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
if (!hasPermission(mContext, callerIdentity)) {
logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName, "NMEA");
logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName, "NMEA");
return;
}
listener.onNmeaReceived(timestamp, nmea);

View File

@@ -49,13 +49,13 @@ public final class LocationPermissionUtil {
private static boolean hasPermissionLocationHardware(Context context,
CallerIdentity callerIdentity) {
return context.checkPermission(android.Manifest.permission.LOCATION_HARDWARE,
callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED;
callerIdentity.pid, callerIdentity.uid) == PackageManager.PERMISSION_GRANTED;
}
private static boolean hasPermissionUpdateAppOpsStats(Context context,
CallerIdentity callerIdentity) {
return context.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED;
callerIdentity.pid, callerIdentity.uid) == PackageManager.PERMISSION_GRANTED;
}
private LocationPermissionUtil() {}

View File

@@ -179,13 +179,12 @@ public abstract class RemoteListenerHelper<TRequest, TListener extends IInterfac
if (LocationPermissionUtil.doesCallerReportToAppOps(context, callerIdentity)) {
// The caller is identified as a location provider that will report location
// access to AppOps. Skip noteOp but do checkOp to check for location permission.
return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.uid,
callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED;
}
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
callerIdentity.mPackageName, callerIdentity.mFeatureId,
"Location sent to " + callerIdentity.mListenerIdentifier)
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.uid,
callerIdentity.packageName, callerIdentity.featureId, null)
== AppOpsManager.MODE_ALLOWED;
}

View File

@@ -22,7 +22,6 @@ import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTE
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST;
import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M;
import static android.provider.Settings.Secure.LOCATION_MODE;
import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
@@ -92,7 +91,6 @@ public class SettingsHelper {
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
30 * 60 * 1000;
private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f;
private final Context mContext;
@@ -284,21 +282,6 @@ public class SettingsHelper {
}
}
/**
* Retrieve maximum age of the last location.
*/
public long getMaxLastLocationAgeMs() {
long identity = Binder.clearCallingIdentity();
try {
return Settings.Global.getLong(
mContext.getContentResolver(),
LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
DEFAULT_MAX_LAST_LOCATION_AGE_MS);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid
* coarsening.

View File

@@ -16,15 +16,11 @@
package com.android.server.location.gnss;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.location.LocationManager.GPS_PROVIDER;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssRequest;
import android.location.IBatchedLocationCallback;
@@ -41,7 +37,6 @@ import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.stats.location.LocationStatsEnums;
import android.util.ArrayMap;
import android.util.Log;
@@ -54,6 +49,7 @@ import com.android.server.LocalServices;
import com.android.server.LocationManagerServiceUtils.LinkedListener;
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.AppForegroundHelper;
import com.android.server.location.AppOpsHelper;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GnssAntennaInfoProvider;
import com.android.server.location.GnssBatchingProvider;
@@ -84,6 +80,7 @@ public class GnssManagerService {
}
private final Context mContext;
private final AppOpsHelper mAppOpsHelper;
private final SettingsHelper mSettingsHelper;
private final AppForegroundHelper mAppForegroundHelper;
private final LocationUsageLogger mLocationUsageLogger;
@@ -120,8 +117,6 @@ public class GnssManagerService {
@GuardedBy("this")
@Nullable private LocationManagerInternal mLocationManagerInternal;
@GuardedBy("this")
@Nullable private AppOpsManager mAppOpsManager;
private final Object mGnssBatchingLock = new Object();
@@ -135,19 +130,21 @@ public class GnssManagerService {
@GuardedBy("mGnssBatchingLock")
private boolean mGnssBatchingInProgress = false;
public GnssManagerService(Context context, SettingsHelper settingsHelper,
AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger) {
this(context, settingsHelper, appForegroundHelper, locationUsageLogger, null);
public GnssManagerService(Context context, AppOpsHelper appOpsHelper,
SettingsHelper settingsHelper, AppForegroundHelper appForegroundHelper,
LocationUsageLogger locationUsageLogger) {
this(context, appOpsHelper, settingsHelper, appForegroundHelper, locationUsageLogger, null);
}
// Can use this constructor to inject GnssLocationProvider for testing
@VisibleForTesting
GnssManagerService(Context context, SettingsHelper settingsHelper,
GnssManagerService(Context context, AppOpsHelper appOpsHelper, SettingsHelper settingsHelper,
AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger,
GnssLocationProvider gnssLocationProvider) {
Preconditions.checkState(isGnssSupported());
mContext = context;
mAppOpsHelper = appOpsHelper;
mSettingsHelper = settingsHelper;
mAppForegroundHelper = appForegroundHelper;
mLocationUsageLogger = locationUsageLogger;
@@ -177,11 +174,11 @@ public class GnssManagerService {
return;
}
mAppOpsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mAppForegroundHelper.addListener(this::onAppForegroundChanged);
}
@@ -215,11 +212,7 @@ public class GnssManagerService {
* Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in
* {@link android.location.GnssCapabilities}.
*/
public long getGnssCapabilities(String packageName) {
if (!checkLocationAppOp(packageName)) {
return GnssCapabilities.INVALID_CAPABILITIES;
}
public long getGnssCapabilities() {
return mGnssCapabilitiesProvider.getGnssCapabilities();
}
@@ -230,10 +223,6 @@ public class GnssManagerService {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return 0;
}
synchronized (mGnssBatchingLock) {
return mGnssBatchingProvider.getBatchSize();
}
@@ -243,11 +232,13 @@ public class GnssManagerService {
* Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered
* as a collection.
*/
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
String featureId) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
if (!mAppOpsHelper.checkLocationAccess(identity)) {
return false;
}
@@ -267,25 +258,19 @@ public class GnssManagerService {
* Adds a GNSS batching callback for delivering GNSS location batch results.
*/
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
@Nullable String featureId, @NonNull String listenerIdentity) {
@Nullable String featureId) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return false;
}
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
featureId, listenerIdentity);
synchronized (mGnssBatchingLock) {
mGnssBatchingCallback = callback;
mGnssBatchingDeathCallback =
new LinkedListener<>(
/* request= */ null,
callback,
"BatchedLocationCallback",
callerIdentity,
identity,
(IBatchedLocationCallback listener) -> {
stopGnssBatch();
removeGnssBatchingCallback();
@@ -305,10 +290,6 @@ public class GnssManagerService {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return;
}
synchronized (mGnssBatchingLock) {
mGnssBatchingProvider.flush();
}
@@ -386,7 +367,7 @@ public class GnssManagerService {
LinkedListener<TRequest, TListener> linkedListener = entry.getValue();
CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
TRequest request = linkedListener.getRequest();
if (callerIdentity.mUid != uid) {
if (callerIdentity.uid != uid) {
continue;
}
@@ -404,21 +385,19 @@ public class GnssManagerService {
TListener listener,
String packageName,
@Nullable String featureId,
@NonNull String listenerIdentifier,
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
ArrayMap<IBinder,
LinkedListener<TRequest, TListener>> gnssDataListeners,
Consumer<TListener> binderDeathCallback) {
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
if (!mAppOpsHelper.checkLocationAccess(identity)) {
return false;
}
CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
Binder.getCallingPid(), packageName, featureId, listenerIdentifier);
LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener,
listenerIdentifier, callerIdentity, binderDeathCallback);
identity, binderDeathCallback);
IBinder binder = listener.asBinder();
if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
return false;
@@ -437,11 +416,11 @@ public class GnssManagerService {
/* hasListener= */ true,
/* hasIntent= */ false,
/* geofence= */ null,
mAppForegroundHelper.getImportance(callerIdentity.mUid));
mAppForegroundHelper.getImportance(identity.uid));
}
if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid)
|| isThrottlingExempt(callerIdentity)) {
gnssDataProvider.addListener(request, listener, callerIdentity);
if (mAppForegroundHelper.isAppForeground(identity.uid)
|| isThrottlingExempt(identity)) {
gnssDataProvider.addListener(request, listener, identity);
}
return true;
}
@@ -471,7 +450,7 @@ public class GnssManagerService {
gnssDataProvider == mGnssMeasurementsProvider
? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
: LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
linkedListener.getCallerIdentity().mPackageName,
linkedListener.getCallerIdentity().packageName,
/* LocationRequest= */ null,
/* hasListener= */ true,
/* hasIntent= */ false,
@@ -493,7 +472,6 @@ public class GnssManagerService {
listener,
packageName,
featureId,
"Gnss status",
mGnssStatusProvider,
mGnssStatusListeners,
this::unregisterGnssStatusCallback);
@@ -514,8 +492,7 @@ public class GnssManagerService {
*/
public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
IGnssMeasurementsListener listener, String packageName,
@Nullable String featureId,
@NonNull String listenerIdentifier) {
@Nullable String featureId) {
if (request != null && request.isFullTracking()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
null);
@@ -526,7 +503,6 @@ public class GnssManagerService {
listener,
packageName,
featureId,
listenerIdentifier,
mGnssMeasurementsProvider,
mGnssMeasurementsListeners,
this::removeGnssMeasurementsListener);
@@ -541,10 +517,6 @@ public class GnssManagerService {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
if (!checkLocationAppOp(packageName)) {
return;
}
mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
measurementCorrections);
}
@@ -564,18 +536,16 @@ public class GnssManagerService {
*
* @param listener called when GNSS antenna info is received
* @param packageName name of requesting package
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssAntennaInfoListener(
IGnssAntennaInfoListener listener, String packageName,
@Nullable String featureId, @NonNull String listenerIdentifier) {
@Nullable String featureId) {
synchronized (mGnssAntennaInfoListeners) {
return addGnssDataListenerLocked(
/* request= */ null,
listener,
packageName,
featureId,
listenerIdentifier,
mGnssAntennaInfoProvider,
mGnssAntennaInfoListeners,
this::removeGnssAntennaInfoListener);
@@ -599,14 +569,13 @@ public class GnssManagerService {
*/
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener, String packageName,
@Nullable String featureId, @NonNull String listenerIdentifier) {
@Nullable String featureId) {
synchronized (mGnssNavigationMessageListeners) {
return addGnssDataListenerLocked(
/* request= */ null,
listener,
packageName,
featureId,
listenerIdentifier,
mGnssNavigationMessageProvider,
mGnssNavigationMessageListeners,
this::removeGnssNavigationMessageListener);
@@ -649,7 +618,7 @@ public class GnssManagerService {
return;
}
int userId = UserHandle.getUserId(gnssBatchingDeathCallback.getCallerIdentity().mUid);
int userId = gnssBatchingDeathCallback.getCallerIdentity().userId;
if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, userId)) {
Log.w(TAG, "reportLocationBatch() called without user permission");
return;
@@ -663,27 +632,19 @@ public class GnssManagerService {
}
private boolean isThrottlingExempt(CallerIdentity callerIdentity) {
if (callerIdentity.mUid == Process.SYSTEM_UID) {
if (callerIdentity.uid == Process.SYSTEM_UID) {
return true;
}
if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
callerIdentity.mPackageName)) {
callerIdentity.packageName)) {
return true;
}
synchronized (this) {
Preconditions.checkState(mLocationManagerInternal != null);
}
return mLocationManagerInternal.isProviderPackage(callerIdentity.mPackageName);
}
private boolean checkLocationAppOp(String packageName) {
synchronized (this) {
Preconditions.checkState(mAppOpsManager != null);
}
return mAppOpsManager.checkOp(OP_FINE_LOCATION, Binder.getCallingUid(), packageName)
== AppOpsManager.MODE_ALLOWED;
return mLocationManagerInternal.isProviderPackage(callerIdentity.packageName);
}
/**

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.location;
import static android.app.AppOpsManager.MODE_ALLOWED;
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.CallerIdentity.PERMISSION_COARSE;
import static com.android.server.location.CallerIdentity.PERMISSION_FINE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
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.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import android.app.AppOpsManager;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.List;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppOpsHelperTest {
private static final long TIMEOUT_MS = 5000;
@Mock private Context mContext;
@Mock private AppOpsManager mAppOps;
private List<AppOpsManager.OnOpChangedInternalListener> mListeners = new ArrayList<>();
private AppOpsHelper mHelper;
@Before
public void setUp() {
initMocks(this);
doReturn(mAppOps).when(mContext).getSystemService(AppOpsManager.class);
doAnswer(invocation -> mListeners.add(invocation.getArgument(3))).when(mAppOps)
.startWatchingMode(
eq(AppOpsManager.OP_COARSE_LOCATION),
isNull(),
eq(AppOpsManager.WATCH_FOREGROUND_CHANGES),
any(AppOpsManager.OnOpChangedInternalListener.class));
mHelper = new AppOpsHelper(mContext);
mHelper.onSystemReady();
}
private void sendAppOp(String packageName) {
for (AppOpsManager.OnOpChangedInternalListener listener : mListeners) {
listener.onOpChanged(AppOpsManager.OP_COARSE_LOCATION, packageName);
}
}
@Test
public void testListener() {
AppOpsHelper.LocationAppOpListener listener = mock(
AppOpsHelper.LocationAppOpListener.class);
mHelper.addListener(listener);
sendAppOp("mypackage1");
verify(listener, timeout(TIMEOUT_MS)).onAppOpsChanged("mypackage1");
sendAppOp("mypackage2");
verify(listener, timeout(TIMEOUT_MS)).onAppOpsChanged("mypackage2");
}
@Test
public void testCheckLocationAccess() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
assertThat(mHelper.checkLocationAccess(identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
assertThat(mHelper.checkLocationAccess(identity)).isFalse();
identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_COARSE);
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
assertThat(mHelper.checkLocationAccess(identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
assertThat(mHelper.checkLocationAccess(identity)).isFalse();
}
@Test
public void testNoteLocationAccess() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
assertThat(mHelper.noteLocationAccess(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)).isFalse();
identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_COARSE);
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
assertThat(mHelper.noteLocationAccess(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)).isFalse();
}
@Test
public void testStartLocationMonitoring() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
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();
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();
}
@Test
public void testStartHighPowerLocationMonitoring() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
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 = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
mHelper.stopLocationMonitoring(identity);
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
public void testStopHighPowerLocationMonitoring() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
mHelper.stopHighPowerLocationMonitoring(identity);
verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
public void testNoteMockLocationAccess() {
CallerIdentity identity = new CallerIdentity(1000, 1000, 1000, "mypackage", "myfeature",
PERMISSION_FINE);
doReturn(MODE_ALLOWED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
assertThat(mHelper.noteMockLocationAccess(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();
}
}

View File

@@ -61,6 +61,7 @@ import android.os.RemoteException;
import com.android.server.LocalServices;
import com.android.server.location.AppForegroundHelper;
import com.android.server.location.AppOpsHelper;
import com.android.server.location.GnssAntennaInfoProvider;
import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
import com.android.server.location.GnssBatchingProvider;
@@ -85,7 +86,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
@@ -111,7 +112,8 @@ public class GnssManagerServiceTest {
private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
// Managers and services
@Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
@@ -139,6 +141,9 @@ public class GnssManagerServiceTest {
Context.APP_OPS_SERVICE);
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
mAppOpsManager);
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[]{"com.android.server"});
enableLocationPermissions();
when(mAppForegroundHelper.isAppForeground(anyInt())).thenReturn(true);
@@ -187,8 +192,11 @@ public class GnssManagerServiceTest {
when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true);
when(mMockGnssBatchingProvider.stop()).thenReturn(true);
// Managers and services
AppOpsHelper appOpsHelper = new AppOpsHelper(mMockContext);
// Create GnssManagerService
mGnssManagerService = new GnssManagerService(mMockContext, mSettingsHelper,
mGnssManagerService = new GnssManagerService(mMockContext, appOpsHelper, mSettingsHelper,
mAppForegroundHelper, new LocationUsageLogger(),
mMockGnssLocationProvider);
mGnssManagerService.onSystemReady();
@@ -240,7 +248,7 @@ public class GnssManagerServiceTest {
new GnssSingleSatCorrection.Builder().build();
return
new GnssMeasurementCorrections.Builder().setSingleSatelliteCorrectionList(
Arrays.asList(gnssSingleSatCorrection)).build();
Collections.singletonList(gnssSingleSatCorrection)).build();
}
private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() {
@@ -270,7 +278,7 @@ public class GnssManagerServiceTest {
signalGainCorrectionsDbi,
signalGainCorrectionsUncertaintyDbi);
List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList();
List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList<>();
gnssAntennaInfos.add(new GnssAntennaInfo.Builder()
.setCarrierFrequencyMHz(carrierFrequencyMHz)
.setPhaseCenterOffset(phaseCenterOffset)
@@ -292,7 +300,7 @@ public class GnssManagerServiceTest {
PackageManager.PERMISSION_GRANTED);
// AppOpsManager will return true if OP_FINE_LOCATION is checked
when(mAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer(
when(mAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())).thenAnswer(
(InvocationOnMock invocation) -> {
int code = (int) (invocation.getArguments()[0]);
if (code == AppOpsManager.OP_FINE_LOCATION) {
@@ -311,7 +319,7 @@ public class GnssManagerServiceTest {
Mockito.doThrow(new SecurityException()).when(
mMockContext).checkPermission(anyString(), anyInt(), anyInt());
when(mAppOpsManager.checkOp(anyInt(), anyInt(),
when(mAppOpsManager.checkOpNoThrow(anyInt(), anyInt(),
anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
when(mLocationManagerInternal.isProviderEnabledForUser(eq(GPS_PROVIDER), anyInt()))
@@ -398,8 +406,7 @@ public class GnssManagerServiceTest {
when(mMockGnssCapabilitiesProvider.getGnssCapabilities()).thenReturn(mGnssCapabilities);
enableLocationPermissions();
assertThat(mGnssManagerService.getGnssCapabilities("com.android.server")).isEqualTo(
mGnssCapabilities);
assertThat(mGnssManagerService.getGnssCapabilities()).isEqualTo(mGnssCapabilities);
}
@Test
@@ -429,7 +436,7 @@ public class GnssManagerServiceTest {
assertThrows(SecurityException.class,
() -> mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull,
"com.android.server"));
"com.android.server", null));
verify(mMockGnssBatchingProvider, times(0)).start(periodNanos, wakeOnFifoFull);
}
@@ -441,7 +448,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
assertThat(mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull,
"com.android.server"))
"com.android.server", null))
.isEqualTo(
true);
verify(mMockGnssBatchingProvider, times(1)).start(100L, true);
@@ -455,8 +462,7 @@ public class GnssManagerServiceTest {
disableLocationPermissions();
assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssBatchingCallback(
mockBatchedLocationCallback, "com.android.server", "abcd123",
"TestBatchedLocationCallback"));
mockBatchedLocationCallback, "com.android.server", null));
mGnssManagerService.onReportLocation(mockLocationList);
@@ -471,8 +477,8 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
assertThat(mGnssManagerService.addGnssBatchingCallback(
mockBatchedLocationCallback, "com.android.server",
"abcd123", "TestBatchedLocationCallback")).isEqualTo(true);
mockBatchedLocationCallback, "com.android.server", null))
.isEqualTo(true);
mGnssManagerService.onReportLocation(mockLocationList);
@@ -488,11 +494,11 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
assertThat(mGnssManagerService.addGnssBatchingCallback(
mockBatchedLocationCallback1, "com.android.server",
"abcd123", "TestBatchedLocationCallback")).isEqualTo(true);
mockBatchedLocationCallback1, "com.android.server", null))
.isEqualTo(true);
assertThat(mGnssManagerService.addGnssBatchingCallback(
mockBatchedLocationCallback2, "com.android.server",
"abcd123", "TestBatchedLocationCallback")).isEqualTo(true);
mockBatchedLocationCallback2, "com.android.server", null))
.isEqualTo(true);
mGnssManagerService.onReportLocation(mockLocationList);
@@ -525,7 +531,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback,
"com.android.server", "abcd123", "TestBatchedLocationCallback");
"com.android.server", null);
disableLocationPermissions();
@@ -546,7 +552,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback,
"com.android.server", "abcd123", "TestBatchedLocationCallback");
"com.android.server", null);
mGnssManagerService.removeGnssBatchingCallback();
@@ -631,7 +637,7 @@ public class GnssManagerServiceTest {
assertThrows(SecurityException.class,
() -> mGnssManagerService.addGnssMeasurementsListener(
new GnssRequest.Builder().build(), mockGnssMeasurementsListener,
"com.android.server", "abcd123", "TestGnssMeasurementsListener"));
"com.android.server", null));
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived(
@@ -650,8 +656,7 @@ public class GnssManagerServiceTest {
assertThat(mGnssManagerService.addGnssMeasurementsListener(
new GnssRequest.Builder().build(),
mockGnssMeasurementsListener,
"com.android.server", "abcd123",
"TestGnssMeasurementsListener")).isEqualTo(true);
"com.android.server", null)).isEqualTo(true);
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived(
@@ -698,8 +703,7 @@ public class GnssManagerServiceTest {
mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
mockGnssMeasurementsListener,
"com.android.server", "abcd123",
"TestGnssMeasurementsListener");
"com.android.server", null);
disableLocationPermissions();
@@ -722,8 +726,7 @@ public class GnssManagerServiceTest {
mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
mockGnssMeasurementsListener,
"com.android.server", "abcd123",
"TestGnssMeasurementsListener");
"com.android.server", null);
disableLocationPermissions();
@@ -746,7 +749,7 @@ public class GnssManagerServiceTest {
assertThrows(SecurityException.class,
() -> mGnssManagerService.addGnssAntennaInfoListener(
mockGnssAntennaInfoListener,
"com.android.server", "abcd123", "TestGnssAntennaInfoListener"));
"com.android.server", null));
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
verify(mockGnssAntennaInfoListener, times(0))
@@ -762,7 +765,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
"com.android.server", "abcd123", "TestGnssAntennaInfoListener")).isEqualTo(true);
"com.android.server", null)).isEqualTo(true);
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
verify(mockGnssAntennaInfoListener, times(1))
@@ -779,7 +782,7 @@ public class GnssManagerServiceTest {
mGnssManagerService.addGnssAntennaInfoListener(
mockGnssAntennaInfoListener,
"com.android.server", "abcd123", "TestGnssAntennaInfoListener");
"com.android.server", null);
disableLocationPermissions();
@@ -801,7 +804,7 @@ public class GnssManagerServiceTest {
mGnssManagerService.addGnssAntennaInfoListener(
mockGnssAntennaInfoListener,
"com.android.server", "abcd123", "TestGnssAntennaInfoListener");
"com.android.server", null);
mGnssManagerService.removeGnssAntennaInfoListener(
mockGnssAntennaInfoListener);
@@ -821,8 +824,7 @@ public class GnssManagerServiceTest {
assertThrows(SecurityException.class,
() -> mGnssManagerService.addGnssNavigationMessageListener(
mockGnssNavigationMessageListener, "com.android.server",
"abcd123", "TestGnssNavigationMessageListener"));
mockGnssNavigationMessageListener, "com.android.server", null));
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
@@ -839,8 +841,8 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
assertThat(mGnssManagerService.addGnssNavigationMessageListener(
mockGnssNavigationMessageListener, "com.android.server",
"abcd123", "TestGnssNavigationMessageListener")).isEqualTo(true);
mockGnssNavigationMessageListener, "com.android.server", null))
.isEqualTo(true);
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
@@ -857,8 +859,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
mGnssManagerService.addGnssNavigationMessageListener(
mockGnssNavigationMessageListener, "com.android.server",
"abcd123", "TestGnssNavigationMessageListener");
mockGnssNavigationMessageListener, "com.android.server", null);
disableLocationPermissions();
@@ -880,8 +881,7 @@ public class GnssManagerServiceTest {
enableLocationPermissions();
mGnssManagerService.addGnssNavigationMessageListener(
mockGnssNavigationMessageListener, "com.android.server",
"abcd123", "TestGnssNavigationMessageListener");
mockGnssNavigationMessageListener, "com.android.server", null);
mGnssManagerService.removeGnssNavigationMessageListener(
mockGnssNavigationMessageListener);