Merge "Refactor GNSS listeners"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8058d91923
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.location;
|
||||
|
||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A base class to manage listeners that have a 1:N -> source:listener relationship.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
abstract class AbstractListenerManager<TRequest, TListener> {
|
||||
|
||||
private static class Registration<TRequest, TListener> {
|
||||
private final Executor mExecutor;
|
||||
@Nullable private TRequest mRequest;
|
||||
@Nullable private volatile TListener mListener;
|
||||
|
||||
private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
|
||||
Preconditions.checkArgument(listener != null, "invalid null listener/callback");
|
||||
Preconditions.checkArgument(executor != null, "invalid null executor");
|
||||
mExecutor = executor;
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
private void unregister() {
|
||||
mRequest = null;
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
private void execute(Consumer<TListener> operation) {
|
||||
mExecutor.execute(
|
||||
obtainRunnable(Registration<TRequest, TListener>::accept, this, operation)
|
||||
.recycleOnUse());
|
||||
}
|
||||
|
||||
private void accept(Consumer<TListener> operation) {
|
||||
TListener listener = mListener;
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we may be under the binder identity if a direct executor is used
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
operation.accept(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
|
||||
new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
@Nullable
|
||||
private TRequest mMergedRequest;
|
||||
|
||||
public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
|
||||
throws RemoteException {
|
||||
return addInternal(/* request= */ null, listener, handler);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(/* request= */ null, listener, executor);
|
||||
}
|
||||
|
||||
public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
@NonNull Handler handler) throws RemoteException {
|
||||
return addInternal(request, listener, handler);
|
||||
}
|
||||
|
||||
public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
@NonNull Executor executor) throws RemoteException {
|
||||
return addInternal(request, listener, executor);
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
|
||||
@NonNull Handler handler) throws RemoteException {
|
||||
return addInternal(request, listener, new HandlerExecutor(handler));
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
|
||||
@NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
Preconditions.checkArgument(listener != null, "invalid null listener/callback");
|
||||
return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
|
||||
}
|
||||
|
||||
private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
|
||||
throws RemoteException {
|
||||
Preconditions.checkNotNull(registration);
|
||||
|
||||
synchronized (mListeners) {
|
||||
boolean initialRequest = mListeners.isEmpty();
|
||||
|
||||
Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
|
||||
if (oldRegistration != null) {
|
||||
oldRegistration.unregister();
|
||||
}
|
||||
TRequest merged = mergeRequests();
|
||||
|
||||
if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
|
||||
mMergedRequest = merged;
|
||||
if (!initialRequest) {
|
||||
unregisterService();
|
||||
}
|
||||
registerService(mMergedRequest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(Object listener) throws RemoteException {
|
||||
synchronized (mListeners) {
|
||||
Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
|
||||
if (oldRegistration == null) {
|
||||
return;
|
||||
}
|
||||
oldRegistration.unregister();
|
||||
|
||||
boolean lastRequest = mListeners.isEmpty();
|
||||
TRequest merged = lastRequest ? null : mergeRequests();
|
||||
boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);
|
||||
|
||||
if (lastRequest || newRequest) {
|
||||
unregisterService();
|
||||
mMergedRequest = merged;
|
||||
if (newRequest) {
|
||||
registerService(mMergedRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected TListener convertKey(@NonNull Object listener) {
|
||||
return (TListener) listener;
|
||||
}
|
||||
|
||||
protected abstract boolean registerService(TRequest request) throws RemoteException;
|
||||
protected abstract void unregisterService() throws RemoteException;
|
||||
|
||||
@Nullable
|
||||
protected TRequest merge(@NonNull TRequest[] requests) {
|
||||
for (TRequest request : requests) {
|
||||
Preconditions.checkArgument(request == null,
|
||||
"merge() has to be overridden for non-null requests.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void execute(Consumer<TListener> operation) {
|
||||
synchronized (mListeners) {
|
||||
for (Registration<TRequest, TListener> registration : mListeners.values()) {
|
||||
registration.execute(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private TRequest mergeRequests() {
|
||||
Preconditions.checkState(Thread.holdsLock(mListeners));
|
||||
|
||||
if (mListeners.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mListeners.size() == 1) {
|
||||
return mListeners.valueAt(0).getRequest();
|
||||
}
|
||||
|
||||
TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
|
||||
for (int index = 0; index < mListeners.size(); index++) {
|
||||
requests[index] = mListeners.valueAt(index).getRequest();
|
||||
}
|
||||
return merge(requests);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,9 @@ public final class GnssRequest implements Parcelable {
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("GnssRequest[");
|
||||
s.append("FullTracking=").append(mFullTracking);
|
||||
if (mFullTracking) {
|
||||
s.append("FullTracking");
|
||||
}
|
||||
s.append(']');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
@@ -57,10 +57,6 @@ interface ILocationManager
|
||||
in PendingIntent intent, String packageName, String featureId);
|
||||
void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
|
||||
|
||||
boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName,
|
||||
String featureId);
|
||||
void unregisterGnssStatusCallback(IGnssStatusListener callback);
|
||||
|
||||
boolean geocoderIsPresent();
|
||||
String getFromLocation(double latitude, double longitude, int maxResults,
|
||||
in GeocoderParams params, out List<Address> addrs);
|
||||
@@ -69,31 +65,30 @@ interface ILocationManager
|
||||
double upperRightLatitude, double upperRightLongitude, int maxResults,
|
||||
in GeocoderParams params, out List<Address> addrs);
|
||||
|
||||
boolean addGnssMeasurementsListener(in GnssRequest request,
|
||||
in IGnssMeasurementsListener listener,
|
||||
String packageName, String featureId);
|
||||
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
|
||||
in String packageName);
|
||||
long getGnssCapabilities();
|
||||
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
|
||||
|
||||
boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener,
|
||||
String packageName, String featureId);
|
||||
void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
|
||||
|
||||
boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
|
||||
String packageName, String featureId);
|
||||
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
|
||||
|
||||
int getGnssYearOfHardware();
|
||||
String getGnssHardwareModelName();
|
||||
|
||||
void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, String featureId);
|
||||
void unregisterGnssStatusCallback(in IGnssStatusListener callback);
|
||||
|
||||
void addGnssMeasurementsListener(in GnssRequest request, in IGnssMeasurementsListener listener, String packageName, String featureId);
|
||||
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
|
||||
|
||||
void addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, String packageName, String featureId);
|
||||
void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
|
||||
|
||||
void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, String featureId);
|
||||
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
|
||||
|
||||
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, String packageName);
|
||||
|
||||
int getGnssBatchSize(String packageName);
|
||||
boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId);
|
||||
void addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId);
|
||||
void removeGnssBatchingCallback();
|
||||
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId);
|
||||
void startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId);
|
||||
void flushGnssBatch(String packageName);
|
||||
boolean stopGnssBatch();
|
||||
void stopGnssBatch();
|
||||
void injectLocation(in Location location);
|
||||
|
||||
List<String> getAllProviders();
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.Manifest.permission.LOCATION_HARDWARE;
|
||||
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
|
||||
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
|
||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
|
||||
|
||||
import android.Manifest;
|
||||
@@ -42,6 +43,7 @@ import android.compat.annotation.EnabledAfter;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.util.listeners.ClientListenerManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -1921,11 +1923,8 @@ public class LocationManager {
|
||||
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
|
||||
}
|
||||
|
||||
try {
|
||||
return mGnssStatusListenerManager.addListener(listener, Runnable::run);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.addListener(listener, DIRECT_EXECUTOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1943,11 +1942,7 @@ public class LocationManager {
|
||||
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
|
||||
}
|
||||
|
||||
try {
|
||||
mGnssStatusListenerManager.removeListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1964,7 +1959,8 @@ public class LocationManager {
|
||||
@Deprecated
|
||||
@RequiresPermission(ACCESS_FINE_LOCATION)
|
||||
public boolean registerGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
|
||||
return registerGnssStatusCallback(Runnable::run, callback);
|
||||
mGnssStatusListenerManager.addListener(callback, DIRECT_EXECUTOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1984,11 +1980,8 @@ public class LocationManager {
|
||||
handler = new Handler();
|
||||
}
|
||||
|
||||
try {
|
||||
return mGnssStatusListenerManager.addListener(callback, handler);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.addListener(callback, new HandlerExecutor(handler));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2006,11 +1999,8 @@ public class LocationManager {
|
||||
public boolean registerGnssStatusCallback(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssStatus.Callback callback) {
|
||||
try {
|
||||
return mGnssStatusListenerManager.addListener(callback, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.addListener(callback, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2019,11 +2009,7 @@ public class LocationManager {
|
||||
* @param callback GNSS status callback object to remove
|
||||
*/
|
||||
public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
|
||||
try {
|
||||
mGnssStatusListenerManager.removeListener(callback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.removeListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2057,7 +2043,7 @@ public class LocationManager {
|
||||
@Deprecated
|
||||
@RequiresPermission(ACCESS_FINE_LOCATION)
|
||||
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
|
||||
return addNmeaListener(Runnable::run, listener);
|
||||
return addNmeaListener(DIRECT_EXECUTOR, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2076,11 +2062,9 @@ public class LocationManager {
|
||||
if (handler == null) {
|
||||
handler = new Handler();
|
||||
}
|
||||
try {
|
||||
return mGnssStatusListenerManager.addListener(listener, handler);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
mGnssStatusListenerManager.addListener(listener, new HandlerExecutor(handler));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2098,11 +2082,8 @@ public class LocationManager {
|
||||
public boolean addNmeaListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnNmeaMessageListener listener) {
|
||||
try {
|
||||
return mGnssStatusListenerManager.addListener(listener, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.addListener(listener, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2111,11 +2092,7 @@ public class LocationManager {
|
||||
* @param listener a {@link OnNmeaMessageListener} object to remove
|
||||
*/
|
||||
public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
|
||||
try {
|
||||
mGnssStatusListenerManager.removeListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssStatusListenerManager.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2155,7 +2132,7 @@ public class LocationManager {
|
||||
@RequiresPermission(ACCESS_FINE_LOCATION)
|
||||
public boolean registerGnssMeasurementsCallback(
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
return registerGnssMeasurementsCallback(Runnable::run, callback);
|
||||
return registerGnssMeasurementsCallback(DIRECT_EXECUTOR, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2174,11 +2151,9 @@ public class LocationManager {
|
||||
if (handler == null) {
|
||||
handler = new Handler();
|
||||
}
|
||||
try {
|
||||
return mGnssMeasurementsListenerManager.addListener(callback, handler);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
return registerGnssMeasurementsCallback(new GnssRequest.Builder().build(),
|
||||
new HandlerExecutor(handler), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2196,11 +2171,8 @@ public class LocationManager {
|
||||
public boolean registerGnssMeasurementsCallback(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
try {
|
||||
return mGnssMeasurementsListenerManager.addListener(callback, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return registerGnssMeasurementsCallback(new GnssRequest.Builder().build(), executor,
|
||||
callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2225,11 +2197,8 @@ public class LocationManager {
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
Preconditions.checkArgument(request != null, "invalid null request");
|
||||
try {
|
||||
return mGnssMeasurementsListenerManager.addListener(request, callback, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssMeasurementsListenerManager.addListener(request, callback, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2262,11 +2231,7 @@ public class LocationManager {
|
||||
*/
|
||||
public void unregisterGnssMeasurementsCallback(
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
try {
|
||||
mGnssMeasurementsListenerManager.removeListener(callback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssMeasurementsListenerManager.removeListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2285,11 +2250,8 @@ public class LocationManager {
|
||||
public boolean registerAntennaInfoListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssAntennaInfo.Listener listener) {
|
||||
try {
|
||||
return mGnssAntennaInfoListenerManager.addListener(listener, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssAntennaInfoListenerManager.addListener(listener, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2298,11 +2260,7 @@ public class LocationManager {
|
||||
* @param listener a {@link GnssAntennaInfo.Listener} object to remove.
|
||||
*/
|
||||
public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
|
||||
try {
|
||||
mGnssAntennaInfoListenerManager.removeListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssAntennaInfoListenerManager.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2341,7 +2299,7 @@ public class LocationManager {
|
||||
@Deprecated
|
||||
public boolean registerGnssNavigationMessageCallback(
|
||||
@NonNull GnssNavigationMessage.Callback callback) {
|
||||
return registerGnssNavigationMessageCallback(Runnable::run, callback);
|
||||
return registerGnssNavigationMessageCallback(DIRECT_EXECUTOR, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2361,11 +2319,8 @@ public class LocationManager {
|
||||
handler = new Handler();
|
||||
}
|
||||
|
||||
try {
|
||||
return mGnssNavigationMessageListenerTransport.addListener(callback, handler);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssNavigationMessageListenerTransport.addListener(callback, new HandlerExecutor(handler));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2383,11 +2338,8 @@ public class LocationManager {
|
||||
public boolean registerGnssNavigationMessageCallback(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssNavigationMessage.Callback callback) {
|
||||
try {
|
||||
return mGnssNavigationMessageListenerTransport.addListener(callback, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssNavigationMessageListenerTransport.addListener(callback, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2397,11 +2349,7 @@ public class LocationManager {
|
||||
*/
|
||||
public void unregisterGnssNavigationMessageCallback(
|
||||
@NonNull GnssNavigationMessage.Callback callback) {
|
||||
try {
|
||||
mGnssNavigationMessageListenerTransport.removeListener(callback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mGnssNavigationMessageListenerTransport.removeListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2451,12 +2399,11 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
synchronized (mBatchedLocationCallbackManager) {
|
||||
mBatchedLocationCallbackManager.addListener(callback, new HandlerExecutor(handler));
|
||||
try {
|
||||
if (mBatchedLocationCallbackManager.addListener(callback, handler)) {
|
||||
return mService.startGnssBatch(periodNanos, wakeOnFifoFull,
|
||||
mService.startGnssBatch(periodNanos, wakeOnFifoFull,
|
||||
mContext.getPackageName(), mContext.getFeatureId());
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -2826,13 +2773,41 @@ public class LocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssStatusListenerManager extends
|
||||
AbstractListenerManager<Void, GnssStatus.Callback> {
|
||||
@Nullable
|
||||
private IGnssStatusListener mListenerTransport;
|
||||
private static class GpsAdapter extends GnssStatus.Callback {
|
||||
|
||||
@Nullable
|
||||
private volatile GnssStatus mGnssStatus;
|
||||
private final GpsStatus.Listener mGpsListener;
|
||||
|
||||
private GpsAdapter(GpsStatus.Listener gpsListener) {
|
||||
mGpsListener = gpsListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFix(int ttffMillis) {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSatelliteStatusChanged(GnssStatus status) {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssStatusListenerManager extends
|
||||
ClientListenerManager<Void, GnssStatus.Callback> {
|
||||
|
||||
private @Nullable IGnssStatusListener mListenerTransport;
|
||||
|
||||
private volatile @Nullable GnssStatus mGnssStatus;
|
||||
private volatile int mTtff;
|
||||
|
||||
public GnssStatus getGnssStatus() {
|
||||
@@ -2843,95 +2818,58 @@ public class LocationManager {
|
||||
return mTtff;
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(null, listener, executor);
|
||||
public void addListener(@NonNull OnNmeaMessageListener listener,
|
||||
@NonNull Executor executor) {
|
||||
addListener(listener, null, new NmeaAdapter(listener), executor);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull OnNmeaMessageListener listener,
|
||||
@NonNull Handler handler)
|
||||
throws RemoteException {
|
||||
return addInternal(null, listener, handler);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull OnNmeaMessageListener listener,
|
||||
@NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(null, listener, executor);
|
||||
public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) {
|
||||
addListener(listener, null, new GpsAdapter(listener), executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GnssStatus.Callback convertKey(Object listener) {
|
||||
if (listener instanceof GnssStatus.Callback) {
|
||||
return (GnssStatus.Callback) listener;
|
||||
} else if (listener instanceof GpsStatus.Listener) {
|
||||
return new GnssStatus.Callback() {
|
||||
private final GpsStatus.Listener mGpsListener = (GpsStatus.Listener) listener;
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFix(int ttffMillis) {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSatelliteStatusChanged(GnssStatus status) {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
|
||||
}
|
||||
};
|
||||
} else if (listener instanceof OnNmeaMessageListener) {
|
||||
return new NmeaAdapter((OnNmeaMessageListener) listener);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
protected boolean registerService(Void ignored) {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssStatusListener transport = new GnssStatusListener();
|
||||
if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId())) {
|
||||
try {
|
||||
mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId());
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport != null);
|
||||
|
||||
mService.unregisterGnssStatusCallback(mListenerTransport);
|
||||
mListenerTransport = null;
|
||||
protected void unregisterService() {
|
||||
if (mListenerTransport != null) {
|
||||
try {
|
||||
IGnssStatusListener transport = mListenerTransport;
|
||||
mListenerTransport = null;
|
||||
mService.unregisterGnssStatusCallback(transport);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssStatusListener extends IGnssStatusListener.Stub {
|
||||
@Override
|
||||
public void onGnssStarted() {
|
||||
execute(GnssStatus.Callback::onStarted);
|
||||
deliverToListeners(GnssStatus.Callback::onStarted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGnssStopped() {
|
||||
execute(GnssStatus.Callback::onStopped);
|
||||
deliverToListeners(GnssStatus.Callback::onStopped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFix(int ttff) {
|
||||
mTtff = ttff;
|
||||
execute((callback) -> callback.onFirstFix(ttff));
|
||||
deliverToListeners(callback -> callback.onFirstFix(ttff));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2941,12 +2879,12 @@ public class LocationManager {
|
||||
GnssStatus localStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0s,
|
||||
elevations, azimuths, carrierFreqs, basebandCn0s);
|
||||
mGnssStatus = localStatus;
|
||||
execute((callback) -> callback.onSatelliteStatusChanged(localStatus));
|
||||
deliverToListeners(callback -> callback.onSatelliteStatusChanged(localStatus));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNmeaReceived(long timestamp, String nmea) {
|
||||
execute((callback) -> {
|
||||
deliverToListeners((callback) -> {
|
||||
if (callback instanceof NmeaAdapter) {
|
||||
((NmeaAdapter) callback).onNmeaMessage(nmea, timestamp);
|
||||
}
|
||||
@@ -2956,168 +2894,192 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
private class GnssMeasurementsListenerManager extends
|
||||
AbstractListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
|
||||
ClientListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
|
||||
|
||||
@Nullable
|
||||
private IGnssMeasurementsListener mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService(GnssRequest request) throws RemoteException {
|
||||
protected boolean registerService(GnssRequest request) {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssMeasurementsListener transport = new GnssMeasurementsListener();
|
||||
if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId())) {
|
||||
try {
|
||||
mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId());
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport != null);
|
||||
|
||||
mService.removeGnssMeasurementsListener(mListenerTransport);
|
||||
mListenerTransport = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected GnssRequest merge(@NonNull GnssRequest[] requests) {
|
||||
Preconditions.checkArgument(requests.length > 0);
|
||||
for (GnssRequest request : requests) {
|
||||
if (request.isFullTracking()) {
|
||||
return request;
|
||||
protected void unregisterService() {
|
||||
if (mListenerTransport != null) {
|
||||
try {
|
||||
IGnssMeasurementsListener transport = mListenerTransport;
|
||||
mListenerTransport = null;
|
||||
mService.removeGnssMeasurementsListener(transport);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
return requests[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GnssRequest mergeRequests(
|
||||
List<Registration<GnssRequest, GnssMeasurementsEvent.Callback>> registrations) {
|
||||
for (Registration<GnssRequest, GnssMeasurementsEvent.Callback> registration :
|
||||
registrations) {
|
||||
if (registration.getRequest().isFullTracking()) {
|
||||
return registration.getRequest();
|
||||
}
|
||||
}
|
||||
|
||||
return registrations.get(0).getRequest();
|
||||
}
|
||||
|
||||
private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
|
||||
@Override
|
||||
public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
|
||||
execute((callback) -> callback.onGnssMeasurementsReceived(event));
|
||||
deliverToListeners((callback) -> callback.onGnssMeasurementsReceived(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(int status) {
|
||||
execute((callback) -> callback.onStatusChanged(status));
|
||||
deliverToListeners((callback) -> callback.onStatusChanged(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssNavigationMessageListenerManager extends
|
||||
AbstractListenerManager<Void, GnssNavigationMessage.Callback> {
|
||||
ClientListenerManager<Void, GnssNavigationMessage.Callback> {
|
||||
|
||||
@Nullable
|
||||
private IGnssNavigationMessageListener mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
protected boolean registerService(Void ignored) {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
|
||||
if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId())) {
|
||||
try {
|
||||
mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId());
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport != null);
|
||||
|
||||
mService.removeGnssNavigationMessageListener(mListenerTransport);
|
||||
mListenerTransport = null;
|
||||
protected void unregisterService() {
|
||||
if (mListenerTransport != null) {
|
||||
try {
|
||||
IGnssNavigationMessageListener transport = mListenerTransport;
|
||||
mListenerTransport = null;
|
||||
mService.removeGnssNavigationMessageListener(transport);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
|
||||
@Override
|
||||
public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
|
||||
execute((listener) -> listener.onGnssNavigationMessageReceived(event));
|
||||
deliverToListeners((listener) -> listener.onGnssNavigationMessageReceived(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(int status) {
|
||||
execute((listener) -> listener.onStatusChanged(status));
|
||||
deliverToListeners((listener) -> listener.onStatusChanged(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssAntennaInfoListenerManager extends
|
||||
AbstractListenerManager<Void, GnssAntennaInfo.Listener> {
|
||||
ClientListenerManager<Void, GnssAntennaInfo.Listener> {
|
||||
|
||||
@Nullable
|
||||
private IGnssAntennaInfoListener mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
protected boolean registerService(Void ignored) {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
|
||||
if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId())) {
|
||||
try {
|
||||
mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId());
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport != null);
|
||||
|
||||
mService.removeGnssAntennaInfoListener(mListenerTransport);
|
||||
mListenerTransport = null;
|
||||
protected void unregisterService() {
|
||||
if (mListenerTransport != null) {
|
||||
try {
|
||||
IGnssAntennaInfoListener transport = mListenerTransport;
|
||||
mListenerTransport = null;
|
||||
mService.removeGnssAntennaInfoListener(transport);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub {
|
||||
@Override
|
||||
public void onGnssAntennaInfoReceived(final List<GnssAntennaInfo> gnssAntennaInfos) {
|
||||
execute((callback) -> callback.onGnssAntennaInfoReceived(gnssAntennaInfos));
|
||||
public void onGnssAntennaInfoReceived(List<GnssAntennaInfo> infos) {
|
||||
deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BatchedLocationCallbackManager extends
|
||||
AbstractListenerManager<Void, BatchedLocationCallback> {
|
||||
ClientListenerManager<Void, BatchedLocationCallback> {
|
||||
|
||||
@Nullable
|
||||
private IBatchedLocationCallback mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
protected boolean registerService(Void ignored) {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
BatchedLocationCallback transport = new BatchedLocationCallback();
|
||||
if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId())) {
|
||||
try {
|
||||
mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId());
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport != null);
|
||||
|
||||
mService.removeGnssBatchingCallback();
|
||||
mListenerTransport = null;
|
||||
protected void unregisterService() {
|
||||
if (mListenerTransport != null) {
|
||||
try {
|
||||
mListenerTransport = null;
|
||||
mService.removeGnssBatchingCallback();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BatchedLocationCallback extends IBatchedLocationCallback.Stub {
|
||||
@Override
|
||||
public void onLocationBatch(List<Location> locations) {
|
||||
execute((listener) -> listener.onLocationBatch(locations));
|
||||
deliverToListeners((listener) -> listener.onLocationBatch(locations));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3126,7 +3088,7 @@ public class LocationManager {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
|
||||
private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
|
||||
"cache_key.location_enabled";
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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 android.location.util.listeners;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A base class to manage listeners multiplexed from some source.
|
||||
*
|
||||
* @param <TKey> key type
|
||||
* @param <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
* @param <TRegistration> registration type
|
||||
* @param <TMergedRequest> merged request type
|
||||
* @hide
|
||||
*/
|
||||
public abstract class AbstractListenerManager<TKey, TRequest, TListener,
|
||||
TRegistration extends AbstractListenerManager.Registration<TRequest, TListener>,
|
||||
TMergedRequest> {
|
||||
|
||||
/**
|
||||
* A listener registration object which holds data associated with the listener.
|
||||
*
|
||||
* @param <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
*/
|
||||
public static class Registration<TRequest, TListener> {
|
||||
|
||||
private final Executor mExecutor;
|
||||
private volatile @Nullable TRequest mRequest;
|
||||
private volatile @Nullable TListener mListener;
|
||||
|
||||
protected Registration(@Nullable TRequest request, @NonNull Executor executor,
|
||||
@NonNull TListener listener) {
|
||||
Preconditions.checkArgument(executor != null, "invalid null executor");
|
||||
Preconditions.checkArgument(listener != null, "invalid null listener/callback");
|
||||
mExecutor = executor;
|
||||
mRequest = request;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request associated with this listener, or null if one wasn't supplied.
|
||||
*/
|
||||
public @Nullable TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the listener, or null if this registration is no longer registered.
|
||||
*/
|
||||
protected @Nullable TListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
boolean register() {
|
||||
Preconditions.checkState(mListener != null);
|
||||
return onRegister();
|
||||
}
|
||||
|
||||
protected final void unregister() {
|
||||
if (mListener != null) {
|
||||
onUnregister();
|
||||
mRequest = null;
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden by subclasses. Invoked when registration is occurring. If this returns
|
||||
* true, then registration will complete successfully. If this returns false, registration
|
||||
* will fail.
|
||||
*/
|
||||
protected boolean onRegister() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden by subclasses. Invoked before unregistration occurs.
|
||||
*/
|
||||
protected void onUnregister() {}
|
||||
|
||||
void execute(Consumer<TListener> operation) {
|
||||
mExecutor.execute(() -> {
|
||||
TListener listener = mListener;
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we may be under the binder identity if a direct executor is used
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
operation.accept(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mRequest == null) {
|
||||
return "[]";
|
||||
} else {
|
||||
return mRequest.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private final ArrayMap<TKey, TRegistration> mRegistrations = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private final ReentrancyGuard mReentrancyGuard = new ReentrancyGuard();
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private boolean mServiceRegistered = false;
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private TMergedRequest mCurrentRequest;
|
||||
|
||||
/**
|
||||
* Adds a new registration with the given key. If the registration succeeds,
|
||||
* {@link #onRegistrationAdded(Object, Registration)} will be invoked.
|
||||
*/
|
||||
protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
|
||||
synchronized (mRegistrations) {
|
||||
// this class does not support adding listeners reentrantly
|
||||
Preconditions.checkState(!mReentrancyGuard.isReentrant());
|
||||
|
||||
if (!registration.register()) {
|
||||
registration.unregister();
|
||||
return;
|
||||
}
|
||||
|
||||
TRegistration old = mRegistrations.put(Objects.requireNonNull(key), registration);
|
||||
if (old != null) {
|
||||
onRegistrationRemoved(key, old);
|
||||
old.unregister();
|
||||
}
|
||||
|
||||
onRegistrationAdded(key, registration);
|
||||
updateService();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given registration with the given key. If unregistration occurs,
|
||||
* {@link #onRegistrationRemoved(Object, Registration)} will be called.
|
||||
*/
|
||||
protected final void removeRegistration(@NonNull TKey key,
|
||||
@NonNull TRegistration registration) {
|
||||
synchronized (mRegistrations) {
|
||||
if (mRegistrations.remove(key, registration)) {
|
||||
unregisterRegistration(key, registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the registration with the given key. If unregistration occurs,
|
||||
* {@link #onRegistrationRemoved(Object, Registration)} will be called.
|
||||
*/
|
||||
protected final void removeRegistration(@NonNull TKey key) {
|
||||
synchronized (mRegistrations) {
|
||||
TRegistration registration = mRegistrations.remove(key);
|
||||
if (registration != null) {
|
||||
unregisterRegistration(key, registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private void unregisterRegistration(TKey key, TRegistration registration) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Preconditions.checkState(Thread.holdsLock(mRegistrations));
|
||||
}
|
||||
|
||||
// this class does not support removing listeners reentrantly
|
||||
Preconditions.checkState(!mReentrancyGuard.isReentrant());
|
||||
|
||||
onRegistrationRemoved(key, registration);
|
||||
registration.unregister();
|
||||
updateService();
|
||||
}
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private void updateService() {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Preconditions.checkState(Thread.holdsLock(mRegistrations));
|
||||
}
|
||||
|
||||
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (isActive(registration)) {
|
||||
actives.add(registration);
|
||||
}
|
||||
}
|
||||
|
||||
if (actives.isEmpty()) {
|
||||
if (mServiceRegistered) {
|
||||
unregisterService();
|
||||
mServiceRegistered = false;
|
||||
}
|
||||
mCurrentRequest = null;
|
||||
return;
|
||||
}
|
||||
|
||||
TMergedRequest merged = mergeRequests(actives);
|
||||
if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
|
||||
if (mServiceRegistered) {
|
||||
unregisterService();
|
||||
}
|
||||
mCurrentRequest = merged;
|
||||
mServiceRegistered = registerService(mCurrentRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines whether a registration is currently active or not. Only active registrations will be
|
||||
* considered within {@link #mergeRequests(List)} to calculate the merged request, and listener
|
||||
* invocations will only be delivered to active requests. If a registration's active state
|
||||
* changes, {@link #updateRegistrations(Function)} should be invoked with a function that
|
||||
* returns true for any registrations that have changed their active state in order to inform
|
||||
* this manager of the active status change.
|
||||
*/
|
||||
protected boolean isActive(@NonNull TRegistration registration) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs some function on all (not just active) registrations. The function should return
|
||||
* true if the active state of the registration has changed, or if the change to the
|
||||
* registration may have changed the result of {@link #mergeRequests(List)}.
|
||||
*/
|
||||
protected final void updateRegistrations(@NonNull Function<TRegistration, Boolean> function) {
|
||||
synchronized (mRegistrations) {
|
||||
boolean changed = false;
|
||||
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
changed |= function.apply(mRegistrations.valueAt(i));
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
updateService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in order to generate a merged request from all active requests. The list of
|
||||
* registrations will never be empty.
|
||||
*/
|
||||
@Nullable
|
||||
protected TMergedRequest mergeRequests(@NonNull List<TRegistration> registrations) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
for (TRegistration registration : registrations) {
|
||||
// if using non-null requests then implementations must override this method
|
||||
Preconditions.checkState(registration.getRequest() == null);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be implemented to register the service with the given request, and should return true
|
||||
* if registration succeeds.
|
||||
*/
|
||||
protected abstract boolean registerService(@Nullable TMergedRequest mergedRequest);
|
||||
|
||||
/**
|
||||
* Should be implemented to unregister the service.
|
||||
*/
|
||||
protected abstract void unregisterService();
|
||||
|
||||
/**
|
||||
* Invoked when a registration is added.
|
||||
*/
|
||||
protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {}
|
||||
|
||||
/**
|
||||
* Invoked when a registration is removed.
|
||||
*/
|
||||
protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {}
|
||||
|
||||
/**
|
||||
* Executes the given delivery operation for all active listeners.
|
||||
*/
|
||||
protected final void deliverToListeners(@NonNull Consumer<TListener> operation) {
|
||||
deliverToListeners(operation, registration -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given delivery operation for all active listeners which pass the given
|
||||
* predicate.
|
||||
*/
|
||||
protected final void deliverToListeners(@NonNull Consumer<TListener> operation,
|
||||
@NonNull Predicate<TRegistration> deliveryPredicate) {
|
||||
synchronized (mRegistrations) {
|
||||
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
if (isActive(registration) && deliveryPredicate.test(registration)) {
|
||||
registration.execute(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps debug information.
|
||||
*/
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
||||
|
||||
synchronized (mRegistrations) {
|
||||
ipw.print("service: ");
|
||||
ipw.println(serviceStateToString());
|
||||
|
||||
if (!mRegistrations.isEmpty()) {
|
||||
ipw.println("listeners:");
|
||||
|
||||
ipw.increaseIndent();
|
||||
for (int i = 0; i < mRegistrations.size(); i++) {
|
||||
TRegistration registration = mRegistrations.valueAt(i);
|
||||
ipw.print(registration);
|
||||
if (!isActive(registration)) {
|
||||
ipw.println(" (inactive)");
|
||||
} else {
|
||||
ipw.println();
|
||||
}
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* May be override to provide additional details on service state when dumping the manager
|
||||
* state.
|
||||
*/
|
||||
protected String serviceStateToString() {
|
||||
if (mServiceRegistered) {
|
||||
if (mCurrentRequest == null) {
|
||||
return "registered";
|
||||
} else {
|
||||
return "registered with " + mCurrentRequest;
|
||||
}
|
||||
} else {
|
||||
return "unregistered";
|
||||
}
|
||||
}
|
||||
|
||||
// this class does not have an idempotent close(), always use via try-with-resources
|
||||
private static class ReentrancyGuard implements AutoCloseable {
|
||||
|
||||
private int mGuard = 0;
|
||||
|
||||
ReentrancyGuard() {
|
||||
}
|
||||
|
||||
public boolean isReentrant() {
|
||||
return mGuard != 0;
|
||||
}
|
||||
|
||||
public ReentrancyGuard acquire() {
|
||||
++mGuard;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
--mGuard;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 android.location.util.listeners;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.location.util.listeners.AbstractListenerManager.Registration;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A listener manager for client side implementations, where there is no need to deal with any
|
||||
* complications other than muxing listeners. Listeners without associated requests are supported
|
||||
* without any further work, and listeners with requests must produce muxed requests of the same
|
||||
* type.
|
||||
*
|
||||
* @param <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
* @hide
|
||||
*/
|
||||
public abstract class ClientListenerManager<TRequest, TListener> extends
|
||||
AbstractListenerManager<Object, TRequest, TListener, Registration<TRequest, TListener>,
|
||||
TRequest> {
|
||||
|
||||
/**
|
||||
* Adds a new listener with no request.
|
||||
*/
|
||||
public void addListener(@NonNull TListener listener, @NonNull Executor executor) {
|
||||
addListener(listener, null, listener, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new listener with the given request.
|
||||
*/
|
||||
public void addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
@NonNull Executor executor) {
|
||||
addListener(listener, request, listener, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new listener with the given request using a custom key, rather than using the listener
|
||||
* as the key.
|
||||
*/
|
||||
protected void addListener(@NonNull Object key, @Nullable TRequest request,
|
||||
@NonNull TListener listener, @NonNull Executor executor) {
|
||||
addRegistration(key, new Registration<>(request, executor, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener with the given key.
|
||||
*/
|
||||
public void removeListener(@NonNull Object key) {
|
||||
removeRegistration(key);
|
||||
}
|
||||
}
|
||||
@@ -152,12 +152,12 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
// enable client caches by doing the first invalidate
|
||||
LocationManager.invalidateLocalLocationEnabledCaches();
|
||||
|
||||
publishBinderService(Context.LOCATION_SERVICE, mService);
|
||||
// disable caching for whatever process contains LocationManagerService
|
||||
((LocationManager) mService.mContext.getSystemService(LocationManager.class))
|
||||
|
||||
// client caching behavior is only enabled after seeing the first invalidate
|
||||
LocationManager.invalidateLocalLocationEnabledCaches();
|
||||
// disable caching for our own process
|
||||
Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
|
||||
.disableLocalLocationEnabledCaches();
|
||||
}
|
||||
|
||||
@@ -588,8 +588,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// initialize gnss last because it has no awareness of boot phases and blindly assumes that
|
||||
// all other location providers are loaded at initialization
|
||||
if (GnssManagerService.isGnssSupported()) {
|
||||
mGnssManagerService = new GnssManagerService(mContext, mAppOpsHelper, mSettingsHelper,
|
||||
mAppForegroundHelper, mLocationUsageLogger);
|
||||
mGnssManagerService = new GnssManagerService(mContext, mUserInfoHelper, mSettingsHelper,
|
||||
mAppOpsHelper, mAppForegroundHelper, mLocationUsageLogger);
|
||||
mGnssManagerService.onSystemReady();
|
||||
|
||||
LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
|
||||
@@ -1358,32 +1358,40 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
|
||||
public void addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
|
||||
String featureId) {
|
||||
return mGnssManagerService != null && mGnssManagerService.addGnssBatchingCallback(
|
||||
callback, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.addGnssBatchingCallback(callback, packageName, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeGnssBatchingCallback() {
|
||||
if (mGnssManagerService != null) mGnssManagerService.removeGnssBatchingCallback();
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.removeGnssBatchingCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
|
||||
public void startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
|
||||
String featureId) {
|
||||
return mGnssManagerService != null && mGnssManagerService.startGnssBatch(periodNanos,
|
||||
wakeOnFifoFull, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, packageName, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushGnssBatch(String packageName) {
|
||||
if (mGnssManagerService != null) mGnssManagerService.flushGnssBatch(packageName);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.flushGnssBatch(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopGnssBatch() {
|
||||
return mGnssManagerService != null && mGnssManagerService.stopGnssBatch();
|
||||
public void stopGnssBatch() {
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.stopGnssBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1725,7 +1733,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
mReceiver.isListener(),
|
||||
mReceiver.isPendingIntent(),
|
||||
/* geofence= */ null,
|
||||
mAppForegroundHelper.getImportance(mReceiver.mCallerIdentity.uid));
|
||||
mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.uid));
|
||||
|
||||
// remove from mRecordsByProvider
|
||||
ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
|
||||
@@ -1877,7 +1885,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
|
||||
packageName, request, listener != null, intent != null,
|
||||
/* geofence= */ null,
|
||||
mAppForegroundHelper.getImportance(identity.uid));
|
||||
mAppForegroundHelper.isAppForeground(identity.uid));
|
||||
|
||||
synchronized (mLock) {
|
||||
Receiver receiver;
|
||||
@@ -2134,7 +2142,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
/* hasListener= */ false,
|
||||
true,
|
||||
geofence,
|
||||
mAppForegroundHelper.getImportance(identity.uid));
|
||||
mAppForegroundHelper.isAppForeground(identity.uid));
|
||||
|
||||
mGeofenceManager.addFence(sanitizedRequest, geofence, intent, identity);
|
||||
}
|
||||
@@ -2155,7 +2163,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
/* hasListener= */ false,
|
||||
true,
|
||||
geofence,
|
||||
mAppForegroundHelper.getImportance(Binder.getCallingUid()));
|
||||
mAppForegroundHelper.isAppForeground(Binder.getCallingUid()));
|
||||
|
||||
// geo-fence manager uses the public location API, need to clear identity
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
@@ -2167,22 +2175,27 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
|
||||
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
|
||||
String featureId) {
|
||||
return mGnssManagerService != null && mGnssManagerService.registerGnssStatusCallback(
|
||||
listener, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.registerGnssStatusCallback(listener, packageName, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
|
||||
if (mGnssManagerService != null) mGnssManagerService.unregisterGnssStatusCallback(listener);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.unregisterGnssStatusCallback(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
public void addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
IGnssMeasurementsListener listener, String packageName, String featureId) {
|
||||
return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener(
|
||||
request, listener, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName,
|
||||
featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2209,11 +2222,11 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addGnssAntennaInfoListener(IGnssAntennaInfoListener listener,
|
||||
public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener,
|
||||
String packageName, String featureId) {
|
||||
|
||||
return mGnssManagerService != null && mGnssManagerService.addGnssAntennaInfoListener(
|
||||
listener, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2224,10 +2237,11 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
|
||||
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
|
||||
String packageName, String featureId) {
|
||||
return mGnssManagerService != null && mGnssManagerService.addGnssNavigationMessageListener(
|
||||
listener, packageName, featureId);
|
||||
if (mGnssManagerService != null) {
|
||||
mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, featureId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2339,9 +2353,6 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
@Override
|
||||
public boolean isProviderEnabledForUser(String provider, int userId) {
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
|
||||
userId, false, false, "isProviderEnabledForUser", null);
|
||||
|
||||
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
|
||||
// so we discourage its use
|
||||
if (FUSED_PROVIDER.equals(provider)) return false;
|
||||
@@ -2616,12 +2627,12 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
|
||||
mGnssManagerService.dump(fd, pw, args);
|
||||
return;
|
||||
}
|
||||
if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
|
||||
mGnssManagerService.dump(fd, pw, args);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
ipw.println("Location Manager State:");
|
||||
ipw.increaseIndent();
|
||||
ipw.print("Current System Time: "
|
||||
@@ -2694,13 +2705,11 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mGnssManagerService != null) {
|
||||
ipw.println("GNSS:");
|
||||
ipw.increaseIndent();
|
||||
mGnssManagerService.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
if (mGnssManagerService != null) {
|
||||
ipw.println("GNSS Manager:");
|
||||
ipw.increaseIndent();
|
||||
mGnssManagerService.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2708,14 +2717,15 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
@Override
|
||||
public boolean isProviderEnabledForUser(@NonNull String provider, int userId) {
|
||||
synchronized (mLock) {
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
if (manager == null) {
|
||||
return false;
|
||||
}
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
|
||||
Binder.getCallingUid(), userId, false, false, "isProviderEnabledForUser", null);
|
||||
|
||||
return manager.isEnabled(userId);
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
if (manager == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return manager.isEnabled(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,53 +17,18 @@
|
||||
package com.android.server;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.server.location.CallerIdentity;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Shared utilities for LocationManagerService and GnssManager.
|
||||
*/
|
||||
public class LocationManagerServiceUtils {
|
||||
|
||||
/**
|
||||
* Listener that can be linked to a binder.
|
||||
* @param <TListener> listener type
|
||||
* @param <TRequest> request type
|
||||
*/
|
||||
public static class LinkedListener<TRequest, TListener> extends
|
||||
LinkedListenerBase {
|
||||
@Nullable protected final TRequest mRequest;
|
||||
private final TListener mListener;
|
||||
private final Consumer<TListener> mBinderDeathCallback;
|
||||
|
||||
public LinkedListener(
|
||||
@Nullable TRequest request,
|
||||
@NonNull TListener listener,
|
||||
@NonNull CallerIdentity callerIdentity,
|
||||
@NonNull Consumer<TListener> binderDeathCallback) {
|
||||
super(callerIdentity);
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
mBinderDeathCallback = binderDeathCallback;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
mBinderDeathCallback.accept(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skeleton class of listener that can be linked to a binder.
|
||||
*/
|
||||
|
||||
@@ -106,24 +106,13 @@ public class AppForegroundHelper {
|
||||
* Whether the given uid is currently foreground.
|
||||
*/
|
||||
public boolean isAppForeground(int uid) {
|
||||
return isForeground(getImportance(uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current importance of the given uid.
|
||||
*
|
||||
* @deprecated Prefer {@link #isAppForeground(int)}.
|
||||
*/
|
||||
@Deprecated
|
||||
@Importance
|
||||
public int getImportance(int uid) {
|
||||
synchronized (this) {
|
||||
Preconditions.checkState(mActivityManager != null);
|
||||
}
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mActivityManager.getUidImportance(uid);
|
||||
return isForeground(mActivityManager.getUidImportance(uid));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
|
||||
@@ -16,107 +16,88 @@
|
||||
|
||||
package com.android.server.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.GnssAntennaInfo;
|
||||
import android.location.IGnssAntennaInfoListener;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.location.gnss.GnssManagerService;
|
||||
import com.android.server.location.util.listeners.GnssListenerManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An base implementation for GNSS antenna info provider. It abstracts out the responsibility of
|
||||
* handling listeners, while still allowing technology specific implementations to be built.
|
||||
*
|
||||
* @hide
|
||||
* Provides GNSS antenna information to clients.
|
||||
*/
|
||||
public abstract class GnssAntennaInfoProvider
|
||||
extends RemoteListenerHelper<Void, IGnssAntennaInfoListener> {
|
||||
private static final String TAG = "GnssAntennaInfoProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
public class GnssAntennaInfoProvider extends
|
||||
GnssListenerManager<Void, IGnssAntennaInfoListener, Void> {
|
||||
|
||||
private final GnssAntennaInfoProviderNative mNative;
|
||||
|
||||
private boolean mIsListeningStarted;
|
||||
|
||||
protected GnssAntennaInfoProvider(Context context, Handler handler) {
|
||||
this(context, handler, new GnssAntennaInfoProviderNative());
|
||||
public GnssAntennaInfoProvider(UserInfoHelper userInfoHelper, SettingsHelper settingsHelper,
|
||||
AppOpsHelper appOpsHelper, AppForegroundHelper appForegroundHelper) {
|
||||
this(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper,
|
||||
new GnssAntennaInfoProviderNative());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public GnssAntennaInfoProvider(
|
||||
Context context, Handler handler, GnssAntennaInfoProviderNative aNative) {
|
||||
super(context, handler, TAG);
|
||||
public GnssAntennaInfoProvider(UserInfoHelper userInfoHelper, SettingsHelper settingsHelper,
|
||||
AppOpsHelper appOpsHelper, AppForegroundHelper appForegroundHelper,
|
||||
GnssAntennaInfoProviderNative aNative) {
|
||||
super(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper);
|
||||
mNative = aNative;
|
||||
}
|
||||
|
||||
void resumeIfStarted() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "resumeIfStarted");
|
||||
}
|
||||
if (mIsListeningStarted) {
|
||||
mNative.startAntennaInfoListening();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAvailableInPlatform() {
|
||||
return mNative.isAntennaInfoSupported();
|
||||
public void addListener(CallerIdentity identity, IGnssAntennaInfoListener listener) {
|
||||
super.addListener(identity, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
boolean started = mNative.startAntennaInfoListening();
|
||||
if (started) {
|
||||
mIsListeningStarted = true;
|
||||
return RemoteListenerHelper.RESULT_SUCCESS;
|
||||
}
|
||||
return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterFromService() {
|
||||
boolean stopped = mNative.stopAntennaInfoListening();
|
||||
if (stopped) {
|
||||
mIsListeningStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle GNSS capabilities update from the GNSS HAL implementation. */
|
||||
public void onCapabilitiesUpdated(boolean isAntennaInfoSupported) {
|
||||
setSupported(isAntennaInfoSupported);
|
||||
updateResult();
|
||||
}
|
||||
|
||||
/** Handle GNSS enabled changes.*/
|
||||
public void onGpsEnabledChanged() {
|
||||
tryUpdateRegistrationWithService();
|
||||
updateResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListenerOperation<IGnssAntennaInfoListener> getHandlerOperation(int result) {
|
||||
return (IGnssAntennaInfoListener listener,
|
||||
CallerIdentity callerIdentity) -> {
|
||||
// Do nothing, as GnssAntennaInfo.Callback does not have an onStatusChanged method.
|
||||
};
|
||||
}
|
||||
|
||||
/** Handle Gnss Antenna Info report. */
|
||||
public void onGnssAntennaInfoAvailable(final List<GnssAntennaInfo> gnssAntennaInfos) {
|
||||
foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> {
|
||||
if (!hasPermission(mContext, callerIdentity)) {
|
||||
logPermissionDisabledEventNotReported(
|
||||
TAG, callerIdentity.packageName, "GNSS antenna info");
|
||||
return;
|
||||
protected boolean registerService(Void ignored) {
|
||||
if (mNative.isAntennaInfoSupported()) {
|
||||
if (mNative.startAntennaInfoListening()) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "starting gnss antenna info");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.e(GnssManagerService.TAG, "error starting gnss antenna info");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() {
|
||||
if (mNative.stopAntennaInfoListening()) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "stopping gnss antenna info");
|
||||
}
|
||||
} else {
|
||||
Log.e(GnssManagerService.TAG, "error stopping gnss antenna info");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onGnssAntennaInfoAvailable(List<GnssAntennaInfo> gnssAntennaInfos) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isServiceSupported() {
|
||||
return mNative.isAntennaInfoSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for native methods. This is mocked for testing.
|
||||
*/
|
||||
@@ -138,9 +119,9 @@ public abstract class GnssAntennaInfoProvider
|
||||
}
|
||||
}
|
||||
|
||||
private static native boolean native_is_antenna_info_supported();
|
||||
static native boolean native_is_antenna_info_supported();
|
||||
|
||||
private static native boolean native_start_antenna_info_listening();
|
||||
static native boolean native_start_antenna_info_listening();
|
||||
|
||||
private static native boolean native_stop_antenna_info_listening();
|
||||
static native boolean native_stop_antenna_info_listening();
|
||||
}
|
||||
|
||||
@@ -236,17 +236,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
// Update duration extension multiplier for emergency REQUEST_LOCATION.
|
||||
private static final int EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER = 3;
|
||||
|
||||
/** simpler wrapper for ProviderRequest + Worksource */
|
||||
private static class GpsRequest {
|
||||
public ProviderRequest request;
|
||||
public WorkSource source;
|
||||
|
||||
public GpsRequest(ProviderRequest request, WorkSource source) {
|
||||
this.request = request;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
// Threadsafe class to hold stats reported in the Extras Bundle
|
||||
private static class LocationExtras {
|
||||
private int mSvCount;
|
||||
@@ -370,9 +359,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
|
||||
// Current request from underlying location clients.
|
||||
private ProviderRequest mProviderRequest;
|
||||
// The WorkSource associated with the most recent client request (i.e, most recent call to
|
||||
// setRequest).
|
||||
private WorkSource mWorkSource = null;
|
||||
// True if gps should be disabled because of PowerManager controls
|
||||
private boolean mDisableGpsForPowerManager = false;
|
||||
|
||||
@@ -396,7 +382,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
|
||||
private final Looper mLooper;
|
||||
private final LocationExtras mLocationExtras = new LocationExtras();
|
||||
private final GnssStatusListenerHelper mGnssStatusListenerHelper;
|
||||
private final GnssStatusProvider mGnssStatusListenerHelper;
|
||||
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
|
||||
private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
|
||||
private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
|
||||
@@ -456,7 +442,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
// GNSS Metrics
|
||||
private GnssMetrics mGnssMetrics;
|
||||
|
||||
public GnssStatusListenerHelper getGnssStatusProvider() {
|
||||
public GnssStatusProvider getGnssStatusProvider() {
|
||||
return mGnssStatusListenerHelper;
|
||||
}
|
||||
|
||||
@@ -631,7 +617,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
}
|
||||
|
||||
public GnssLocationProvider(Context context) {
|
||||
public GnssLocationProvider(Context context, UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, LocationUsageLogger logger) {
|
||||
super(FgThread.getExecutor(), context);
|
||||
|
||||
ensureInitialized();
|
||||
@@ -679,40 +667,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
mSuplEsEnabled);
|
||||
sendMessage(INITIALIZE_HANDLER, 0, null);
|
||||
|
||||
mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
|
||||
@Override
|
||||
protected boolean isAvailableInPlatform() {
|
||||
return isSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return GnssLocationProvider.this.isGpsEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
mGnssMeasurementsProvider = new GnssMeasurementsProvider(mContext, mHandler) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return GnssLocationProvider.this.isGpsEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
mGnssStatusListenerHelper = new GnssStatusProvider(userInfoHelper, settingsHelper,
|
||||
appOpsHelper, appForegroundHelper, logger);
|
||||
mGnssMeasurementsProvider = new GnssMeasurementsProvider(userInfoHelper,
|
||||
settingsHelper, appOpsHelper, appForegroundHelper, logger);
|
||||
mGnssMeasurementCorrectionsProvider = new GnssMeasurementCorrectionsProvider(mHandler);
|
||||
|
||||
mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mContext, mHandler) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return GnssLocationProvider.this.isGpsEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return GnssLocationProvider.this.isGpsEnabled();
|
||||
}
|
||||
};
|
||||
mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(userInfoHelper, settingsHelper,
|
||||
appOpsHelper, appForegroundHelper);
|
||||
mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(userInfoHelper,
|
||||
settingsHelper, appOpsHelper, appForegroundHelper);
|
||||
|
||||
mGnssMetrics = new GnssMetrics(mContext, mBatteryStats);
|
||||
mNtpTimeHelper = new NtpTimeHelper(mContext, mLooper, this);
|
||||
@@ -1009,9 +972,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
mC2KServerHost, mC2KServerPort);
|
||||
}
|
||||
|
||||
mGnssMeasurementsProvider.onGpsEnabledChanged();
|
||||
mGnssNavigationMessageProvider.onGpsEnabledChanged();
|
||||
mGnssAntennaInfoProvider.onGpsEnabledChanged();
|
||||
mGnssBatchingProvider.enable();
|
||||
if (mGnssVisibilityControl != null) {
|
||||
mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true);
|
||||
@@ -1037,10 +997,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
mGnssBatchingProvider.disable();
|
||||
// do this before releasing wakelock
|
||||
native_cleanup();
|
||||
|
||||
mGnssAntennaInfoProvider.onGpsEnabledChanged();
|
||||
mGnssMeasurementsProvider.onGpsEnabledChanged();
|
||||
mGnssNavigationMessageProvider.onGpsEnabledChanged();
|
||||
}
|
||||
|
||||
private void updateEnabled() {
|
||||
@@ -1077,26 +1033,25 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
sendMessage(SET_REQUEST, 0, new GpsRequest(request, request.workSource));
|
||||
sendMessage(SET_REQUEST, 0, request);
|
||||
}
|
||||
|
||||
private void handleSetRequest(ProviderRequest request, WorkSource source) {
|
||||
private void handleSetRequest(ProviderRequest request) {
|
||||
mProviderRequest = request;
|
||||
mWorkSource = source;
|
||||
updateEnabled();
|
||||
updateRequirements();
|
||||
}
|
||||
|
||||
// Called when the requirements for GPS may have changed
|
||||
private void updateRequirements() {
|
||||
if (mProviderRequest == null || mWorkSource == null) {
|
||||
if (mProviderRequest == null || mProviderRequest.workSource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
|
||||
if (mProviderRequest.reportLocation && isGpsEnabled()) {
|
||||
// update client uids
|
||||
updateClientUids(mWorkSource);
|
||||
updateClientUids(mProviderRequest.workSource);
|
||||
|
||||
mFixInterval = (int) mProviderRequest.interval;
|
||||
mLowPowerMode = mProviderRequest.lowPowerMode;
|
||||
@@ -1533,19 +1488,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
meanCn0 += gnssStatus.getCn0DbHz(i);
|
||||
mGnssMetrics.logConstellationType(gnssStatus.getConstellationType(i));
|
||||
}
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "svid: " + gnssStatus.getSvid(i)
|
||||
+ " cn0: " + gnssStatus.getCn0DbHz(i)
|
||||
+ " basebandCn0: " + gnssStatus.getBasebandCn0DbHz(i)
|
||||
+ " elev: " + gnssStatus.getElevationDegrees(i)
|
||||
+ " azimuth: " + gnssStatus.getAzimuthDegrees(i)
|
||||
+ " carrier frequency: " + gnssStatus.getCn0DbHz(i)
|
||||
+ (gnssStatus.hasEphemerisData(i) ? " E" : " ")
|
||||
+ (gnssStatus.hasAlmanacData(i) ? " A" : " ")
|
||||
+ (gnssStatus.usedInFix(i) ? "U" : "")
|
||||
+ (gnssStatus.hasCarrierFrequencyHz(i) ? "F" : "")
|
||||
+ (gnssStatus.hasBasebandCn0DbHz(i) ? "B" : ""));
|
||||
}
|
||||
}
|
||||
if (usedInFixCount > 0) {
|
||||
meanCn0 /= usedInFixCount;
|
||||
@@ -1601,13 +1543,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
requestUtcTime();
|
||||
}
|
||||
|
||||
mGnssMeasurementsProvider.onCapabilitiesUpdated(
|
||||
hasCapability(GPS_CAPABILITY_MEASUREMENTS));
|
||||
mGnssNavigationMessageProvider.onCapabilitiesUpdated(
|
||||
hasCapability(GPS_CAPABILITY_NAV_MESSAGES));
|
||||
restartRequests();
|
||||
mGnssAntennaInfoProvider.onCapabilitiesUpdated(
|
||||
hasCapability(GPS_CAPABILITY_ANTENNA_INFO));
|
||||
|
||||
mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities);
|
||||
});
|
||||
@@ -1629,9 +1565,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
Log.i(TAG, "restartRequests");
|
||||
|
||||
restartLocationRequest();
|
||||
mGnssAntennaInfoProvider.resumeIfStarted();
|
||||
mGnssMeasurementsProvider.resumeIfStarted();
|
||||
mGnssNavigationMessageProvider.resumeIfStarted();
|
||||
mGnssBatchingProvider.resumeIfStarted();
|
||||
mGnssGeofenceProvider.resumeIfStarted();
|
||||
}
|
||||
@@ -2062,8 +1995,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
int message = msg.what;
|
||||
switch (message) {
|
||||
case SET_REQUEST:
|
||||
GpsRequest gpsRequest = (GpsRequest) msg.obj;
|
||||
handleSetRequest(gpsRequest.request, gpsRequest.source);
|
||||
handleSetRequest((ProviderRequest) msg.obj);
|
||||
break;
|
||||
case INJECT_NTP_TIME:
|
||||
mNtpTimeHelper.retrieveAndInjectNtpTime();
|
||||
@@ -2152,20 +2084,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class LocationChangeListener implements LocationListener {
|
||||
private abstract static class LocationChangeListener implements LocationListener {
|
||||
private int mNumLocationUpdateRequest;
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
}
|
||||
|
||||
private final class NetworkLocationListener extends LocationChangeListener {
|
||||
@@ -2222,12 +2142,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
s.append(" ago)").append('\n');
|
||||
s.append("mFixInterval=").append(mFixInterval).append('\n');
|
||||
s.append("mLowPowerMode=").append(mLowPowerMode).append('\n');
|
||||
s.append("mGnssAntennaInfoProvider.isRegistered()=")
|
||||
.append(mGnssAntennaInfoProvider.isRegistered()).append('\n');
|
||||
s.append("mGnssMeasurementsProvider.isRegistered()=")
|
||||
.append(mGnssMeasurementsProvider.isRegistered()).append('\n');
|
||||
s.append("mGnssNavigationMessageProvider.isRegistered()=")
|
||||
.append(mGnssNavigationMessageProvider.isRegistered()).append('\n');
|
||||
s.append("mDisableGpsForPowerManager=").append(mDisableGpsForPowerManager).append('\n');
|
||||
s.append("mTopHalCapabilities=0x").append(Integer.toHexString(mTopHalCapabilities));
|
||||
s.append(" ( ");
|
||||
|
||||
@@ -144,8 +144,8 @@ public class GnssMeasurementCorrectionsProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static native boolean native_is_measurement_corrections_supported();
|
||||
static native boolean native_is_measurement_corrections_supported();
|
||||
|
||||
private static native boolean native_inject_gnss_measurement_corrections(
|
||||
static native boolean native_inject_gnss_measurement_corrections(
|
||||
GnssMeasurementCorrections measurementCorrections);
|
||||
}
|
||||
|
||||
@@ -16,163 +16,141 @@
|
||||
|
||||
package com.android.server.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.GnssMeasurementsEvent;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.stats.location.LocationStatsEnums;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.location.gnss.GnssManagerService;
|
||||
import com.android.server.location.util.listeners.GnssListenerManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An base implementation for GPS measurements provider. It abstracts out the responsibility of
|
||||
* An base implementation for GNSS measurements provider. It abstracts out the responsibility of
|
||||
* handling listeners, while still allowing technology specific implementations to be built.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class GnssMeasurementsProvider
|
||||
extends RemoteListenerHelper<GnssRequest, IGnssMeasurementsListener> {
|
||||
private static final String TAG = "GnssMeasProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
public class GnssMeasurementsProvider extends
|
||||
GnssListenerManager<GnssRequest, IGnssMeasurementsListener, Boolean> {
|
||||
|
||||
private final LocationUsageLogger mLogger;
|
||||
private final GnssMeasurementProviderNative mNative;
|
||||
|
||||
private boolean mStartedCollection;
|
||||
private boolean mStartedFullTracking;
|
||||
|
||||
protected GnssMeasurementsProvider(Context context, Handler handler) {
|
||||
this(context, handler, new GnssMeasurementProviderNative());
|
||||
public GnssMeasurementsProvider(UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, LocationUsageLogger logger) {
|
||||
this(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper, logger,
|
||||
new GnssMeasurementProviderNative());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public GnssMeasurementsProvider(
|
||||
Context context, Handler handler, GnssMeasurementProviderNative aNative) {
|
||||
super(context, handler, TAG);
|
||||
public GnssMeasurementsProvider(UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, LocationUsageLogger logger,
|
||||
GnssMeasurementProviderNative aNative) {
|
||||
super(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper);
|
||||
mLogger = logger;
|
||||
mNative = aNative;
|
||||
}
|
||||
|
||||
void resumeIfStarted() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "resumeIfStarted");
|
||||
}
|
||||
if (mStartedCollection) {
|
||||
mNative.startMeasurementCollection(mStartedFullTracking);
|
||||
}
|
||||
@Override
|
||||
public void addListener(GnssRequest request, CallerIdentity identity,
|
||||
IGnssMeasurementsListener listener) {
|
||||
super.addListener(request, identity, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailableInPlatform() {
|
||||
return mNative.isMeasurementSupported();
|
||||
}
|
||||
|
||||
private boolean getMergedFullTracking() {
|
||||
int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
|
||||
int enableFullTracking = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0);
|
||||
boolean enableFullTrackingBySetting = (devOptions == 1 /* Developer Mode enabled */)
|
||||
&& (enableFullTracking == 1 /* Raw Measurements Full Tracking enabled */);
|
||||
if (enableFullTrackingBySetting) {
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized (mListenerMap) {
|
||||
for (IdentifiedListener identifiedListener : mListenerMap.values()) {
|
||||
GnssRequest request = identifiedListener.getRequest();
|
||||
if (request != null && request.isFullTracking()) {
|
||||
return true;
|
||||
protected boolean registerService(Boolean fullTrackingRequest) {
|
||||
if (mNative.isMeasurementSupported()) {
|
||||
if (mNative.startMeasurementCollection(fullTrackingRequest)) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG,
|
||||
"starting gnss measurements (" + fullTrackingRequest + ")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.e(GnssManagerService.TAG, "error starting gnss measurements");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
boolean enableFullTracking = getMergedFullTracking();
|
||||
boolean result = mNative.startMeasurementCollection(enableFullTracking);
|
||||
if (result) {
|
||||
mStartedCollection = true;
|
||||
mStartedFullTracking = enableFullTracking;
|
||||
return RemoteListenerHelper.RESULT_SUCCESS;
|
||||
} else {
|
||||
return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterFromService() {
|
||||
boolean stopped = mNative.stopMeasurementCollection();
|
||||
if (stopped) {
|
||||
mStartedCollection = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
|
||||
foreach((IGnssMeasurementsListener listener, CallerIdentity callerIdentity) -> {
|
||||
if (!hasPermission(mContext, callerIdentity)) {
|
||||
logPermissionDisabledEventNotReported(
|
||||
TAG, callerIdentity.packageName, "GNSS measurements");
|
||||
return;
|
||||
protected void unregisterService() {
|
||||
if (mNative.isMeasurementSupported()) {
|
||||
if (mNative.stopMeasurementCollection()) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "stopping gnss measurements");
|
||||
}
|
||||
} else {
|
||||
Log.e(GnssManagerService.TAG, "error stopping gnss measurements");
|
||||
}
|
||||
listener.onGnssMeasurementsReceived(event);
|
||||
});
|
||||
}
|
||||
|
||||
/** Handle GNSS capabilities update from the GNSS HAL implementation. */
|
||||
public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
|
||||
setSupported(isGnssMeasurementsSupported);
|
||||
updateResult();
|
||||
}
|
||||
|
||||
public void onGpsEnabledChanged() {
|
||||
tryUpdateRegistrationWithService();
|
||||
updateResult();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListenerOperation<IGnssMeasurementsListener> getHandlerOperation(int result) {
|
||||
int status;
|
||||
switch (result) {
|
||||
case RESULT_SUCCESS:
|
||||
status = GnssMeasurementsEvent.Callback.STATUS_READY;
|
||||
break;
|
||||
case RESULT_NOT_AVAILABLE:
|
||||
case RESULT_NOT_SUPPORTED:
|
||||
case RESULT_INTERNAL_ERROR:
|
||||
status = GnssMeasurementsEvent.Callback.STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
case RESULT_NOT_ALLOWED:
|
||||
status = GnssMeasurementsEvent.Callback.STATUS_NOT_ALLOWED;
|
||||
break;
|
||||
case RESULT_GPS_LOCATION_DISABLED:
|
||||
status = GnssMeasurementsEvent.Callback.STATUS_LOCATION_DISABLED;
|
||||
break;
|
||||
case RESULT_UNKNOWN:
|
||||
return null;
|
||||
default:
|
||||
Log.v(TAG, "Unhandled addListener result: " + result);
|
||||
return null;
|
||||
protected Boolean mergeRequests(List<GnssRegistration> registrations) {
|
||||
if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) {
|
||||
return true;
|
||||
}
|
||||
return new StatusChangedOperation(status);
|
||||
|
||||
for (GnssRegistration registration : registrations) {
|
||||
if (registration.getRequest().isFullTracking()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class StatusChangedOperation
|
||||
implements ListenerOperation<IGnssMeasurementsListener> {
|
||||
private final int mStatus;
|
||||
@Override
|
||||
protected void onRegistrationAdded(IBinder key, GnssRegistration registration) {
|
||||
mLogger.logLocationApiUsage(
|
||||
LocationStatsEnums.USAGE_STARTED,
|
||||
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
|
||||
registration.getIdentity().packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
registration.isForeground());
|
||||
}
|
||||
|
||||
public StatusChangedOperation(int status) {
|
||||
mStatus = status;
|
||||
}
|
||||
@Override
|
||||
protected void onRegistrationRemoved(IBinder key, GnssRegistration registration) {
|
||||
mLogger.logLocationApiUsage(
|
||||
LocationStatsEnums.USAGE_ENDED,
|
||||
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
|
||||
registration.getIdentity().packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
registration.isForeground());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(IGnssMeasurementsListener listener,
|
||||
CallerIdentity callerIdentity) throws RemoteException {
|
||||
listener.onStatusChanged(mStatus);
|
||||
}
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
|
||||
deliverToListeners(listener -> {
|
||||
try {
|
||||
listener.onGnssMeasurementsReceived(event);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
}, registration -> mAppOpsHelper.noteLocationAccess(registration.getIdentity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isServiceSupported() {
|
||||
return mNative.isMeasurementSupported();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -190,9 +168,9 @@ public abstract class GnssMeasurementsProvider
|
||||
}
|
||||
}
|
||||
|
||||
private static native boolean native_is_measurement_supported();
|
||||
static native boolean native_is_measurement_supported();
|
||||
|
||||
private static native boolean native_start_measurement_collection(boolean enableFullTracking);
|
||||
static native boolean native_start_measurement_collection(boolean enableFullTracking);
|
||||
|
||||
private static native boolean native_stop_measurement_collection();
|
||||
static native boolean native_stop_measurement_collection();
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
package com.android.server.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.GnssNavigationMessage;
|
||||
import android.location.IGnssNavigationMessageListener;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.location.gnss.GnssManagerService;
|
||||
import com.android.server.location.util.listeners.GnssListenerManager;
|
||||
|
||||
/**
|
||||
* An base implementation for GPS navigation messages provider.
|
||||
@@ -32,113 +32,75 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class GnssNavigationMessageProvider
|
||||
extends RemoteListenerHelper<Void, IGnssNavigationMessageListener> {
|
||||
private static final String TAG = "GnssNavigationMessageProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
public class GnssNavigationMessageProvider extends
|
||||
GnssListenerManager<Void, IGnssNavigationMessageListener, Void> {
|
||||
|
||||
private final GnssNavigationMessageProviderNative mNative;
|
||||
private boolean mCollectionStarted;
|
||||
|
||||
protected GnssNavigationMessageProvider(Context context, Handler handler) {
|
||||
this(context, handler, new GnssNavigationMessageProviderNative());
|
||||
public GnssNavigationMessageProvider(UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper) {
|
||||
this(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper,
|
||||
new GnssNavigationMessageProviderNative());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public GnssNavigationMessageProvider(Context context, Handler handler,
|
||||
GnssNavigationMessageProviderNative aNative) {
|
||||
super(context, handler, TAG);
|
||||
public GnssNavigationMessageProvider(UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, GnssNavigationMessageProviderNative aNative) {
|
||||
super(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper);
|
||||
mNative = aNative;
|
||||
}
|
||||
|
||||
void resumeIfStarted() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "resumeIfStarted");
|
||||
}
|
||||
if (mCollectionStarted) {
|
||||
mNative.startNavigationMessageCollection();
|
||||
}
|
||||
@Override
|
||||
public void addListener(CallerIdentity identity, IGnssNavigationMessageListener listener) {
|
||||
super.addListener(identity, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAvailableInPlatform() {
|
||||
return mNative.isNavigationMessageSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
boolean result = mNative.startNavigationMessageCollection();
|
||||
if (result) {
|
||||
mCollectionStarted = true;
|
||||
return RemoteListenerHelper.RESULT_SUCCESS;
|
||||
} else {
|
||||
return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterFromService() {
|
||||
boolean stopped = mNative.stopNavigationMessageCollection();
|
||||
if (stopped) {
|
||||
mCollectionStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
|
||||
foreach((IGnssNavigationMessageListener listener, CallerIdentity callerIdentity) -> {
|
||||
listener.onGnssNavigationMessageReceived(event);
|
||||
protected boolean registerService(Void ignored) {
|
||||
if (mNative.isNavigationMessageSupported()) {
|
||||
if (mNative.startNavigationMessageCollection()) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "starting gnss navigation messages");
|
||||
}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Handle GNSS capabilities update from the GNSS HAL implementation */
|
||||
public void onCapabilitiesUpdated(boolean isGnssNavigationMessageSupported) {
|
||||
setSupported(isGnssNavigationMessageSupported);
|
||||
updateResult();
|
||||
}
|
||||
|
||||
public void onGpsEnabledChanged() {
|
||||
tryUpdateRegistrationWithService();
|
||||
updateResult();
|
||||
Log.e(GnssManagerService.TAG, "error starting gnss navigation messages");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListenerOperation<IGnssNavigationMessageListener> getHandlerOperation(int result) {
|
||||
int status;
|
||||
switch (result) {
|
||||
case RESULT_SUCCESS:
|
||||
status = GnssNavigationMessage.Callback.STATUS_READY;
|
||||
break;
|
||||
case RESULT_NOT_AVAILABLE:
|
||||
case RESULT_NOT_SUPPORTED:
|
||||
case RESULT_INTERNAL_ERROR:
|
||||
status = GnssNavigationMessage.Callback.STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
case RESULT_GPS_LOCATION_DISABLED:
|
||||
status = GnssNavigationMessage.Callback.STATUS_LOCATION_DISABLED;
|
||||
break;
|
||||
case RESULT_UNKNOWN:
|
||||
return null;
|
||||
default:
|
||||
Log.v(TAG, "Unhandled addListener result: " + result);
|
||||
return null;
|
||||
protected void unregisterService() {
|
||||
if (mNative.isNavigationMessageSupported()) {
|
||||
if (mNative.stopNavigationMessageCollection()) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "stopping gnss navigation messages");
|
||||
}
|
||||
} else {
|
||||
Log.e(GnssManagerService.TAG, "error stopping gnss navigation messages");
|
||||
}
|
||||
}
|
||||
return new StatusChangedOperation(status);
|
||||
}
|
||||
|
||||
private static class StatusChangedOperation
|
||||
implements ListenerOperation<IGnssNavigationMessageListener> {
|
||||
private final int mStatus;
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onGnssNavigationMessageReceived(event);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
}, registration -> mAppOpsHelper.noteLocationAccess(registration.getIdentity()));
|
||||
}
|
||||
|
||||
public StatusChangedOperation(int status) {
|
||||
mStatus = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(IGnssNavigationMessageListener listener,
|
||||
CallerIdentity callerIdentity) throws RemoteException {
|
||||
listener.onStatusChanged(mStatus);
|
||||
}
|
||||
@Override
|
||||
protected boolean isServiceSupported() {
|
||||
return mNative.isNavigationMessageSupported();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -156,9 +118,9 @@ public abstract class GnssNavigationMessageProvider
|
||||
}
|
||||
}
|
||||
|
||||
private static native boolean native_is_navigation_message_supported();
|
||||
static native boolean native_is_navigation_message_supported();
|
||||
|
||||
private static native boolean native_start_navigation_message_collection();
|
||||
static native boolean native_start_navigation_message_collection();
|
||||
|
||||
private static native boolean native_stop_navigation_message_collection();
|
||||
static native boolean native_stop_navigation_message_collection();
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.content.Context;
|
||||
import android.location.IGnssStatusListener;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Implementation of a handler for {@link IGnssStatusListener}.
|
||||
*/
|
||||
public abstract class GnssStatusListenerHelper extends
|
||||
RemoteListenerHelper<Void, IGnssStatusListener> {
|
||||
private static final String TAG = "GnssStatusListenerHelper";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
protected GnssStatusListenerHelper(Context context, Handler handler) {
|
||||
super(context, handler, TAG);
|
||||
setSupported(GnssLocationProvider.isSupported());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
return RemoteListenerHelper.RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterFromService() {}
|
||||
|
||||
@Override
|
||||
protected ListenerOperation<IGnssStatusListener> getHandlerOperation(int result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onStatusChanged(boolean isNavigating) {
|
||||
if (isNavigating) {
|
||||
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
|
||||
listener.onGnssStarted();
|
||||
});
|
||||
} else {
|
||||
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
|
||||
listener.onGnssStopped();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onFirstFix(final int timeToFirstFix) {
|
||||
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
|
||||
listener.onFirstFix(timeToFirstFix);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void onSvStatusChanged(
|
||||
final int svCount,
|
||||
final int[] prnWithFlags,
|
||||
final float[] cn0s,
|
||||
final float[] elevations,
|
||||
final float[] azimuths,
|
||||
final float[] carrierFreqs,
|
||||
final float[] basebandCn0s) {
|
||||
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
|
||||
if (!hasPermission(mContext, callerIdentity)) {
|
||||
logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName,
|
||||
"GNSS status");
|
||||
return;
|
||||
}
|
||||
listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
|
||||
carrierFreqs, basebandCn0s);
|
||||
});
|
||||
}
|
||||
|
||||
public void onNmeaReceived(final long timestamp, final String nmea) {
|
||||
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
|
||||
if (!hasPermission(mContext, callerIdentity)) {
|
||||
logPermissionDisabledEventNotReported(TAG, callerIdentity.packageName, "NMEA");
|
||||
return;
|
||||
}
|
||||
listener.onNmeaReceived(timestamp, nmea);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.location.IGnssStatusListener;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.stats.location.LocationStatsEnums;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.server.location.gnss.GnssManagerService;
|
||||
import com.android.server.location.util.listeners.GnssListenerManager;
|
||||
|
||||
/**
|
||||
* Implementation of a handler for {@link IGnssStatusListener}.
|
||||
*/
|
||||
public class GnssStatusProvider extends GnssListenerManager<Void, IGnssStatusListener, Void> {
|
||||
|
||||
private final LocationUsageLogger mLogger;
|
||||
|
||||
public GnssStatusProvider(UserInfoHelper userInfoHelper, SettingsHelper settingsHelper,
|
||||
AppOpsHelper appOpsHelper, AppForegroundHelper appForegroundHelper,
|
||||
LocationUsageLogger logger) {
|
||||
super(userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper);
|
||||
mLogger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(CallerIdentity identity, IGnssStatusListener listener) {
|
||||
super.addListener(identity, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Void ignored) {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "starting gnss status");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() {
|
||||
if (GnssManagerService.D) {
|
||||
Log.d(GnssManagerService.TAG, "stopping gnss status");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRegistrationAdded(IBinder key, GnssRegistration registration) {
|
||||
mLogger.logLocationApiUsage(
|
||||
LocationStatsEnums.USAGE_STARTED,
|
||||
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
|
||||
registration.getIdentity().packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
registration.isForeground());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRegistrationRemoved(IBinder key, GnssRegistration registration) {
|
||||
mLogger.logLocationApiUsage(
|
||||
LocationStatsEnums.USAGE_ENDED,
|
||||
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
|
||||
registration.getIdentity().packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
registration.isForeground());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onStatusChanged(boolean isNavigating) {
|
||||
if (isNavigating) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onGnssStarted();
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
});
|
||||
} else {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onGnssStopped();
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onFirstFix(int ttff) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onFirstFix(ttff);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onSvStatusChanged(
|
||||
final int svCount,
|
||||
final int[] prnWithFlags,
|
||||
final float[] cn0s,
|
||||
final float[] elevations,
|
||||
final float[] azimuths,
|
||||
final float[] carrierFreqs,
|
||||
final float[] basebandCn0s) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
|
||||
carrierFreqs, basebandCn0s);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
}, registration -> mAppOpsHelper.noteLocationAccess(registration.getIdentity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by GnssLocationProvider.
|
||||
*/
|
||||
public void onNmeaReceived(long timestamp, String nmea) {
|
||||
deliverToListeners((listener) -> {
|
||||
try {
|
||||
listener.onNmeaReceived(timestamp, nmea);
|
||||
} catch (RemoteException e) {
|
||||
// ignore - the listener will get cleaned up later anyways
|
||||
}
|
||||
}, registration -> mAppOpsHelper.noteLocationAccess(registration.getIdentity()));
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package com.android.server.location;
|
||||
|
||||
import static com.android.server.LocationManagerService.TAG;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.location.Geofence;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationRequest;
|
||||
@@ -52,7 +51,7 @@ public class LocationUsageLogger {
|
||||
public void logLocationApiUsage(int usageType, int apiInUse,
|
||||
String packageName, LocationRequest locationRequest,
|
||||
boolean hasListener, boolean hasIntent,
|
||||
Geofence geofence, int activityImportance) {
|
||||
Geofence geofence, boolean foreground) {
|
||||
try {
|
||||
if (hitApiUsageLogCap()) {
|
||||
return;
|
||||
@@ -85,7 +84,7 @@ public class LocationUsageLogger {
|
||||
isGeofenceNull
|
||||
? LocationStatsEnums.RADIUS_UNKNOWN
|
||||
: bucketizeRadius(geofence.getRadius()),
|
||||
categorizeActivityImportance(activityImportance));
|
||||
categorizeActivityImportance(foreground));
|
||||
} catch (Exception e) {
|
||||
// Swallow exceptions to avoid crashing LMS.
|
||||
Log.w(TAG, "Failed to log API usage to statsd.", e);
|
||||
@@ -197,13 +196,9 @@ public class LocationUsageLogger {
|
||||
}
|
||||
}
|
||||
|
||||
private static int categorizeActivityImportance(int importance) {
|
||||
if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
|
||||
private static int categorizeActivityImportance(boolean foreground) {
|
||||
if (foreground) {
|
||||
return LocationStatsEnums.IMPORTANCE_TOP;
|
||||
} else if (importance == ActivityManager
|
||||
.RunningAppProcessInfo
|
||||
.IMPORTANCE_FOREGROUND_SERVICE) {
|
||||
return LocationStatsEnums.IMPORTANCE_FORGROUND_SERVICE;
|
||||
} else {
|
||||
return LocationStatsEnums.IMPORTANCE_BACKGROUND;
|
||||
}
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A helper class that handles operations in remote listeners.
|
||||
*
|
||||
* @param <TRequest> the type of request.
|
||||
* @param <TListener> the type of GNSS data listener.
|
||||
*/
|
||||
public abstract class RemoteListenerHelper<TRequest, TListener extends IInterface> {
|
||||
|
||||
protected static final int RESULT_SUCCESS = 0;
|
||||
protected static final int RESULT_NOT_AVAILABLE = 1;
|
||||
protected static final int RESULT_NOT_SUPPORTED = 2;
|
||||
protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
|
||||
protected static final int RESULT_INTERNAL_ERROR = 4;
|
||||
protected static final int RESULT_UNKNOWN = 5;
|
||||
protected static final int RESULT_NOT_ALLOWED = 6;
|
||||
|
||||
protected final Handler mHandler;
|
||||
private final String mTag;
|
||||
|
||||
protected final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
|
||||
|
||||
protected final Context mContext;
|
||||
protected final AppOpsManager mAppOps;
|
||||
|
||||
private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
|
||||
|
||||
private boolean mHasIsSupported;
|
||||
private boolean mIsSupported;
|
||||
|
||||
private int mLastReportedResult = RESULT_UNKNOWN;
|
||||
|
||||
protected RemoteListenerHelper(Context context, Handler handler, String name) {
|
||||
Objects.requireNonNull(name);
|
||||
mHandler = handler;
|
||||
mTag = name;
|
||||
mContext = context;
|
||||
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
}
|
||||
|
||||
// read-only access for a dump() thread assured via volatile
|
||||
public boolean isRegistered() {
|
||||
return mIsRegistered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
|
||||
*/
|
||||
public void addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
CallerIdentity callerIdentity) {
|
||||
Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
|
||||
IBinder binder = listener.asBinder();
|
||||
synchronized (mListenerMap) {
|
||||
if (mListenerMap.containsKey(binder)) {
|
||||
// listener already added
|
||||
return;
|
||||
}
|
||||
|
||||
IdentifiedListener identifiedListener = new IdentifiedListener(request, listener,
|
||||
callerIdentity);
|
||||
mListenerMap.put(binder, identifiedListener);
|
||||
|
||||
// update statuses we already know about, starting from the ones that will never change
|
||||
int result;
|
||||
if (!isAvailableInPlatform()) {
|
||||
result = RESULT_NOT_AVAILABLE;
|
||||
} else if (mHasIsSupported && !mIsSupported) {
|
||||
result = RESULT_NOT_SUPPORTED;
|
||||
} else if (!isGpsEnabled()) {
|
||||
// only attempt to register if GPS is enabled, otherwise we will register once GPS
|
||||
// becomes available
|
||||
result = RESULT_GPS_LOCATION_DISABLED;
|
||||
} else if (mHasIsSupported && mIsSupported) {
|
||||
tryRegister();
|
||||
// initially presume success, possible internal error could follow asynchornously
|
||||
result = RESULT_SUCCESS;
|
||||
} else {
|
||||
// at this point if the supported flag is not set, the notification will be sent
|
||||
// asynchronously in the future
|
||||
return;
|
||||
}
|
||||
post(identifiedListener, getHandlerOperation(result));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove GNSS data listener {@code listener}.
|
||||
*/
|
||||
public void removeListener(@NonNull TListener listener) {
|
||||
Objects.requireNonNull(listener, "Attempted to remove a 'null' listener.");
|
||||
synchronized (mListenerMap) {
|
||||
mListenerMap.remove(listener.asBinder());
|
||||
if (mListenerMap.isEmpty()) {
|
||||
tryUnregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isAvailableInPlatform();
|
||||
protected abstract boolean isGpsEnabled();
|
||||
// must access only on handler thread
|
||||
protected abstract int registerWithService();
|
||||
protected abstract void unregisterFromService(); // must access only on handler thread
|
||||
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
|
||||
|
||||
protected interface ListenerOperation<TListener extends IInterface> {
|
||||
void execute(TListener listener, CallerIdentity callerIdentity) throws RemoteException;
|
||||
}
|
||||
|
||||
protected void foreach(ListenerOperation<TListener> operation) {
|
||||
synchronized (mListenerMap) {
|
||||
foreachUnsafe(operation);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setSupported(boolean value) {
|
||||
synchronized (mListenerMap) {
|
||||
mHasIsSupported = true;
|
||||
mIsSupported = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected void tryUpdateRegistrationWithService() {
|
||||
synchronized (mListenerMap) {
|
||||
if (!isGpsEnabled()) {
|
||||
tryUnregister();
|
||||
return;
|
||||
}
|
||||
if (mListenerMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
tryRegister();
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateResult() {
|
||||
synchronized (mListenerMap) {
|
||||
int newResult = calculateCurrentResultUnsafe();
|
||||
if (mLastReportedResult == newResult) {
|
||||
return;
|
||||
}
|
||||
foreachUnsafe(getHandlerOperation(newResult));
|
||||
mLastReportedResult = newResult;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasPermission(Context context, CallerIdentity callerIdentity) {
|
||||
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.uid,
|
||||
callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.uid,
|
||||
callerIdentity.packageName, callerIdentity.featureId, null)
|
||||
== AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
protected void logPermissionDisabledEventNotReported(String tag, String packageName,
|
||||
String event) {
|
||||
if (Log.isLoggable(tag, Log.DEBUG)) {
|
||||
Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
|
||||
+ packageName);
|
||||
}
|
||||
}
|
||||
|
||||
private void foreachUnsafe(ListenerOperation<TListener> operation) {
|
||||
for (IdentifiedListener identifiedListener : mListenerMap.values()) {
|
||||
post(identifiedListener, operation);
|
||||
}
|
||||
}
|
||||
|
||||
private void post(IdentifiedListener identifiedListener,
|
||||
ListenerOperation<TListener> operation) {
|
||||
if (operation != null) {
|
||||
mHandler.post(new HandlerRunnable(identifiedListener, operation));
|
||||
}
|
||||
}
|
||||
|
||||
private void tryRegister() {
|
||||
mHandler.post(new Runnable() {
|
||||
int registrationState = RESULT_INTERNAL_ERROR;
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mIsRegistered) {
|
||||
registrationState = registerWithService();
|
||||
mIsRegistered = registrationState == RESULT_SUCCESS;
|
||||
}
|
||||
if (!mIsRegistered) {
|
||||
// post back a failure
|
||||
mHandler.post(() -> {
|
||||
synchronized (mListenerMap) {
|
||||
foreachUnsafe(getHandlerOperation(registrationState));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void tryUnregister() {
|
||||
mHandler.post(() -> {
|
||||
if (!mIsRegistered) {
|
||||
return;
|
||||
}
|
||||
unregisterFromService();
|
||||
mIsRegistered = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private int calculateCurrentResultUnsafe() {
|
||||
// update statuses we already know about, starting from the ones that will never change
|
||||
if (!isAvailableInPlatform()) {
|
||||
return RESULT_NOT_AVAILABLE;
|
||||
}
|
||||
if (!mHasIsSupported || mListenerMap.isEmpty()) {
|
||||
// we'll update once we have a supported status available
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
if (!mIsSupported) {
|
||||
return RESULT_NOT_SUPPORTED;
|
||||
}
|
||||
if (!isGpsEnabled()) {
|
||||
return RESULT_GPS_LOCATION_DISABLED;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
protected class IdentifiedListener {
|
||||
@Nullable private final TRequest mRequest;
|
||||
private final TListener mListener;
|
||||
private final CallerIdentity mCallerIdentity;
|
||||
|
||||
private IdentifiedListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
CallerIdentity callerIdentity) {
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
mCallerIdentity = callerIdentity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
}
|
||||
|
||||
private class HandlerRunnable implements Runnable {
|
||||
private final IdentifiedListener mIdentifiedListener;
|
||||
private final ListenerOperation<TListener> mOperation;
|
||||
|
||||
private HandlerRunnable(IdentifiedListener identifiedListener,
|
||||
ListenerOperation<TListener> operation) {
|
||||
mIdentifiedListener = identifiedListener;
|
||||
mOperation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mOperation.execute(mIdentifiedListener.mListener,
|
||||
mIdentifiedListener.mCallerIdentity);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(mTag, "Error in monitored listener.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.server.location;
|
||||
|
||||
import static android.location.LocationManager.FUSED_PROVIDER;
|
||||
import static android.location.LocationManager.PASSIVE_PROVIDER;
|
||||
import static android.provider.Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
|
||||
@@ -282,6 +283,19 @@ public class SettingsHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the gnss measurements full tracking enabled setting.
|
||||
*/
|
||||
public boolean isGnssMeasurementsFullTrackingEnabled() {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0) == 1;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid
|
||||
* coarsening.
|
||||
|
||||
@@ -33,12 +33,7 @@ import android.location.INetInitiatedListener;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManagerInternal;
|
||||
import android.os.Binder;
|
||||
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;
|
||||
@@ -46,8 +41,6 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.LocationManagerServiceUtils.LinkedListener;
|
||||
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
|
||||
import com.android.server.location.AppForegroundHelper;
|
||||
import com.android.server.location.AppOpsHelper;
|
||||
import com.android.server.location.CallerIdentity;
|
||||
@@ -58,35 +51,33 @@ 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.GnssStatusProvider;
|
||||
import com.android.server.location.LocationUsageLogger;
|
||||
import com.android.server.location.RemoteListenerHelper;
|
||||
import com.android.server.location.SettingsHelper;
|
||||
import com.android.server.location.UserInfoHelper;
|
||||
|
||||
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";
|
||||
public static final String TAG = "GnssManager";
|
||||
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
public static boolean isGnssSupported() {
|
||||
return GnssLocationProvider.isSupported();
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final SettingsHelper mSettingsHelper;
|
||||
private final AppOpsHelper mAppOpsHelper;
|
||||
private final AppForegroundHelper mAppForegroundHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
private final LocationManagerInternal mLocationManagerInternal;
|
||||
|
||||
private final GnssLocationProvider mGnssLocationProvider;
|
||||
private final GnssStatusListenerHelper mGnssStatusProvider;
|
||||
private final GnssStatusProvider mGnssStatusProvider;
|
||||
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
|
||||
private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
|
||||
private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
|
||||
@@ -98,59 +89,40 @@ public class GnssManagerService {
|
||||
private final INetInitiatedListener mNetInitiatedListener;
|
||||
private final IGpsGeofenceHardware mGpsGeofenceProxy;
|
||||
|
||||
@GuardedBy("mGnssMeasurementsListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>>
|
||||
mGnssMeasurementsListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mGnssAntennaInfoListeners")
|
||||
private final ArrayMap<IBinder,
|
||||
LinkedListener<Void, IGnssAntennaInfoListener>>
|
||||
mGnssAntennaInfoListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mGnssNavigationMessageListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>>
|
||||
mGnssNavigationMessageListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mGnssStatusListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<Void, IGnssStatusListener>>
|
||||
mGnssStatusListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("this")
|
||||
@Nullable private LocationManagerInternal mLocationManagerInternal;
|
||||
|
||||
private final Object mGnssBatchingLock = new Object();
|
||||
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
@Nullable private IBatchedLocationCallback mGnssBatchingCallback;
|
||||
|
||||
private @Nullable IBatchedLocationCallback mGnssBatchingCallback;
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
@Nullable
|
||||
private LinkedListener<Void, IBatchedLocationCallback> mGnssBatchingDeathCallback;
|
||||
|
||||
private @Nullable CallerIdentity mGnssBatchingIdentity;
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
private @Nullable Binder.DeathRecipient mGnssBatchingDeathRecipient;
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
private boolean mGnssBatchingInProgress = false;
|
||||
|
||||
public GnssManagerService(Context context, AppOpsHelper appOpsHelper,
|
||||
SettingsHelper settingsHelper, AppForegroundHelper appForegroundHelper,
|
||||
LocationUsageLogger locationUsageLogger) {
|
||||
this(context, appOpsHelper, settingsHelper, appForegroundHelper, locationUsageLogger, null);
|
||||
public GnssManagerService(Context context, UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger) {
|
||||
this(context, userInfoHelper, settingsHelper, appOpsHelper, appForegroundHelper,
|
||||
locationUsageLogger, null);
|
||||
}
|
||||
|
||||
// Can use this constructor to inject GnssLocationProvider for testing
|
||||
@VisibleForTesting
|
||||
GnssManagerService(Context context, AppOpsHelper appOpsHelper, SettingsHelper settingsHelper,
|
||||
GnssManagerService(Context context, UserInfoHelper userInfoHelper,
|
||||
SettingsHelper settingsHelper, AppOpsHelper appOpsHelper,
|
||||
AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger,
|
||||
GnssLocationProvider gnssLocationProvider) {
|
||||
Preconditions.checkState(isGnssSupported());
|
||||
|
||||
mContext = context;
|
||||
mAppOpsHelper = appOpsHelper;
|
||||
mSettingsHelper = settingsHelper;
|
||||
mAppOpsHelper = appOpsHelper;
|
||||
mAppForegroundHelper = appForegroundHelper;
|
||||
mLocationUsageLogger = locationUsageLogger;
|
||||
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
|
||||
|
||||
if (gnssLocationProvider == null) {
|
||||
gnssLocationProvider = new GnssLocationProvider(mContext);
|
||||
gnssLocationProvider = new GnssLocationProvider(mContext, userInfoHelper,
|
||||
mSettingsHelper, mAppOpsHelper, mAppForegroundHelper, locationUsageLogger);
|
||||
}
|
||||
|
||||
mGnssLocationProvider = gnssLocationProvider;
|
||||
@@ -170,17 +142,9 @@ public class GnssManagerService {
|
||||
|
||||
/** Called when system is ready. */
|
||||
public synchronized void onSystemReady() {
|
||||
if (mLocationManagerInternal != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAppOpsHelper.onSystemReady();
|
||||
mSettingsHelper.onSystemReady();
|
||||
mAppForegroundHelper.onSystemReady();
|
||||
|
||||
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
|
||||
|
||||
mAppForegroundHelper.addListener(this::onAppForegroundChanged);
|
||||
}
|
||||
|
||||
/** Retrieve the GnssLocationProvider. */
|
||||
@@ -220,8 +184,8 @@ public class GnssManagerService {
|
||||
* Get size of GNSS batch (GNSS location results are batched together for power savings).
|
||||
*/
|
||||
public int getGnssBatchSize(String packageName) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
return mGnssBatchingProvider.getBatchSize();
|
||||
@@ -234,8 +198,8 @@ public class GnssManagerService {
|
||||
*/
|
||||
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);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
if (!mAppOpsHelper.checkLocationAccess(identity)) {
|
||||
@@ -259,25 +223,28 @@ public class GnssManagerService {
|
||||
*/
|
||||
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
|
||||
@Nullable String featureId) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
mGnssBatchingCallback = callback;
|
||||
mGnssBatchingDeathCallback =
|
||||
new LinkedListener<>(
|
||||
/* request= */ null,
|
||||
callback,
|
||||
identity,
|
||||
(IBatchedLocationCallback listener) -> {
|
||||
stopGnssBatch();
|
||||
removeGnssBatchingCallback();
|
||||
});
|
||||
Binder.DeathRecipient deathRecipient = () -> {
|
||||
synchronized (mGnssBatchingLock) {
|
||||
stopGnssBatch();
|
||||
removeGnssBatchingCallback();
|
||||
}
|
||||
};
|
||||
|
||||
return mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
|
||||
callback.asBinder());
|
||||
try {
|
||||
callback.asBinder().linkToDeath(mGnssBatchingDeathRecipient, 0);
|
||||
mGnssBatchingCallback = callback;
|
||||
mGnssBatchingIdentity = identity;
|
||||
mGnssBatchingDeathRecipient = deathRecipient;
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +254,8 @@ public class GnssManagerService {
|
||||
* @param packageName name of requesting package
|
||||
*/
|
||||
public void flushGnssBatch(String packageName) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
mGnssBatchingProvider.flush();
|
||||
@@ -299,13 +266,17 @@ public class GnssManagerService {
|
||||
* Removes GNSS batching callback.
|
||||
*/
|
||||
public void removeGnssBatchingCallback() {
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked(
|
||||
mGnssBatchingCallback.asBinder());
|
||||
if (mGnssBatchingCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathRecipient, 0);
|
||||
mGnssBatchingCallback = null;
|
||||
mGnssBatchingDeathCallback = null;
|
||||
mGnssBatchingIdentity = null;
|
||||
mGnssBatchingDeathRecipient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +284,7 @@ public class GnssManagerService {
|
||||
* Stop GNSS batch collection.
|
||||
*/
|
||||
public boolean stopGnssBatch() {
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
mGnssBatchingInProgress = false;
|
||||
@@ -321,192 +292,37 @@ public class GnssManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
private void onAppForegroundChanged(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);
|
||||
}
|
||||
synchronized (mGnssAntennaInfoListeners) {
|
||||
updateListenersOnForegroundChangedLocked(
|
||||
mGnssAntennaInfoListeners,
|
||||
mGnssAntennaInfoProvider,
|
||||
IGnssAntennaInfoListener.Stub::asInterface,
|
||||
uid,
|
||||
foreground);
|
||||
}
|
||||
}
|
||||
|
||||
private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
|
||||
Map<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners,
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
Function<IBinder, TListener> mapBinderToListener,
|
||||
int uid,
|
||||
boolean foreground) {
|
||||
for (Map.Entry<IBinder, LinkedListener<TRequest, TListener>> entry :
|
||||
gnssDataListeners.entrySet()) {
|
||||
LinkedListener<TRequest, TListener> linkedListener = entry.getValue();
|
||||
CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
|
||||
TRequest request = linkedListener.getRequest();
|
||||
if (callerIdentity.uid != uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TListener listener = mapBinderToListener.apply(entry.getKey());
|
||||
if (foreground || isThrottlingExempt(callerIdentity)) {
|
||||
gnssDataProvider.addListener(request, listener, callerIdentity);
|
||||
} else {
|
||||
gnssDataProvider.removeListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <TListener extends IInterface, TRequest> boolean addGnssDataListenerLocked(
|
||||
@Nullable TRequest request,
|
||||
TListener listener,
|
||||
String packageName,
|
||||
@Nullable String featureId,
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder,
|
||||
LinkedListener<TRequest, TListener>> gnssDataListeners,
|
||||
Consumer<TListener> binderDeathCallback) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
if (!mAppOpsHelper.checkLocationAccess(identity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener,
|
||||
identity, binderDeathCallback);
|
||||
IBinder binder = listener.asBinder();
|
||||
if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gnssDataListeners.put(binder, linkedListener);
|
||||
if (gnssDataProvider == mGnssMeasurementsProvider
|
||||
|| gnssDataProvider == mGnssStatusProvider) {
|
||||
mLocationUsageLogger.logLocationApiUsage(
|
||||
LocationStatsEnums.USAGE_STARTED,
|
||||
gnssDataProvider == mGnssMeasurementsProvider
|
||||
? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
|
||||
: LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
|
||||
packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
mAppForegroundHelper.getImportance(identity.uid));
|
||||
}
|
||||
if (mAppForegroundHelper.isAppForeground(identity.uid)
|
||||
|| isThrottlingExempt(identity)) {
|
||||
gnssDataProvider.addListener(request, listener, identity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private <TRequest, TListener extends IInterface> void removeGnssDataListenerLocked(
|
||||
TListener listener,
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder, LinkedListener<TRequest, 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<TRequest, TListener> linkedListener =
|
||||
gnssDataListeners.remove(binder);
|
||||
if (linkedListener == null) {
|
||||
return;
|
||||
}
|
||||
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().packageName,
|
||||
/* LocationRequest= */ null,
|
||||
/* hasListener= */ true,
|
||||
/* hasIntent= */ false,
|
||||
/* geofence= */ null,
|
||||
mAppForegroundHelper.getImportance(Binder.getCallingUid()));
|
||||
}
|
||||
linkedListener.unlinkFromListenerDeathNotificationLocked(binder);
|
||||
gnssDataProvider.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers listener for GNSS status changes.
|
||||
*/
|
||||
public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
|
||||
public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
|
||||
@Nullable String featureId) {
|
||||
synchronized (mGnssStatusListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
/* request= */ null,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
mGnssStatusProvider,
|
||||
mGnssStatusListeners,
|
||||
this::unregisterGnssStatusCallback);
|
||||
}
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
mGnssStatusProvider.addListener(identity, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters listener for GNSS status changes.
|
||||
*/
|
||||
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
|
||||
synchronized (mGnssStatusListeners) {
|
||||
removeGnssDataListenerLocked(listener, mGnssStatusProvider, mGnssStatusListeners);
|
||||
}
|
||||
mGnssStatusProvider.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a GNSS measurements listener.
|
||||
*/
|
||||
public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
public void addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
IGnssMeasurementsListener listener, String packageName,
|
||||
@Nullable String featureId) {
|
||||
if (request != null && request.isFullTracking()) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
|
||||
null);
|
||||
}
|
||||
synchronized (mGnssMeasurementsListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
request,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
mGnssMeasurementsProvider,
|
||||
mGnssMeasurementsListeners,
|
||||
this::removeGnssMeasurementsListener);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
if (request.isFullTracking()) {
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
}
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
mGnssMeasurementsProvider.addListener(request, identity, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,8 +330,8 @@ public class GnssManagerService {
|
||||
*/
|
||||
public void injectGnssMeasurementCorrections(
|
||||
GnssMeasurementCorrections measurementCorrections, String packageName) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
|
||||
measurementCorrections);
|
||||
@@ -525,10 +341,7 @@ public class GnssManagerService {
|
||||
* Removes a GNSS measurements listener.
|
||||
*/
|
||||
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
|
||||
synchronized (mGnssMeasurementsListeners) {
|
||||
removeGnssDataListenerLocked(listener, mGnssMeasurementsProvider,
|
||||
mGnssMeasurementsListeners);
|
||||
}
|
||||
mGnssMeasurementsProvider.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -537,19 +350,12 @@ public class GnssManagerService {
|
||||
* @param listener called when GNSS antenna info is received
|
||||
* @param packageName name of requesting package
|
||||
*/
|
||||
public boolean addGnssAntennaInfoListener(
|
||||
IGnssAntennaInfoListener listener, String packageName,
|
||||
public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
|
||||
@Nullable String featureId) {
|
||||
synchronized (mGnssAntennaInfoListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
/* request= */ null,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
mGnssAntennaInfoProvider,
|
||||
mGnssAntennaInfoListeners,
|
||||
this::removeGnssAntennaInfoListener);
|
||||
}
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
mGnssAntennaInfoProvider.addListener(identity, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,38 +364,25 @@ public class GnssManagerService {
|
||||
* @param listener called when GNSS antenna info is received
|
||||
*/
|
||||
public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
|
||||
synchronized (mGnssAntennaInfoListeners) {
|
||||
removeGnssDataListenerLocked(
|
||||
listener, mGnssAntennaInfoProvider, mGnssAntennaInfoListeners);
|
||||
}
|
||||
mGnssAntennaInfoProvider.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a GNSS navigation message listener.
|
||||
*/
|
||||
public boolean addGnssNavigationMessageListener(
|
||||
IGnssNavigationMessageListener listener, String packageName,
|
||||
@Nullable String featureId) {
|
||||
synchronized (mGnssNavigationMessageListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
/* request= */ null,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
mGnssNavigationMessageProvider,
|
||||
mGnssNavigationMessageListeners,
|
||||
this::removeGnssNavigationMessageListener);
|
||||
}
|
||||
public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
|
||||
String packageName, @Nullable String featureId) {
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
|
||||
mGnssNavigationMessageProvider.addListener(identity, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a GNSS navigation message listener.
|
||||
*/
|
||||
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
|
||||
synchronized (mGnssNavigationMessageListeners) {
|
||||
removeGnssDataListenerLocked(
|
||||
listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners);
|
||||
}
|
||||
mGnssNavigationMessageProvider.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,46 +400,29 @@ public class GnssManagerService {
|
||||
* Report location results to GNSS batching listener.
|
||||
*/
|
||||
public void onReportLocation(List<Location> locations) {
|
||||
IBatchedLocationCallback gnssBatchingCallback;
|
||||
LinkedListener<Void, IBatchedLocationCallback> gnssBatchingDeathCallback;
|
||||
IBatchedLocationCallback callback;
|
||||
CallerIdentity identity;
|
||||
synchronized (mGnssBatchingLock) {
|
||||
gnssBatchingCallback = mGnssBatchingCallback;
|
||||
gnssBatchingDeathCallback = mGnssBatchingDeathCallback;
|
||||
callback = mGnssBatchingCallback;
|
||||
identity = mGnssBatchingIdentity;
|
||||
}
|
||||
|
||||
if (gnssBatchingCallback == null || gnssBatchingDeathCallback == null) {
|
||||
if (callback == null || identity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int userId = gnssBatchingDeathCallback.getCallerIdentity().userId;
|
||||
if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, userId)) {
|
||||
if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, identity.userId)) {
|
||||
Log.w(TAG, "reportLocationBatch() called without user permission");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
gnssBatchingCallback.onLocationBatch(locations);
|
||||
callback.onLocationBatch(locations);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "reportLocationBatch() failed", e);
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isThrottlingExempt(CallerIdentity callerIdentity) {
|
||||
if (callerIdentity.uid == Process.SYSTEM_UID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
|
||||
callerIdentity.packageName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
Preconditions.checkState(mLocationManagerInternal != null);
|
||||
}
|
||||
return mLocationManagerInternal.isProviderPackage(callerIdentity.packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump info for debugging.
|
||||
*/
|
||||
@@ -660,31 +436,24 @@ public class GnssManagerService {
|
||||
return;
|
||||
}
|
||||
|
||||
ipw.println("GnssMeasurement Listeners:");
|
||||
ipw.println("Antenna Info Provider:");
|
||||
ipw.increaseIndent();
|
||||
synchronized (mGnssMeasurementsListeners) {
|
||||
for (LinkedListenerBase listener : mGnssMeasurementsListeners.values()) {
|
||||
ipw.println(listener);
|
||||
}
|
||||
}
|
||||
mGnssAntennaInfoProvider.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
|
||||
ipw.println("GnssNavigationMessage Listeners:");
|
||||
ipw.println("Measurement Provider:");
|
||||
ipw.increaseIndent();
|
||||
synchronized (mGnssNavigationMessageListeners) {
|
||||
for (LinkedListenerBase listener : mGnssNavigationMessageListeners.values()) {
|
||||
ipw.println(listener);
|
||||
}
|
||||
}
|
||||
mGnssMeasurementsProvider.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
|
||||
ipw.println("GnssStatus Listeners:");
|
||||
ipw.println("Navigation Message Provider:");
|
||||
ipw.increaseIndent();
|
||||
synchronized (mGnssStatusListeners) {
|
||||
for (LinkedListenerBase listener : mGnssStatusListeners.values()) {
|
||||
ipw.println(listener);
|
||||
}
|
||||
}
|
||||
mGnssNavigationMessageProvider.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
|
||||
ipw.println("Status Provider:");
|
||||
ipw.increaseIndent();
|
||||
mGnssStatusProvider.dump(fd, ipw, args);
|
||||
ipw.decreaseIndent();
|
||||
|
||||
synchronized (mGnssBatchingLock) {
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.location.util.listeners;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationManagerInternal;
|
||||
import android.location.util.listeners.AbstractListenerManager;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.location.AppForegroundHelper;
|
||||
import com.android.server.location.AppOpsHelper;
|
||||
import com.android.server.location.CallerIdentity;
|
||||
import com.android.server.location.SettingsHelper;
|
||||
import com.android.server.location.UserInfoHelper;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Manager for all GNSS related listeners. This class handles deactivating listeners that do not
|
||||
* belong to the current user, that do not have the appropriate permissions, or that are not
|
||||
* currently in the foreground. It will also disable listeners if the GNSS provider is disabled.
|
||||
* Listeners must be registered with the associated IBinder as the key, if the IBinder dies, the
|
||||
* registration will automatically be removed.
|
||||
*
|
||||
* @param <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
* @param <TMergedRequest> merged request type
|
||||
*/
|
||||
public abstract class GnssListenerManager<TRequest, TListener extends IInterface, TMergedRequest>
|
||||
extends AbstractListenerManager<IBinder, TRequest, TListener, GnssListenerManager<TRequest,
|
||||
TListener, TMergedRequest>.GnssRegistration, TMergedRequest> {
|
||||
|
||||
/**
|
||||
* Registration object for GNSS listeners.
|
||||
*/
|
||||
protected class GnssRegistration extends
|
||||
IdentifiedRegistration<TRequest, TListener> implements Binder.DeathRecipient {
|
||||
|
||||
private volatile @Nullable IBinder mKey;
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean mAppOpsAllowed;
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean mForeground;
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean mActive;
|
||||
|
||||
protected GnssRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
|
||||
TListener listener) {
|
||||
super(request, callerIdentity, listener);
|
||||
mKey = listener.asBinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this registration is currently in the foreground.
|
||||
*/
|
||||
public synchronized boolean isForeground() {
|
||||
return mForeground;
|
||||
}
|
||||
|
||||
synchronized boolean isActive() {
|
||||
return mActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean onRegister() {
|
||||
try {
|
||||
Objects.requireNonNull(mKey).linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
mKey = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity());
|
||||
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().uid);
|
||||
onActiveChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnregister() {
|
||||
IBinder key = mKey;
|
||||
if (key != null) {
|
||||
mKey = null;
|
||||
key.unlinkToDeath(this, 0);
|
||||
}
|
||||
}
|
||||
|
||||
boolean onUserChanged(int userId) {
|
||||
return getIdentity().userId == userId;
|
||||
}
|
||||
|
||||
boolean onAppOpsChanged(String packageName) {
|
||||
if (getIdentity().packageName.equals(packageName)) {
|
||||
boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity());
|
||||
synchronized (this) {
|
||||
if (appOpsAllowed != mAppOpsAllowed) {
|
||||
mAppOpsAllowed = appOpsAllowed;
|
||||
return onActiveChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean onForegroundChanged(int uid, boolean foreground) {
|
||||
if (getIdentity().uid == uid) {
|
||||
synchronized (this) {
|
||||
if (foreground != mForeground) {
|
||||
mForeground = foreground;
|
||||
return onActiveChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onActiveChanged() {
|
||||
synchronized (this) {
|
||||
boolean active = mAppOpsAllowed
|
||||
&& (mForeground || isBackgroundRestrictionExempt(getIdentity()))
|
||||
&& mUserInfoHelper.isCurrentUserId(getIdentity().userId);
|
||||
if (active != mActive) {
|
||||
mActive = active;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
IBinder key = mKey;
|
||||
if (key != null) {
|
||||
removeListener(key, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getIdentity());
|
||||
|
||||
ArraySet<String> flags = new ArraySet<>(2);
|
||||
if (!mForeground) {
|
||||
flags.add("bg");
|
||||
}
|
||||
if (!mAppOpsAllowed) {
|
||||
flags.add("na");
|
||||
}
|
||||
if (!flags.isEmpty()) {
|
||||
builder.append(" ").append(flags);
|
||||
}
|
||||
|
||||
if (getRequest() != null) {
|
||||
builder.append(" ").append(getRequest());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected final UserInfoHelper mUserInfoHelper;
|
||||
protected final SettingsHelper mSettingsHelper;
|
||||
protected final AppOpsHelper mAppOpsHelper;
|
||||
protected final AppForegroundHelper mAppForegroundHelper;
|
||||
protected final LocationManagerInternal mLocationManagerInternal;
|
||||
|
||||
protected GnssListenerManager(UserInfoHelper userInfoHelper, SettingsHelper settingsHelper,
|
||||
AppOpsHelper appOpsHelper, AppForegroundHelper appForegroundHelper) {
|
||||
mUserInfoHelper = userInfoHelper;
|
||||
mSettingsHelper = settingsHelper;
|
||||
mAppOpsHelper = appOpsHelper;
|
||||
mAppForegroundHelper = appForegroundHelper;
|
||||
mLocationManagerInternal = Objects.requireNonNull(
|
||||
LocalServices.getService(LocationManagerInternal.class));
|
||||
|
||||
mUserInfoHelper.addListener(this::onUserChanged);
|
||||
mAppOpsHelper.addListener(this::onAppOpsChanged);
|
||||
mAppForegroundHelper.addListener(this::onAppForegroundChanged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener with the given identity.
|
||||
*/
|
||||
protected void addListener(CallerIdentity identity, TListener listener) {
|
||||
addListener(null, identity, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener with the given identity and request.
|
||||
*/
|
||||
protected void addListener(TRequest request, CallerIdentity identity, TListener listener) {
|
||||
addRegistration(listener.asBinder(), new GnssRegistration(request, identity, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given listener.
|
||||
*/
|
||||
public void removeListener(TListener listener) {
|
||||
removeRegistration(listener.asBinder());
|
||||
}
|
||||
|
||||
void removeListener(IBinder key, GnssRegistration registration) {
|
||||
removeRegistration(key, registration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isActive(GnssRegistration registration) {
|
||||
// we don't have an easy listener for provider enabled status changes available, so we
|
||||
// check it every time, which should be pretty cheap
|
||||
return registration.isActive() && mLocationManagerInternal.isProviderEnabledForUser(
|
||||
LocationManager.GPS_PROVIDER, registration.getIdentity().userId);
|
||||
}
|
||||
|
||||
private void onUserChanged(int userId, int change) {
|
||||
if (change == UserInfoHelper.UserListener.USER_SWITCHED) {
|
||||
updateRegistrations(registration -> registration.onUserChanged(userId));
|
||||
}
|
||||
}
|
||||
|
||||
private void onAppOpsChanged(String packageName) {
|
||||
updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
|
||||
}
|
||||
|
||||
private void onAppForegroundChanged(int uid, boolean foreground) {
|
||||
updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground));
|
||||
}
|
||||
|
||||
boolean isBackgroundRestrictionExempt(CallerIdentity callerIdentity) {
|
||||
if (callerIdentity.uid == Process.SYSTEM_UID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
|
||||
callerIdentity.packageName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mLocationManagerInternal.isProviderPackage(callerIdentity.packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden by subclasses to provide extra debug information.
|
||||
*/
|
||||
protected boolean isServiceSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String serviceStateToString() {
|
||||
if (!isServiceSupported()) {
|
||||
return "unsupported";
|
||||
}
|
||||
|
||||
return super.serviceStateToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.location.util.listeners;
|
||||
|
||||
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.location.util.listeners.AbstractListenerManager;
|
||||
import android.os.Process;
|
||||
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.location.CallerIdentity;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A listener manager for system server side implementations where client callers are identified via
|
||||
* {@link CallerIdentity}. Listener callbacks going to other processes will be run on a direct
|
||||
* executor, listener callbacks going to the same process will be run asynchronously.
|
||||
*
|
||||
* @param <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
*/
|
||||
public class IdentifiedRegistration<TRequest, TListener> extends
|
||||
AbstractListenerManager.Registration<TRequest, TListener> {
|
||||
|
||||
private static Executor chooseExecutor(CallerIdentity identity) {
|
||||
// if a client is in the same process as us, binder calls will execute synchronously and
|
||||
// we shouldn't run callbacks directly since they might be run under lock and deadlock
|
||||
if (identity.pid == Process.myPid()) {
|
||||
// there's a slight loophole here for pending intents - pending intent callbacks can
|
||||
// always be run on the direct executor since they're always asynchronous, but honestly
|
||||
// you shouldn't be using pending intent callbacks within the same process anyways
|
||||
return FgThread.getExecutor();
|
||||
} else {
|
||||
return DIRECT_EXECUTOR;
|
||||
}
|
||||
}
|
||||
|
||||
private final CallerIdentity mCallerIdentity;
|
||||
|
||||
protected IdentifiedRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
|
||||
TListener listener) {
|
||||
super(request, chooseExecutor(callerIdentity), listener);
|
||||
mCallerIdentity = Objects.requireNonNull(callerIdentity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the listener identity.
|
||||
*/
|
||||
public CallerIdentity getIdentity() {
|
||||
return mCallerIdentity;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GnssAntennaInfoProvider}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Presubmit
|
||||
public class GnssAntennaInfoProviderTest {
|
||||
@Mock
|
||||
private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative;
|
||||
private GnssAntennaInfoProvider mTestProvider;
|
||||
|
||||
/** Setup. */
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mMockNative.startAntennaInfoListening()).thenReturn(true);
|
||||
when(mMockNative.stopAntennaInfoListening()).thenReturn(true);
|
||||
|
||||
mTestProvider = new GnssAntennaInfoProvider(RuntimeEnvironment.application,
|
||||
new Handler(Looper.myLooper()), mMockNative) {
|
||||
@Override
|
||||
public boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that registerWithService calls the native startAntennaInfoListening method.
|
||||
*/
|
||||
@Test
|
||||
public void register_nativeStarted() {
|
||||
mTestProvider.registerWithService();
|
||||
verify(mMockNative, times(1)).startAntennaInfoListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that unregisterFromService calls the native stopAntennaInfoListening method.
|
||||
*/
|
||||
@Test
|
||||
public void unregister_nativeStopped() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.unregisterFromService();
|
||||
verify(mMockNative, times(1)).stopAntennaInfoListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that GnssAntennaInfoProvider.isAntennaInfoSupported() returns the result of the
|
||||
* native isAntennaInfoSupported method.
|
||||
*/
|
||||
@Test
|
||||
public void isSupported_nativeIsSupported() {
|
||||
when(mMockNative.isAntennaInfoSupported()).thenReturn(true);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
|
||||
|
||||
when(mMockNative.isAntennaInfoSupported()).thenReturn(false);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.android.server.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GnssMeasurementsProvider}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Presubmit
|
||||
public class GnssMeasurementsProviderTest {
|
||||
@Mock
|
||||
private GnssMeasurementsProvider.GnssMeasurementProviderNative mMockNative;
|
||||
private GnssMeasurementsProvider mTestProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mMockNative.startMeasurementCollection(anyBoolean())).thenReturn(true);
|
||||
when(mMockNative.stopMeasurementCollection()).thenReturn(true);
|
||||
|
||||
mTestProvider = new GnssMeasurementsProvider(RuntimeEnvironment.application,
|
||||
new Handler(Looper.myLooper()), mMockNative) {
|
||||
@Override
|
||||
public boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_nativeStarted() {
|
||||
mTestProvider.registerWithService();
|
||||
verify(mMockNative).startMeasurementCollection(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister_nativeStopped() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.unregisterFromService();
|
||||
verify(mMockNative).stopMeasurementCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSupported_nativeIsSupported() {
|
||||
when(mMockNative.isMeasurementSupported()).thenReturn(true);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
|
||||
|
||||
when(mMockNative.isMeasurementSupported()).thenReturn(false);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_resume_started() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.resumeIfStarted();
|
||||
verify(mMockNative, times(2)).startMeasurementCollection(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister_resume_notStarted() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.unregisterFromService();
|
||||
mTestProvider.resumeIfStarted();
|
||||
verify(mMockNative, times(1)).startMeasurementCollection(anyBoolean());
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.android.server.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GnssNavigationMessageProvider}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Presubmit
|
||||
public class GnssNavigationMessageProviderTest {
|
||||
@Mock
|
||||
private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative;
|
||||
private GnssNavigationMessageProvider mTestProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
|
||||
when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
|
||||
|
||||
mTestProvider = new GnssNavigationMessageProvider(RuntimeEnvironment.application,
|
||||
new Handler(Looper.myLooper()), mMockNative) {
|
||||
@Override
|
||||
public boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_nativeStarted() {
|
||||
mTestProvider.registerWithService();
|
||||
verify(mMockNative).startNavigationMessageCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister_nativeStopped() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.unregisterFromService();
|
||||
verify(mMockNative).stopNavigationMessageCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSupported_nativeIsSupported() {
|
||||
when(mMockNative.isNavigationMessageSupported()).thenReturn(true);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
|
||||
|
||||
when(mMockNative.isNavigationMessageSupported()).thenReturn(false);
|
||||
assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_resume_started() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.resumeIfStarted();
|
||||
verify(mMockNative, times(2)).startNavigationMessageCollection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister_resume_notStarted() {
|
||||
mTestProvider.registerWithService();
|
||||
mTestProvider.unregisterFromService();
|
||||
mTestProvider.resumeIfStarted();
|
||||
verify(mMockNative, times(1)).startNavigationMessageCollection();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ android_test {
|
||||
"platform-test-annotations",
|
||||
"truth-prebuilt",
|
||||
"testables",
|
||||
// TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
|
||||
"testng",
|
||||
],
|
||||
|
||||
libs: [
|
||||
|
||||
@@ -27,7 +27,9 @@ import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -62,6 +64,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.CallerIdentity;
|
||||
import com.android.server.location.GnssAntennaInfoProvider;
|
||||
import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
|
||||
import com.android.server.location.GnssBatchingProvider;
|
||||
@@ -72,9 +75,10 @@ import com.android.server.location.GnssMeasurementsProvider;
|
||||
import com.android.server.location.GnssMeasurementsProvider.GnssMeasurementProviderNative;
|
||||
import com.android.server.location.GnssNavigationMessageProvider;
|
||||
import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
|
||||
import com.android.server.location.GnssStatusListenerHelper;
|
||||
import com.android.server.location.GnssStatusProvider;
|
||||
import com.android.server.location.LocationUsageLogger;
|
||||
import com.android.server.location.SettingsHelper;
|
||||
import com.android.server.location.UserInfoHelper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -94,6 +98,9 @@ import java.util.List;
|
||||
*/
|
||||
public class GnssManagerServiceTest {
|
||||
|
||||
private static final long TIMEOUT_MS = 5000;
|
||||
private static final long FAILURE_TIMEOUT_MS = 200;
|
||||
|
||||
// Gnss Providers
|
||||
@Mock
|
||||
private GnssLocationProvider mMockGnssLocationProvider;
|
||||
@@ -108,15 +115,19 @@ public class GnssManagerServiceTest {
|
||||
@Mock
|
||||
private INetInitiatedListener mNetInitiatedListener;
|
||||
private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
|
||||
private GnssStatusListenerHelper mTestGnssStatusProvider;
|
||||
private GnssStatusProvider mTestGnssStatusProvider;
|
||||
private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
|
||||
private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private AppOpsHelper mAppOpsHelper;
|
||||
@Mock
|
||||
private AppOpsManager mAppOpsManager;
|
||||
@Mock
|
||||
private UserInfoHelper mUserInfoHelper;
|
||||
@Mock
|
||||
private SettingsHelper mSettingsHelper;
|
||||
@Mock
|
||||
private AppForegroundHelper mAppForegroundHelper;
|
||||
@@ -146,6 +157,9 @@ public class GnssManagerServiceTest {
|
||||
new String[]{"com.android.server"});
|
||||
enableLocationPermissions();
|
||||
|
||||
when(mUserInfoHelper.isCurrentUserId(anyInt())).thenReturn(true);
|
||||
when(mAppOpsHelper.checkLocationAccess(any(CallerIdentity.class))).thenReturn(true);
|
||||
when(mAppOpsHelper.noteLocationAccess(any(CallerIdentity.class))).thenReturn(true);
|
||||
when(mAppForegroundHelper.isAppForeground(anyInt())).thenReturn(true);
|
||||
|
||||
LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
|
||||
@@ -159,14 +173,10 @@ public class GnssManagerServiceTest {
|
||||
});
|
||||
|
||||
// Setup providers
|
||||
mTestGnssMeasurementsProvider = createGnssMeasurementsProvider(
|
||||
mMockContext, mMockHandler);
|
||||
mTestGnssStatusProvider = createGnssStatusListenerHelper(
|
||||
mMockContext, mMockHandler);
|
||||
mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider(
|
||||
mMockContext, mMockHandler);
|
||||
mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider(
|
||||
mMockContext, mMockHandler);
|
||||
mTestGnssMeasurementsProvider = createGnssMeasurementsProvider();
|
||||
mTestGnssStatusProvider = createGnssStatusListenerHelper();
|
||||
mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider();
|
||||
mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider();
|
||||
|
||||
// Setup GnssLocationProvider to return providers
|
||||
when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn(
|
||||
@@ -192,12 +202,9 @@ 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, appOpsHelper, mSettingsHelper,
|
||||
mAppForegroundHelper, new LocationUsageLogger(),
|
||||
mGnssManagerService = new GnssManagerService(mMockContext, mUserInfoHelper, mSettingsHelper,
|
||||
mAppOpsHelper, mAppForegroundHelper, new LocationUsageLogger(),
|
||||
mMockGnssLocationProvider);
|
||||
mGnssManagerService.onSystemReady();
|
||||
}
|
||||
@@ -315,9 +322,7 @@ public class GnssManagerServiceTest {
|
||||
|
||||
private void disableLocationPermissions() {
|
||||
Mockito.doThrow(new SecurityException()).when(
|
||||
mMockContext).enforceCallingPermission(anyString(), nullable(String.class));
|
||||
Mockito.doThrow(new SecurityException()).when(
|
||||
mMockContext).checkPermission(anyString(), anyInt(), anyInt());
|
||||
mMockContext).enforceCallingOrSelfPermission(anyString(), nullable(String.class));
|
||||
|
||||
when(mAppOpsManager.checkOpNoThrow(anyInt(), anyInt(),
|
||||
anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
|
||||
@@ -326,58 +331,31 @@ public class GnssManagerServiceTest {
|
||||
.thenReturn(false);
|
||||
}
|
||||
|
||||
private GnssStatusListenerHelper createGnssStatusListenerHelper(Context context,
|
||||
Handler handler) {
|
||||
return new GnssStatusListenerHelper(
|
||||
context, handler) {
|
||||
@Override
|
||||
protected boolean isAvailableInPlatform() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private GnssStatusProvider createGnssStatusListenerHelper() {
|
||||
return new GnssStatusProvider(mUserInfoHelper, mSettingsHelper, mAppOpsHelper,
|
||||
mAppForegroundHelper, new LocationUsageLogger());
|
||||
}
|
||||
|
||||
private GnssMeasurementsProvider createGnssMeasurementsProvider(Context context,
|
||||
Handler handler) {
|
||||
private GnssMeasurementsProvider createGnssMeasurementsProvider() {
|
||||
GnssMeasurementProviderNative
|
||||
mockGnssMeasurementProviderNative = mock(GnssMeasurementProviderNative.class);
|
||||
return new GnssMeasurementsProvider(
|
||||
context, handler, mockGnssMeasurementProviderNative) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return new GnssMeasurementsProvider(mUserInfoHelper, mSettingsHelper,
|
||||
mAppOpsHelper, mAppForegroundHelper, new LocationUsageLogger(),
|
||||
mockGnssMeasurementProviderNative);
|
||||
}
|
||||
|
||||
private GnssNavigationMessageProvider createGnssNavigationMessageProvider(Context context,
|
||||
Handler handler) {
|
||||
private GnssNavigationMessageProvider createGnssNavigationMessageProvider() {
|
||||
GnssNavigationMessageProviderNative mockGnssNavigationMessageProviderNative = mock(
|
||||
GnssNavigationMessageProviderNative.class);
|
||||
return new GnssNavigationMessageProvider(context, handler,
|
||||
mockGnssNavigationMessageProviderNative) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return new GnssNavigationMessageProvider(mUserInfoHelper, mSettingsHelper, mAppOpsHelper,
|
||||
mAppForegroundHelper, mockGnssNavigationMessageProviderNative);
|
||||
}
|
||||
|
||||
private GnssAntennaInfoProvider createGnssAntennaInfoProvider(Context context,
|
||||
Handler handler) {
|
||||
private GnssAntennaInfoProvider createGnssAntennaInfoProvider() {
|
||||
GnssAntennaInfoProviderNative mockGnssAntenaInfoProviderNative = mock(
|
||||
GnssAntennaInfoProviderNative.class);
|
||||
return new GnssAntennaInfoProvider(context, handler, mockGnssAntenaInfoProviderNative) {
|
||||
@Override
|
||||
protected boolean isGpsEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return new GnssAntennaInfoProvider(mUserInfoHelper, mSettingsHelper, mAppOpsHelper,
|
||||
mAppForegroundHelper, mockGnssAntenaInfoProviderNative);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -590,7 +568,8 @@ public class GnssManagerServiceTest {
|
||||
|
||||
mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
|
||||
|
||||
verify(mockGnssStatusListener, times(0)).onFirstFix(timeToFirstFix);
|
||||
verify(mockGnssStatusListener, after(FAILURE_TIMEOUT_MS).times(0)).onFirstFix(
|
||||
timeToFirstFix);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -600,12 +579,12 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
assertThat(mGnssManagerService.registerGnssStatusCallback(
|
||||
mockGnssStatusListener, "com.android.server", "abcd123")).isEqualTo(true);
|
||||
mGnssManagerService.registerGnssStatusCallback(
|
||||
mockGnssStatusListener, "com.android.server", "abcd123");
|
||||
|
||||
mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
|
||||
|
||||
verify(mockGnssStatusListener, times(1)).onFirstFix(timeToFirstFix);
|
||||
verify(mockGnssStatusListener, timeout(TIMEOUT_MS).times(1)).onFirstFix(timeToFirstFix);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -622,7 +601,8 @@ public class GnssManagerServiceTest {
|
||||
|
||||
mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
|
||||
|
||||
verify(mockGnssStatusListener, times(0)).onFirstFix(timeToFirstFix);
|
||||
verify(mockGnssStatusListener, after(FAILURE_TIMEOUT_MS).times(0)).onFirstFix(
|
||||
timeToFirstFix);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -640,7 +620,8 @@ public class GnssManagerServiceTest {
|
||||
"com.android.server", null));
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived(
|
||||
verify(mockGnssMeasurementsListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
|
||||
gnssMeasurementsEvent);
|
||||
}
|
||||
|
||||
@@ -653,13 +634,14 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
assertThat(mGnssManagerService.addGnssMeasurementsListener(
|
||||
mGnssManagerService.addGnssMeasurementsListener(
|
||||
new GnssRequest.Builder().build(),
|
||||
mockGnssMeasurementsListener,
|
||||
"com.android.server", null)).isEqualTo(true);
|
||||
"com.android.server", null);
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived(
|
||||
verify(mockGnssMeasurementsListener,
|
||||
timeout(TIMEOUT_MS).times(1)).onGnssMeasurementsReceived(
|
||||
gnssMeasurementsEvent);
|
||||
}
|
||||
|
||||
@@ -711,7 +693,8 @@ public class GnssManagerServiceTest {
|
||||
mockGnssMeasurementsListener);
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived(
|
||||
verify(mockGnssMeasurementsListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
|
||||
gnssMeasurementsEvent);
|
||||
}
|
||||
|
||||
@@ -734,7 +717,8 @@ public class GnssManagerServiceTest {
|
||||
mockGnssMeasurementsListener);
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived(
|
||||
verify(mockGnssMeasurementsListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
|
||||
gnssMeasurementsEvent);
|
||||
}
|
||||
|
||||
@@ -752,7 +736,7 @@ public class GnssManagerServiceTest {
|
||||
"com.android.server", null));
|
||||
|
||||
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
|
||||
verify(mockGnssAntennaInfoListener, times(0))
|
||||
verify(mockGnssAntennaInfoListener, after(FAILURE_TIMEOUT_MS).times(0))
|
||||
.onGnssAntennaInfoReceived(gnssAntennaInfos);
|
||||
}
|
||||
|
||||
@@ -764,11 +748,11 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
|
||||
"com.android.server", null)).isEqualTo(true);
|
||||
mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
|
||||
"com.android.server", null);
|
||||
|
||||
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
|
||||
verify(mockGnssAntennaInfoListener, times(1))
|
||||
verify(mockGnssAntennaInfoListener, timeout(TIMEOUT_MS).times(1))
|
||||
.onGnssAntennaInfoReceived(gnssAntennaInfos);
|
||||
}
|
||||
|
||||
@@ -790,8 +774,8 @@ public class GnssManagerServiceTest {
|
||||
mockGnssAntennaInfoListener);
|
||||
|
||||
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
|
||||
verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
|
||||
gnssAntennaInfos);
|
||||
verify(mockGnssAntennaInfoListener, after(FAILURE_TIMEOUT_MS).times(0))
|
||||
.onGnssAntennaInfoReceived(gnssAntennaInfos);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -810,7 +794,8 @@ public class GnssManagerServiceTest {
|
||||
mockGnssAntennaInfoListener);
|
||||
|
||||
mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
|
||||
verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
|
||||
verify(mockGnssAntennaInfoListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssAntennaInfoReceived(
|
||||
gnssAntennaInfos);
|
||||
}
|
||||
|
||||
@@ -828,7 +813,8 @@ public class GnssManagerServiceTest {
|
||||
|
||||
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
|
||||
|
||||
verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived(
|
||||
verify(mockGnssNavigationMessageListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
|
||||
gnssNavigationMessage);
|
||||
}
|
||||
|
||||
@@ -840,13 +826,13 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
assertThat(mGnssManagerService.addGnssNavigationMessageListener(
|
||||
mockGnssNavigationMessageListener, "com.android.server", null))
|
||||
.isEqualTo(true);
|
||||
mGnssManagerService.addGnssNavigationMessageListener(
|
||||
mockGnssNavigationMessageListener, "com.android.server", null);
|
||||
|
||||
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
|
||||
|
||||
verify(mockGnssNavigationMessageListener, times(1)).onGnssNavigationMessageReceived(
|
||||
verify(mockGnssNavigationMessageListener,
|
||||
timeout(TIMEOUT_MS).times(1)).onGnssNavigationMessageReceived(
|
||||
gnssNavigationMessage);
|
||||
}
|
||||
|
||||
@@ -868,7 +854,8 @@ public class GnssManagerServiceTest {
|
||||
|
||||
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
|
||||
|
||||
verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived(
|
||||
verify(mockGnssNavigationMessageListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
|
||||
gnssNavigationMessage);
|
||||
}
|
||||
|
||||
@@ -888,7 +875,8 @@ public class GnssManagerServiceTest {
|
||||
|
||||
mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
|
||||
|
||||
verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived(
|
||||
verify(mockGnssNavigationMessageListener,
|
||||
after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
|
||||
gnssNavigationMessage);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.location.util.listeners;
|
||||
|
||||
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.location.util.listeners.AbstractListenerManager;
|
||||
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 java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AbstractListenerManagerTest {
|
||||
|
||||
private TestListenerManager mListenerManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mListenerManager = new TestListenerManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
Runnable listener = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(0);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
Runnable listener = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener);
|
||||
mListenerManager.removeListener(listener);
|
||||
assertThat(mListenerManager.mRegistered).isFalse();
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener, never()).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeMultiple() {
|
||||
Runnable listener1 = mock(Runnable.class);
|
||||
Runnable listener2 = mock(Runnable.class);
|
||||
Runnable listener3 = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener1);
|
||||
mListenerManager.addListener(1, listener2);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(1);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(1)).run();
|
||||
verify(listener2, times(1)).run();
|
||||
verify(listener3, times(0)).run();
|
||||
|
||||
mListenerManager.addListener(0, listener3);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(1);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(2)).run();
|
||||
verify(listener2, times(2)).run();
|
||||
verify(listener3, times(1)).run();
|
||||
|
||||
mListenerManager.removeListener(listener2);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(0);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(3)).run();
|
||||
verify(listener2, times(2)).run();
|
||||
verify(listener3, times(2)).run();
|
||||
|
||||
mListenerManager.removeListener(listener1);
|
||||
mListenerManager.removeListener(listener3);
|
||||
assertThat(mListenerManager.mRegistered).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredicate() {
|
||||
Runnable listener = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener);
|
||||
|
||||
mListenerManager.notifyListeners(i -> i != 0);
|
||||
verify(listener, never()).run();
|
||||
|
||||
mListenerManager.notifyListeners(i -> i == 0);
|
||||
verify(listener).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInactive() {
|
||||
Runnable listener = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener);
|
||||
mListenerManager.setActive(0, false);
|
||||
assertThat(mListenerManager.mRegistered).isFalse();
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener, never()).run();
|
||||
|
||||
mListenerManager.setActive(0, true);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(0);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeMultiple_Inactive() {
|
||||
Runnable listener1 = mock(Runnable.class);
|
||||
Runnable listener2 = mock(Runnable.class);
|
||||
Runnable listener3 = mock(Runnable.class);
|
||||
|
||||
mListenerManager.addListener(0, listener1);
|
||||
mListenerManager.addListener(2, listener2);
|
||||
mListenerManager.addListener(1, listener3);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(2);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(1)).run();
|
||||
verify(listener2, times(1)).run();
|
||||
verify(listener3, times(1)).run();
|
||||
|
||||
mListenerManager.setActive(2, false);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(1);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(2)).run();
|
||||
verify(listener2, times(1)).run();
|
||||
verify(listener3, times(2)).run();
|
||||
|
||||
mListenerManager.setActive(2, true);
|
||||
assertThat(mListenerManager.mRegistered).isTrue();
|
||||
assertThat(mListenerManager.mMergedRequest).isEqualTo(2);
|
||||
|
||||
mListenerManager.notifyListeners();
|
||||
verify(listener1, times(3)).run();
|
||||
verify(listener2, times(2)).run();
|
||||
verify(listener3, times(3)).run();
|
||||
|
||||
mListenerManager.setActive(0, false);
|
||||
mListenerManager.setActive(1, false);
|
||||
mListenerManager.setActive(2, false);
|
||||
assertThat(mListenerManager.mRegistered).isFalse();
|
||||
}
|
||||
|
||||
private static class TestRegistration extends
|
||||
AbstractListenerManager.Registration<Integer, Runnable> {
|
||||
|
||||
private boolean mActive = true;
|
||||
|
||||
protected TestRegistration(Integer integer, Runnable runnable) {
|
||||
super(integer, DIRECT_EXECUTOR, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestListenerManager extends
|
||||
AbstractListenerManager<Runnable, Integer, Runnable, TestRegistration, Integer> {
|
||||
|
||||
private boolean mRegistered;
|
||||
private int mMergedRequest;
|
||||
|
||||
public void addListener(Integer request, Runnable listener) {
|
||||
addRegistration(listener, new TestRegistration(request, listener));
|
||||
}
|
||||
|
||||
public void removeListener(Runnable listener) {
|
||||
removeRegistration(listener);
|
||||
}
|
||||
|
||||
public void setActive(Integer request, boolean active) {
|
||||
updateRegistrations(testRegistration -> {
|
||||
if (testRegistration.getRequest().equals(request)) {
|
||||
testRegistration.mActive = active;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyListeners() {
|
||||
deliverToListeners(Runnable::run);
|
||||
}
|
||||
|
||||
public void notifyListeners(Predicate<Integer> predicate) {
|
||||
deliverToListeners(Runnable::run, r -> predicate.test(r.getRequest()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerService(Integer mergedRequest) {
|
||||
mRegistered = true;
|
||||
mMergedRequest = mergedRequest;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterService() {
|
||||
mRegistered = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isActive(TestRegistration registration) {
|
||||
return registration.mActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer mergeRequests(List<TestRegistration> testRegistrations) {
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (TestRegistration registration : testRegistrations) {
|
||||
if (registration.getRequest() > max) {
|
||||
max = registration.getRequest();
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user