blob: 1eb2c525ff226762ef410f56b1e2dc9ff8062a1d [file] [log] [blame]
/*
* 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.gnss;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
import android.location.IGpsGeofenceHardware;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
import android.os.RemoteException;
import android.stats.location.LocationStatsEnums;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
import com.android.server.LocationManagerService;
import com.android.server.LocationManagerServiceUtils;
import com.android.server.LocationManagerServiceUtils.LinkedListener;
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GnssBatchingProvider;
import com.android.server.location.GnssCapabilitiesProvider;
import com.android.server.location.GnssLocationProvider;
import com.android.server.location.GnssMeasurementCorrectionsProvider;
import com.android.server.location.GnssMeasurementsProvider;
import com.android.server.location.GnssNavigationMessageProvider;
import com.android.server.location.GnssStatusListenerHelper;
import com.android.server.location.LocationUsageLogger;
import com.android.server.location.RemoteListenerHelper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
public class GnssManagerService {
private static final String TAG = "GnssManagerService";
private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
// Providers
private final GnssLocationProvider mGnssLocationProvider;
private final GnssStatusListenerHelper mGnssStatusProvider;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
private final GnssBatchingProvider mGnssBatchingProvider;
private final INetInitiatedListener mNetInitiatedListener;
private final IGpsGeofenceHardware mGpsGeofenceProxy;
private final LocationManagerService mLocationManagerService;
private final LocationUsageLogger mLocationUsageLogger;
@GuardedBy("mGnssMeasurementsListeners")
private final ArrayMap<IBinder,
LinkedListener<IGnssMeasurementsListener>>
mGnssMeasurementsListeners = new ArrayMap<>();
@GuardedBy("mGnssNavigationMessageListeners")
private final ArrayMap<
IBinder, LinkedListener<IGnssNavigationMessageListener>>
mGnssNavigationMessageListeners = new ArrayMap<>();
@GuardedBy("mGnssStatusListeners")
private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
mGnssStatusListeners = new ArrayMap<>();
@GuardedBy("mGnssBatchingLock")
private IBatchedLocationCallback mGnssBatchingCallback;
@GuardedBy("mGnssBatchingLock")
private LinkedListener<IBatchedLocationCallback>
mGnssBatchingDeathCallback;
@GuardedBy("mGnssBatchingLock")
private boolean mGnssBatchingInProgress = false;
private final Object mGnssBatchingLock = new Object();
private final Context mContext;
private final Handler mHandler;
public GnssManagerService(LocationManagerService locationManagerService,
Context context, LocationUsageLogger locationUsageLogger) {
this(locationManagerService, context,
new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger);
}
// Can use this constructor to inject GnssLocationProvider for testing
@VisibleForTesting
public GnssManagerService(LocationManagerService locationManagerService,
Context context,
GnssLocationProvider gnssLocationProvider,
LocationUsageLogger locationUsageLogger) {
mContext = context;
mHandler = FgThread.getHandler();
mGnssLocationProvider =
gnssLocationProvider;
mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
mGnssMeasurementCorrectionsProvider =
mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
mGnssSystemInfoProvider = mGnssLocationProvider.getGnssSystemInfoProvider();
mGnssMetricsProvider = mGnssLocationProvider.getGnssMetricsProvider();
mGnssCapabilitiesProvider = mGnssLocationProvider.getGnssCapabilitiesProvider();
mGnssBatchingProvider = mGnssLocationProvider.getGnssBatchingProvider();
mNetInitiatedListener = mGnssLocationProvider.getNetInitiatedListener();
mGpsGeofenceProxy = mGnssLocationProvider.getGpsGeofenceProxy();
mLocationManagerService = locationManagerService;
mLocationUsageLogger = locationUsageLogger;
registerUidListener();
}
public static boolean isGnssSupported() {
return GnssLocationProvider.isSupported();
}
private boolean hasGnssPermissions(String packageName) {
mContext.enforceCallingPermission(
Manifest.permission.ACCESS_FINE_LOCATION,
"Fine location permission not granted.");
int uid = Binder.getCallingUid();
long identity = Binder.clearCallingIdentity();
try {
return mContext.getSystemService(
AppOpsManager.class).checkOp(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
== AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
public GnssLocationProvider getGnssLocationProvider() {
return mGnssLocationProvider;
}
public IGpsGeofenceHardware getGpsGeofenceProxy() {
return mGpsGeofenceProxy;
}
/**
* Get year of GNSS hardware.
*
* @return year of GNSS hardware as an int if possible, otherwise zero
*/
public int getGnssYearOfHardware() {
if (mGnssSystemInfoProvider != null) {
return mGnssSystemInfoProvider.getGnssYearOfHardware();
} else {
return 0;
}
}
/**
* Get model name of GNSS hardware.
*
* @return GNSS hardware model name as a string if possible, otherwise null
*/
public String getGnssHardwareModelName() {
if (mGnssSystemInfoProvider != null) {
return mGnssSystemInfoProvider.getGnssHardwareModelName();
} else {
return null;
}
}
/**
* Get GNSS hardware capabilities. The capabilities are described in {@link
* android.location.GnssCapabilities} and their integer values correspond to the
* bit positions in the returned {@code long} value.
*
* @param packageName name of requesting package
* @return capabilities supported by the GNSS chipset
*/
public long getGnssCapabilities(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to obtain GNSS chipset capabilities.");
if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) {
return GnssCapabilities.INVALID_CAPABILITIES;
}
return mGnssCapabilitiesProvider.getGnssCapabilities();
}
/**
* Get size of GNSS batch (GNSS location results are batched together for power savings).
* Requires LOCATION_HARDWARE and GNSS permissions.
*
* @param packageName name of requesting package
* @return size of the GNSS batch collection
*/
public int getGnssBatchSize(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "getGnssBatchSize called without GNSS permissions");
return 0;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not get GNSS batch size. GNSS batching provider "
+ "not available.");
return 0;
}
synchronized (mGnssBatchingLock) {
return mGnssBatchingProvider.getBatchSize();
}
}
/**
* Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered
* as a collection.
*
* @param periodNanos duration over which to collect GPS positions before delivering as a
* batch
* @param wakeOnFifoFull specifying whether to wake on full queue
* @param packageName name of requesting package
* @return true of batch started successfully, false otherwise
*/
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "startGnssBatch called without GNSS permissions");
return false;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not start GNSS batching. GNSS batching provider "
+ "not available.");
return false;
}
synchronized (mGnssBatchingLock) {
if (mGnssBatchingInProgress) {
// Current design does not expect multiple starts to be called repeatedly
Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
// Try to clean up anyway, and continue
stopGnssBatch();
}
mGnssBatchingInProgress = true;
return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
}
}
/**
* Adds a GNSS batching callback for delivering GNSS location batch results.
*
* @param callback called when batching operation is complete to deliver GPS positions
* @param packageName name of requesting package
* @return true if callback is successfully added, false otherwise
*/
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
@Nullable String featureId, @NonNull String listenerIdentity) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "addGnssBatchingCallback called without GNSS permissions");
return false;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not add GNSS batching callback. GNSS batching provider "
+ "not available.");
return false;
}
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
featureId, listenerIdentity);
synchronized (mGnssBatchingLock) {
mGnssBatchingCallback = callback;
mGnssBatchingDeathCallback =
new LinkedListener<>(
callback,
"BatchedLocationCallback",
callerIdentity,
(IBatchedLocationCallback listener) -> {
stopGnssBatch();
removeGnssBatchingCallback();
});
if (!mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
callback.asBinder())) {
return false;
}
return true;
}
}
/**
* Force flush GNSS location results from batch.
*
* @param packageName name of requesting package
*/
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "flushGnssBatch called without GNSS permissions");
return;
}
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not flush GNSS batch. GNSS batching provider "
+ "not available.");
return;
}
synchronized (mGnssBatchingLock) {
if (!mGnssBatchingInProgress) {
Log.w(TAG, "flushGnssBatch called with no batch in progress");
}
mGnssBatchingProvider.flush();
}
}
/**
* Removes GNSS batching callback.
*/
public void removeGnssBatchingCallback() {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not add GNSS batching callback. GNSS batching provider "
+ "not available.");
return;
}
synchronized (mGnssBatchingLock) {
mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked(
mGnssBatchingCallback.asBinder());
mGnssBatchingCallback = null;
mGnssBatchingDeathCallback = null;
}
}
/**
* Stop GNSS batch collection.
*
* @return true if GNSS batch successfully stopped, false otherwise
*/
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
if (mGnssBatchingProvider == null) {
Log.e(
TAG,
"Can not stop GNSS batch. GNSS batching provider "
+ "not available.");
return false;
}
synchronized (mGnssBatchingLock) {
mGnssBatchingInProgress = false;
return mGnssBatchingProvider.stop();
}
}
private void registerUidListener() {
mContext.getSystemService(
ActivityManager.class).addOnUidImportanceListener(
(uid, importance) -> {
// listener invoked on ui thread, move to our thread to reduce risk
// of blocking ui thread
mHandler.post(
() -> {
onForegroundChanged(uid,
LocationManagerServiceUtils.isImportanceForeground(
importance));
});
},
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
}
private void onForegroundChanged(int uid, boolean foreground) {
synchronized (mGnssMeasurementsListeners) {
updateListenersOnForegroundChangedLocked(
mGnssMeasurementsListeners,
mGnssMeasurementsProvider,
IGnssMeasurementsListener.Stub::asInterface,
uid,
foreground);
}
synchronized (mGnssNavigationMessageListeners) {
updateListenersOnForegroundChangedLocked(
mGnssNavigationMessageListeners,
mGnssNavigationMessageProvider,
IGnssNavigationMessageListener.Stub::asInterface,
uid,
foreground);
}
synchronized (mGnssStatusListeners) {
updateListenersOnForegroundChangedLocked(
mGnssStatusListeners,
mGnssStatusProvider,
IGnssStatusListener.Stub::asInterface,
uid,
foreground);
}
}
private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
ArrayMap<IBinder, ? extends LinkedListenerBase>
gnssDataListeners,
RemoteListenerHelper<TListener> gnssDataProvider,
Function<IBinder, TListener> mapBinderToListener,
int uid,
boolean foreground) {
for (Map.Entry<IBinder, ? extends LinkedListenerBase> entry :
gnssDataListeners.entrySet()) {
LinkedListenerBase linkedListener = entry.getValue();
CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
if (callerIdentity.mUid != uid) {
continue;
}
if (D) {
Log.d(
TAG,
linkedListener.getListenerName()
+ " from uid "
+ uid
+ " is now "
+ LocationManagerServiceUtils.foregroundAsString(foreground));
}
TListener listener = mapBinderToListener.apply(entry.getKey());
if (foreground || mLocationManagerService.isThrottlingExemptLocked(callerIdentity)) {
gnssDataProvider.addListener(listener, callerIdentity);
} else {
gnssDataProvider.removeListener(listener);
}
}
}
private <TListener extends IInterface> boolean addGnssDataListenerLocked(
TListener listener,
String packageName,
@Nullable String featureId,
@NonNull String listenerIdentifier,
RemoteListenerHelper<TListener> gnssDataProvider,
ArrayMap<IBinder,
LinkedListener<TListener>> gnssDataListeners,
Consumer<TListener> binderDeathCallback) {
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "addGnssDataListenerLocked called without GNSS permissions");
return false;
}
if (gnssDataProvider == null) {
Log.e(
TAG,
"Can not add GNSS data listener. GNSS data provider "
+ "not available.");
return false;
}
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
featureId, listenerIdentifier);
LinkedListener<TListener> linkedListener =
new LocationManagerServiceUtils.LinkedListener<>(
listener, listenerIdentifier, callerIdentity, binderDeathCallback);
IBinder binder = listener.asBinder();
if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
return false;
}
gnssDataListeners.put(binder, linkedListener);
long identity = Binder.clearCallingIdentity();
try {
if (gnssDataProvider == mGnssMeasurementsProvider
|| gnssDataProvider == mGnssStatusProvider) {
mLocationUsageLogger.logLocationApiUsage(
LocationStatsEnums.USAGE_STARTED,
gnssDataProvider == mGnssMeasurementsProvider
? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
: LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
packageName,
/* LocationRequest= */ null,
/* hasListener= */ true,
/* hasIntent= */ false,
/* geofence= */ null,
LocationManagerServiceUtils.getPackageImportance(packageName,
mContext));
}
if (mLocationManagerService.isThrottlingExemptLocked(callerIdentity)
|| LocationManagerServiceUtils.isImportanceForeground(
LocationManagerServiceUtils.getPackageImportance(packageName, mContext))) {
gnssDataProvider.addListener(listener, callerIdentity);
}
return true;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private <TListener extends IInterface> void removeGnssDataListener(
TListener listener,
RemoteListenerHelper<TListener> gnssDataProvider,
ArrayMap<IBinder,
LinkedListener<TListener>> gnssDataListeners) {
if (gnssDataProvider == null) {
Log.e(
TAG,
"Can not remove GNSS data listener. GNSS data provider "
+ "not available.");
return;
}
IBinder binder = listener.asBinder();
LinkedListener<TListener> linkedListener =
gnssDataListeners.remove(binder);
if (linkedListener == null) {
return;
}
long identity = Binder.clearCallingIdentity();
try {
if (gnssDataProvider == mGnssMeasurementsProvider
|| gnssDataProvider == mGnssStatusProvider) {
mLocationUsageLogger.logLocationApiUsage(
LocationStatsEnums.USAGE_ENDED,
gnssDataProvider == mGnssMeasurementsProvider
? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
: LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
linkedListener.getCallerIdentity().mPackageName,
/* LocationRequest= */ null,
/* hasListener= */ true,
/* hasIntent= */ false,
/* geofence= */ null,
LocationManagerServiceUtils.getPackageImportance(
linkedListener.getCallerIdentity().mPackageName, mContext));
}
} finally {
Binder.restoreCallingIdentity(identity);
}
linkedListener.unlinkFromListenerDeathNotificationLocked(binder);
gnssDataProvider.removeListener(listener);
}
/**
* Registers listener for GNSS status changes.
*
* @param listener called when GNSS status changes
* @param packageName name of requesting package
* @return true if listener is successfully registered, false otherwise
*/
public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
@Nullable String featureId) {
synchronized (mGnssStatusListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
featureId,
"Gnss status",
mGnssStatusProvider,
mGnssStatusListeners,
this::unregisterGnssStatusCallback);
}
}
/**
* Unregisters listener for GNSS status changes.
*
* @param listener called when GNSS status changes
*/
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
synchronized (mGnssStatusListeners) {
removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
}
}
/**
* Adds a GNSS measurements listener.
*
* @param listener called when GNSS measurements are received
* @param packageName name of requesting package
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssMeasurementsListener(
IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
synchronized (mGnssMeasurementsListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
featureId,
listenerIdentifier,
mGnssMeasurementsProvider,
mGnssMeasurementsListeners,
this::removeGnssMeasurementsListener);
}
}
/**
* Injects GNSS measurement corrections.
*
* @param measurementCorrections GNSS measurement corrections
* @param packageName name of requesting package
*/
public void injectGnssMeasurementCorrections(
GnssMeasurementCorrections measurementCorrections, String packageName) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject GNSS measurement corrections.");
if (!hasGnssPermissions(packageName)) {
Log.e(TAG, "Can not inject GNSS corrections due to no permission.");
return;
}
if (mGnssMeasurementCorrectionsProvider == null) {
Log.e(
TAG,
"Can not inject GNSS corrections. GNSS measurement corrections provider "
+ "not available.");
return;
}
mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
measurementCorrections);
}
/**
* Removes a GNSS measurements listener.
*
* @param listener called when GNSS measurements are received
*/
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
synchronized (mGnssMeasurementsListeners) {
removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
}
}
/**
* Adds a GNSS navigation message listener.
*
* @param listener called when navigation message is received
* @param packageName name of requesting package
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener, String packageName,
@Nullable String featureId, @NonNull String listenerIdentifier) {
synchronized (mGnssNavigationMessageListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
featureId,
listenerIdentifier,
mGnssNavigationMessageProvider,
mGnssNavigationMessageListeners,
this::removeGnssNavigationMessageListener);
}
}
/**
* Removes a GNSS navigation message listener.
*
* @param listener called when navigation message is received
*/
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
synchronized (mGnssNavigationMessageListeners) {
removeGnssDataListener(
listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners);
}
}
/**
* Send Ni Response, indicating a location request initiated by a network carrier.
*/
public boolean sendNiResponse(int notifId, int userResponse) {
if (Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException(
"calling sendNiResponse from outside of the system is not allowed");
}
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
return false;
}
}
/**
* Report location results to GNSS batching listener.
*
* @param locations batch of locations to report to GNSS batching callback
*/
public void onReportLocation(List<Location> locations) {
if (mGnssBatchingCallback == null) {
Log.e(TAG, "reportLocationBatch() called without active Callback");
return;
}
try {
mGnssBatchingCallback.onLocationBatch(locations);
} catch (RemoteException e) {
Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
}
}
/**
* Dump for debugging.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
if (args.length > 0 && args[0].equals("--gnssmetrics")) {
if (mGnssMetricsProvider != null) {
pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
}
return;
}
ipw.println("GnssMeasurement Listeners:");
ipw.increaseIndent();
synchronized (mGnssMeasurementsListeners) {
for (LinkedListenerBase listener :
mGnssMeasurementsListeners
.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
ipw.println("GnssNavigationMessage Listeners:");
ipw.increaseIndent();
synchronized (mGnssNavigationMessageListeners) {
for (LinkedListenerBase listener :
mGnssNavigationMessageListeners.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
ipw.println("GnssStatus Listeners:");
ipw.increaseIndent();
synchronized (mGnssStatusListeners) {
for (LinkedListenerBase listener :
mGnssStatusListeners.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
synchronized (mGnssBatchingLock) {
if (mGnssBatchingInProgress) {
ipw.println("GNSS batching in progress");
}
}
}
}