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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
253
services/core/java/com/android/server/location/AppOpsHelper.java
Normal file
253
services/core/java/com/android/server/location/AppOpsHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user