Refactor how location providers are managed
Put enabled/disabled state under location provider control, and use it
to represent whether a location provider may be used, not whether the
user has enabled or disabled a location provider.
Bug: 118885128
Test: manually
Change-Id: I1209c49c13ca8995b223f383ad332322fffc7a96
diff --git a/Android.bp b/Android.bp
index 5568673..d64e7a2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -451,6 +451,7 @@
"location/java/android/location/IGpsGeofenceHardware.aidl",
"location/java/android/location/INetInitiatedListener.aidl",
"location/java/com/android/internal/location/ILocationProvider.aidl",
+ "location/java/com/android/internal/location/ILocationProviderManager.aidl",
"media/java/android/media/IAudioFocusDispatcher.aidl",
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
diff --git a/api/current.txt b/api/current.txt
index fe91ba1..da64a13 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22732,8 +22732,8 @@
method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
- method public void clearTestProviderEnabled(java.lang.String);
- method public void clearTestProviderLocation(java.lang.String);
+ method public deprecated void clearTestProviderEnabled(java.lang.String);
+ method public deprecated void clearTestProviderLocation(java.lang.String);
method public deprecated void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 01c7028..56dc4c1 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1849,13 +1849,16 @@
Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
+Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V
Lcom/android/internal/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
Lcom/android/internal/net/VpnConfig;-><init>()V
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ae87998..32c7520 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -96,9 +96,7 @@
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
void setTestProviderLocation(String provider, in Location loc, String opPackageName);
- void clearTestProviderLocation(String provider, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
- void clearTestProviderEnabled(String provider, String opPackageName);
// --- deprecated ---
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
@@ -108,12 +106,8 @@
// --- internal ---
- // Used by location providers to tell the location manager when it has a new location.
- // Passive is true if the location is coming from the passive provider, in which case
- // it need not be shared with other providers.
+ // --- deprecated ---
void reportLocation(in Location location, boolean passive);
-
- // Used when a (initially Gnss) Location batch arrives
void reportLocationBatch(in List<Location> locations);
// for reporting callback completion
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3bf98b3..334170e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1537,14 +1537,11 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This function has always been a no-op, and may be removed in the future.
*/
- public void clearTestProviderLocation(String provider) {
- try {
- mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ @Deprecated
+ public void clearTestProviderLocation(String provider) {}
/**
* Sets a mock enabled value for the given provider. This value will be used in place
@@ -1575,13 +1572,12 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
*/
+ @Deprecated
public void clearTestProviderEnabled(String provider) {
- try {
- mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderEnabled(provider, true);
}
/**
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 39c2d92..71b54fb 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -16,29 +16,26 @@
package com.android.internal.location;
-import android.location.Location;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.WorkSource;
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderRequest;
/**
- * Binder interface for services that implement location providers.
- * <p>Use {@link LocationProviderBase} as a helper to implement this
- * interface.
+ * Binder interface for services that implement location providers. Do not implement this directly,
+ * extend {@link LocationProviderBase} instead.
* @hide
*/
interface ILocationProvider {
- void enable();
- void disable();
- void setRequest(in ProviderRequest request, in WorkSource ws);
+ oneway void setLocationProviderManager(in ILocationProviderManager manager);
- // --- deprecated (but still supported) ---
- ProviderProperties getProperties();
+ oneway void setRequest(in ProviderRequest request, in WorkSource ws);
+
+ oneway void sendExtraCommand(String command, in Bundle extras);
+
+ // --- deprecated and will be removed the future ---
int getStatus(out Bundle extras);
long getStatusUpdateTime();
- boolean sendExtraCommand(String command, inout Bundle extras);
}
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
new file mode 100644
index 0000000..b1b8f0c
--- /dev/null
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.internal.location;
+
+import android.location.Location;
+
+import com.android.internal.location.ProviderProperties;
+
+/**
+ * Binder interface for manager of all location providers.
+ * @hide
+ */
+interface ILocationProviderManager {
+
+ void onSetEnabled(boolean enabled);
+
+ void onSetProperties(in ProviderProperties properties);
+
+ void onReportLocation(in Location location);
+}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 88919f6..a45c20d 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,15 +16,15 @@
package com.android.internal.location;
-import java.util.ArrayList;
-import java.util.List;
-
import android.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.TimeUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class ProviderRequest implements Parcelable {
/** Location reporting is requested (true) */
@@ -36,6 +36,13 @@
public long interval = Long.MAX_VALUE;
/**
+ * When this flag is true, providers should ignore all location settings, user consents, power
+ * restrictions or any other restricting factors and always satisfy this request to the best of
+ * their ability. This flag should only be used in event of an emergency.
+ */
+ public boolean forceLocation = false;
+
+ /**
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
* use.
*/
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d19559e..10c3447 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -8,14 +8,18 @@
public abstract class LocationProviderBase {
ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
method public android.os.IBinder getBinder();
- method public abstract void onDisable();
- method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public abstract void onEnable();
- method public deprecated int onGetStatus(android.os.Bundle);
- method public deprecated long onGetStatusUpdateTime();
- method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
- method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method public final void reportLocation(android.location.Location);
+ method public boolean isEnabled();
+ method protected deprecated void onDisable();
+ method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method protected deprecated void onEnable();
+ method protected deprecated int onGetStatus(android.os.Bundle);
+ method protected deprecated long onGetStatusUpdateTime();
+ method protected void onInit();
+ method protected boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+ method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+ method public void reportLocation(android.location.Location);
+ method public void setEnabled(boolean);
+ method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
field public static final java.lang.String FUSED_PROVIDER = "fused";
}
@@ -38,6 +42,7 @@
}
public final class ProviderRequestUnbundled {
+ method public boolean getForceLocation();
method public long getInterval();
method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
method public boolean getReportLocation();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d45a4ba..5bcec92 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,6 +16,7 @@
package com.android.location.provider;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
@@ -29,12 +30,11 @@
import android.util.Log;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.PrintWriter;
/**
@@ -55,12 +55,6 @@
* of this package for more information.
*/
public abstract class LocationProviderBase {
- private final String TAG;
-
- /** @hide */
- protected final ILocationManager mLocationManager;
- private final ProviderProperties mProperties;
- private final IBinder mBinder;
/**
* Bundle key for a version of the location containing no GPS data.
@@ -77,49 +71,34 @@
*/
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
- private final class Service extends ILocationProvider.Stub {
- @Override
- public void enable() {
- onEnable();
- }
- @Override
- public void disable() {
- onDisable();
- }
- @Override
- public void setRequest(ProviderRequest request, WorkSource ws) {
- onSetRequest(new ProviderRequestUnbundled(request), ws);
- }
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
- @Override
- public int getStatus(Bundle extras) {
- return onGetStatus(extras);
- }
- @Override
- public long getStatusUpdateTime() {
- return onGetStatusUpdateTime();
- }
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return onSendExtraCommand(command, extras);
- }
- @Override
- public void dump(FileDescriptor fd, String[] args) {
- PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
- onDump(fd, pw, args);
- pw.flush();
- }
- }
+ private final String mTag;
+ private final IBinder mBinder;
+
+ /**
+ * This field may be removed in the future, do not rely on it.
+ *
+ * @deprecated Do not use this field! Use LocationManager APIs instead. If you use this field
+ * you may be broken in the future.
+ * @hide
+ */
+ @Deprecated
+ protected final ILocationManager mLocationManager;
+
+ // write locked on mBinder, read lock is optional depending on atomicity requirements
+ @Nullable private volatile ILocationProviderManager mManager;
+ private volatile ProviderProperties mProperties;
+ private volatile boolean mEnabled;
public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
- TAG = tag;
- IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
- mLocationManager = ILocationManager.Stub.asInterface(b);
- mProperties = properties.getProviderProperties();
+ mTag = tag;
mBinder = new Service();
+
+ mLocationManager = ILocationManager.Stub.asInterface(
+ ServiceManager.getService(Context.LOCATION_SERVICE));
+
+ mManager = null;
+ mProperties = properties.getProviderProperties();
+ mEnabled = true;
}
public IBinder getBinder() {
@@ -127,51 +106,116 @@
}
/**
- * Used by the location provider to report new locations.
+ * Sets whether this provider is currently enabled or not. Note that this is specific to the
+ * provider only, and is not related to global location settings. This is a hint to the Location
+ * Manager that this provider will generally be unable to fulfill incoming requests. This
+ * provider may still receive callbacks to onSetRequest while not enabled, and must decide
+ * whether to attempt to satisfy those requests or not.
*
- * @param location new Location to report
- *
- * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+ * Some guidelines: providers should set their own enabled/disabled status based only on state
+ * "owned" by that provider. For instance, providers should not take into account the state of
+ * the location master setting when setting themselves enabled or disabled, as this state is not
+ * owned by a particular provider. If a provider requires some additional user consent that is
+ * particular to the provider, this should be use to set the enabled/disabled state. If the
+ * provider proxies to another provider, the child provider's enabled/disabled state should be
+ * taken into account in the parent's enabled/disabled state. For most providers, it is expected
+ * that they will be always enabled.
*/
- public final void reportLocation(Location location) {
- try {
- mLocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException", e);
- } catch (Exception e) {
- // never crash provider, might be running in a system process
- Log.e(TAG, "Exception", e);
+ public void setEnabled(boolean enabled) {
+ synchronized (mBinder) {
+ if (mEnabled == enabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
}
}
/**
- * Enable the location provider.
- * <p>The provider may initialize resources, but does
- * not yet need to report locations.
+ * Sets the provider properties that may be queried by clients. Generally speaking, providers
+ * should try to avoid changing their properties after construction.
*/
- public abstract void onEnable();
+ public void setProperties(ProviderPropertiesUnbundled properties) {
+ synchronized (mBinder) {
+ mProperties = properties.getProviderProperties();
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetProperties(mProperties);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
/**
- * Disable the location provider.
- * <p>The provider must release resources, and stop
- * performing work. It may no longer report locations.
+ * Returns true if this provider has been set as enabled. This will be true unless explicitly
+ * set otherwise.
*/
- public abstract void onDisable();
+ public boolean isEnabled() {
+ return mEnabled;
+ }
/**
- * Set the {@link ProviderRequest} requirements for this provider.
- * <p>Each call to this method overrides all previous requests.
- * <p>This method might trigger the provider to start returning
- * locations, or to stop returning locations, depending on the
- * parameters in the request.
+ * Reports a new location from this provider.
*/
- public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+ public void reportLocation(Location location) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocation(location);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ protected void onInit() {
+ // call once so that providers designed for APIs pre-Q are not broken
+ onEnable();
+ }
+
+ /**
+ * @deprecated This callback will be invoked once when the provider is created to maintain
+ * backwards compatibility with providers not designed for Android Q and above. This method
+ * should only be implemented in location providers that need to support SDKs below Android Q.
+ * Even in this case, it is usually unnecessary to implement this callback with the correct
+ * design. This method may be removed in the future.
+ */
+ @Deprecated
+ protected void onEnable() {}
+
+ /**
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. Even in
+ * this case, it is usually unnecessary to implement this callback with the correct design. This
+ * method may be removed in the future.
+ */
+ @Deprecated
+ protected void onDisable() {}
+
+ /**
+ * Set the {@link ProviderRequest} requirements for this provider. Each call to this method
+ * overrides all previous requests. This method might trigger the provider to start returning
+ * locations, or to stop returning locations, depending on the parameters in the request.
+ */
+ protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
/**
* Dump debug information.
*/
- public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- }
+ protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
/**
* This method will no longer be invoked.
@@ -187,10 +231,12 @@
* <p>If extras is non-null, additional status information may be
* added to it in the form of provider-specific key/value pairs.
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public int onGetStatus(Bundle extras) {
+ protected int onGetStatus(Bundle extras) {
return LocationProvider.AVAILABLE;
}
@@ -206,24 +252,64 @@
*
* @return time of last status update in millis since last reboot
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public long onGetStatusUpdateTime() {
+ protected long onGetStatusUpdateTime() {
return 0;
}
/**
- * Implements addditional location provider specific additional commands.
- *
- * @param command name of the command to send to the provider.
- * @param extras optional arguments for the command (or null).
- * The provider may optionally fill the extras Bundle with results from the command.
- *
- * @return true if the command succeeds.
+ * Implements location provider specific custom commands. The return value will be ignored on
+ * Android Q and above.
*/
- public boolean onSendExtraCommand(String command, Bundle extras) {
- // default implementation
+ protected boolean onSendExtraCommand(@Nullable String command, @Nullable Bundle extras) {
return false;
}
+
+ private final class Service extends ILocationProvider.Stub {
+
+ @Override
+ public void setLocationProviderManager(ILocationProviderManager manager) {
+ synchronized (mBinder) {
+ try {
+ manager.onSetProperties(mProperties);
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException e) {
+ Log.w(mTag, e);
+ }
+
+ mManager = manager;
+ }
+
+ onInit();
+ }
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource ws) {
+ onSetRequest(new ProviderRequestUnbundled(request), ws);
+ }
+
+ @Override
+ public int getStatus(Bundle extras) {
+ return onGetStatus(extras);
+ }
+
+ @Override
+ public long getStatusUpdateTime() {
+ return onGetStatusUpdateTime();
+ }
+
+ @Override
+ public void sendExtraCommand(String command, Bundle extras) {
+ onSendExtraCommand(command, extras);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ onDump(fd, pw, args);
+ }
+ }
}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6a8e618..b825b58 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,13 +16,13 @@
package com.android.location.provider;
-import java.util.ArrayList;
-import java.util.List;
-
import android.location.LocationRequest;
import com.android.internal.location.ProviderRequest;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class is an interface to Provider Requests for unbundled applications.
*
@@ -46,6 +46,10 @@
return mRequest.interval;
}
+ public boolean getForceLocation() {
+ return mRequest.forceLocation;
+ }
+
/**
* Never null.
*/
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 87d6e4a..be817d6 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -23,7 +23,6 @@
import android.location.Criteria;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -34,87 +33,53 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
false, false, false, false, true, true, true, Criteria.POWER_LOW,
Criteria.ACCURACY_FINE);
- private static final int MSG_ENABLE = 1;
- private static final int MSG_DISABLE = 2;
- private static final int MSG_SET_REQUEST = 3;
-
+ private final Context mContext;
+ private final Handler mHandler;
private final FusionEngine mEngine;
- private static class RequestWrapper {
- public ProviderRequestUnbundled request;
- public WorkSource source;
- public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) {
- this.request = request;
- this.source = source;
- }
- }
-
- public FusedLocationProvider(Context context) {
- super(TAG, PROPERTIES);
- mEngine = new FusionEngine(context, Looper.myLooper());
-
- // listen for user change
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- context.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mEngine.switchUser();
- }
- }
- }, UserHandle.ALL, intentFilter, null, mHandler);
- }
-
- /**
- * For serializing requests to mEngine.
- */
- private Handler mHandler = new Handler() {
+ private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENABLE:
- mEngine.init(FusedLocationProvider.this);
- break;
- case MSG_DISABLE:
- mEngine.deinit();
- break;
- case MSG_SET_REQUEST:
- {
- RequestWrapper wrapper = (RequestWrapper) msg.obj;
- mEngine.setRequest(wrapper.request, wrapper.source);
- break;
- }
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mEngine.switchUser();
}
}
};
- @Override
- public void onEnable() {
- mHandler.sendEmptyMessage(MSG_ENABLE);
+ FusedLocationProvider(Context context) {
+ super(TAG, PROPERTIES);
+
+ mContext = context;
+ mHandler = new Handler(Looper.myLooper());
+ mEngine = new FusionEngine(context, Looper.myLooper(), this);
}
- @Override
- public void onDisable() {
- mHandler.sendEmptyMessage(MSG_DISABLE);
+ void init() {
+ // listen for user change
+ mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+ }
+
+ void destroy() {
+ mContext.unregisterReceiver(mUserSwitchReceiver);
+ mHandler.post(() -> mEngine.setRequest(null));
}
@Override
public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
- mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)).sendToTarget();
+ mHandler.post(() -> mEngine.setRequest(request));
}
@Override
public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // perform synchronously
mEngine.dump(fd, pw, args);
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 12966cf..75bb5ec 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -21,27 +21,24 @@
import android.os.IBinder;
public class FusedLocationService extends Service {
+
private FusedLocationProvider mProvider;
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
- mProvider = new FusedLocationProvider(getApplicationContext());
+ mProvider = new FusedLocationProvider(this);
+ mProvider.init();
}
+
return mProvider.getBinder();
}
@Override
- public boolean onUnbind(Intent intent) {
- // make sure to stop performing work
- if (mProvider != null) {
- mProvider.onDisable();
- }
- return false;
- }
-
- @Override
public void onDestroy() {
- mProvider = null;
+ if (mProvider != null) {
+ mProvider.destroy();
+ mProvider = null;
+ }
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7a49524..e4610cf 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -16,14 +16,6 @@
package com.android.location.fused;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
@@ -32,9 +24,16 @@
import android.os.Looper;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.os.WorkSource;
import android.util.Log;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
public class FusionEngine implements LocationListener {
public interface Callback {
void reportLocation(Location location);
@@ -47,72 +46,35 @@
public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
- private final Context mContext;
private final LocationManager mLocationManager;
private final Looper mLooper;
+ private final Callback mCallback;
// all fields are only used on mLooper thread. except for in dump() which is not thread-safe
- private Callback mCallback;
private Location mFusedLocation;
private Location mGpsLocation;
private Location mNetworkLocation;
- private boolean mEnabled;
private ProviderRequestUnbundled mRequest;
private final HashMap<String, ProviderStats> mStats = new HashMap<>();
- public FusionEngine(Context context, Looper looper) {
- mContext = context;
+ FusionEngine(Context context, Looper looper, Callback callback) {
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
mNetworkLocation = new Location("");
mNetworkLocation.setAccuracy(Float.MAX_VALUE);
mGpsLocation = new Location("");
mGpsLocation.setAccuracy(Float.MAX_VALUE);
mLooper = looper;
+ mCallback = callback;
mStats.put(GPS, new ProviderStats());
mStats.put(NETWORK, new ProviderStats());
-
- }
-
- public void init(Callback callback) {
- Log.i(TAG, "engine started (" + mContext.getPackageName() + ")");
- mCallback = callback;
- }
-
- /**
- * Called to stop doing any work, and release all resources
- * This can happen when a better fusion engine is installed
- * in a different package, and this one is no longer needed.
- * Called on mLooper thread
- */
- public void deinit() {
- mRequest = null;
- disable();
- Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
}
/** Called on mLooper thread */
- public void enable() {
- if (!mEnabled) {
- mEnabled = true;
- updateRequirements();
- }
- }
-
- /** Called on mLooper thread */
- public void disable() {
- if (mEnabled) {
- mEnabled = false;
- updateRequirements();
- }
- }
-
- /** Called on mLooper thread */
- public void setRequest(ProviderRequestUnbundled request, WorkSource source) {
+ public void setRequest(ProviderRequestUnbundled request) {
mRequest = request;
- mEnabled = request.getReportLocation();
updateRequirements();
}
@@ -120,6 +82,7 @@
public boolean requested;
public long requestTime;
public long minTime;
+
@Override
public String toString() {
return (requested ? " REQUESTED" : " ---");
@@ -154,7 +117,7 @@
}
private void updateRequirements() {
- if (!mEnabled || mRequest == null) {
+ if (mRequest == null || !mRequest.getReportLocation()) {
mRequest = null;
disableProvider(NETWORK);
disableProvider(GPS);
@@ -200,29 +163,30 @@
* Test whether one location (a) is better to use than another (b).
*/
private static boolean isBetterThan(Location locationA, Location locationB) {
- if (locationA == null) {
- return false;
- }
- if (locationB == null) {
- return true;
- }
- // A provider is better if the reading is sufficiently newer. Heading
- // underground can cause GPS to stop reporting fixes. In this case it's
- // appropriate to revert to cell, even when its accuracy is less.
- if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
- return true;
- }
+ if (locationA == null) {
+ return false;
+ }
+ if (locationB == null) {
+ return true;
+ }
+ // A provider is better if the reading is sufficiently newer. Heading
+ // underground can cause GPS to stop reporting fixes. In this case it's
+ // appropriate to revert to cell, even when its accuracy is less.
+ if (locationA.getElapsedRealtimeNanos()
+ > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
+ return true;
+ }
- // A provider is better if it has better accuracy. Assuming both readings
- // are fresh (and by that accurate), choose the one with the smaller
- // accuracy circle.
- if (!locationA.hasAccuracy()) {
- return false;
- }
- if (!locationB.hasAccuracy()) {
- return true;
- }
- return locationA.getAccuracy() < locationB.getAccuracy();
+ // A provider is better if it has better accuracy. Assuming both readings
+ // are fresh (and by that accurate), choose the one with the smaller
+ // accuracy circle.
+ if (!locationA.hasAccuracy()) {
+ return false;
+ }
+ if (!locationB.hasAccuracy()) {
+ return true;
+ }
+ return locationA.getAccuracy() < locationB.getAccuracy();
}
private void updateFusedLocation() {
@@ -252,9 +216,9 @@
}
if (mCallback != null) {
- mCallback.reportLocation(mFusedLocation);
+ mCallback.reportLocation(mFusedLocation);
} else {
- Log.w(TAG, "Location updates received while fusion engine not started");
+ Log.w(TAG, "Location updates received while fusion engine not started");
}
}
@@ -272,19 +236,22 @@
/** Called on mLooper thread */
@Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderEnabled(String provider) { }
+ public void onProviderEnabled(String provider) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderDisabled(String provider) { }
+ public void onProviderDisabled(String provider) {
+ }
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
- s.append("mEnabled=").append(mEnabled).append(' ').append(mRequest).append('\n');
+ s.append(mRequest).append('\n');
s.append("fused=").append(mFusedLocation).append('\n');
s.append(String.format("gps %s\n", mGpsLocation));
s.append(" ").append(mStats.get(GPS)).append('\n');
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cc7bf33..db445c1 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,11 @@
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationProvider.AVAILABLE;
import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
+import static com.android.internal.util.Preconditions.checkState;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -54,7 +57,6 @@
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.location.LocationRequest;
import android.os.Binder;
import android.os.Bundle;
@@ -84,6 +86,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
@@ -94,7 +97,6 @@
import com.android.server.location.GnssNavigationMessageProvider;
import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.LocationRequestStatistics;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
@@ -131,8 +133,6 @@
// Location resolution level: fine location data
private static final int RESOLUTION_LEVEL_FINE = 2;
- private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
@@ -192,12 +192,6 @@
private IGpsGeofenceHardware mGpsGeofenceProxy;
// --- fields below are protected by mLock ---
- // Set of providers that are explicitly enabled
- // Only used by passive, fused & test. Network & GPS are controlled separately, and not listed.
- private final Set<String> mEnabledProviders = new HashSet<>();
-
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<>();
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
@@ -207,15 +201,15 @@
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
- private final ArrayList<LocationProviderInterface> mProviders =
+ private final ArrayList<LocationProvider> mProviders =
new ArrayList<>();
// real providers, saved here when mocked out
- private final HashMap<String, LocationProviderInterface> mRealProviders =
+ private final HashMap<String, LocationProvider> mRealProviders =
new HashMap<>();
// mapping from provider name to provider
- private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ private final HashMap<String, LocationProvider> mProvidersByName =
new HashMap<>();
// mapping from provider name to all its UpdateRecords
@@ -270,13 +264,8 @@
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setLocationPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- return mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationProviderPackageNames);
- }
- });
+ userId -> mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames));
if (D) Log.d(TAG, "Constructed");
@@ -321,30 +310,17 @@
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
- PackageManager.OnPermissionsChangedListener permissionListener
- = new PackageManager.OnPermissionsChangedListener() {
- @Override
- public void onPermissionsChanged(final int uid) {
- synchronized (mLock) {
- applyAllProviderRequirementsLocked();
- }
+ PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+ synchronized (mLock) {
+ applyAllProviderRequirementsLocked();
}
};
mPackageManager.addOnPermissionsChangeListener(permissionListener);
// listen for background/foreground changes
- ActivityManager.OnUidImportanceListener uidImportanceListener
- = new ActivityManager.OnUidImportanceListener() {
- @Override
- public void onUidImportance(final int uid, final int importance) {
- mLocationHandler.post(new Runnable() {
- @Override
- public void run() {
- onUidImportanceChanged(uid, importance);
- }
- });
- }
- };
+ ActivityManager.OnUidImportanceListener uidImportanceListener =
+ (uid, importance) -> mLocationHandler.post(
+ () -> onUidImportanceChanged(uid, importance));
mActivityManager.addOnUidImportanceListener(uidImportanceListener,
FOREGROUND_IMPORTANCE_CUTOFF);
@@ -356,7 +332,10 @@
// prepare providers
loadProvidersLocked();
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
// listen for settings changes
@@ -366,7 +345,7 @@
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
}
}
}, UserHandle.USER_ALL);
@@ -377,7 +356,9 @@
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -402,7 +383,9 @@
public void onChange(boolean selfChange) {
synchronized (mLock) {
updateBackgroundThrottlingWhitelistLocked();
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -414,7 +397,6 @@
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- intentFilter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
@@ -425,12 +407,6 @@
} else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
updateUserProfiles(mCurrentUserId);
- } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
- // shutdown only if UserId indicates whole system, not just one user
- if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
- if (getSendingUserId() == UserHandle.USER_ALL) {
- shutdownComponents();
- }
}
}
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
@@ -502,30 +478,13 @@
}
/**
- * Provides a way for components held by the {@link LocationManagerService} to clean-up
- * gracefully on system's shutdown.
- *
- * NOTES:
- * 1) Only provides a chance to clean-up on an opt-in basis. This guarantees back-compat
- * support for components that do not wish to handle such event.
- */
- private void shutdownComponents() {
- if (D) Log.d(TAG, "Shutting down components...");
-
- LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
- if (gpsProvider != null && gpsProvider.isEnabled()) {
- gpsProvider.disable();
- }
- }
-
- /**
* Makes a list of userids that are related to the current user. This is
* relevant when using managed profiles. Otherwise the list only contains
* the current user.
*
* @param currentUserId the current user, who might have an alter-ego.
*/
- void updateUserProfiles(int currentUserId) {
+ private void updateUserProfiles(int currentUserId) {
int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
synchronized (mLock) {
mCurrentUserProfiles = profileIds;
@@ -614,22 +573,28 @@
private void loadProvidersLocked() {
// create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProviderLocked(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
+ LocationProvider passiveProviderManager = new LocationProvider(
+ LocationManager.PASSIVE_PROVIDER);
+ PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
+
+ addProviderLocked(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
// Create a gps location provider
- GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
+ LocationProvider gnssProviderManager = new LocationProvider(
+ LocationManager.GPS_PROVIDER);
+ GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
+ gnssProviderManager,
mLocationHandler.getLooper());
+
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProviderLocked(gnssProvider);
- mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+ addProviderLocked(gnssProviderManager);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -657,34 +622,38 @@
ensureFallbackFusedProviderPresentLocked(pkgs);
// bind to network provider
+
+ LocationProvider networkProviderManager = new LocationProvider(
+ LocationManager.NETWORK_PROVIDER);
LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.NETWORK_PROVIDER,
+ networkProviderManager,
NETWORK_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (networkProvider != null) {
- mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProviderLocked(networkProvider);
+ addProviderLocked(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
// bind to fused provider
- LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ LocationProvider fusedProviderManager = new LocationProvider(
+ LocationManager.FUSED_PROVIDER);
+ LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.FUSED_PROVIDER,
+ fusedProviderManager,
FUSED_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
- if (fusedLocationProvider != null) {
- addProviderLocked(fusedLocationProvider);
- mProxyProviders.add(fusedLocationProvider);
- mEnabledProviders.add(fusedLocationProvider.getName());
- mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
+ if (fusedProvider != null) {
+ addProviderLocked(fusedProviderManager);
+ mProxyProviders.add(fusedProvider);
+ mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
Slog.e(TAG, "no fused location provider found",
new IllegalStateException("Location service needs a fused location provider"));
@@ -765,12 +734,9 @@
synchronized (mLock) {
mLastLocation.clear();
mLastLocationCoarseInterval.clear();
- for (LocationProviderInterface p : mProviders) {
- updateProviderListenersLocked(p.getName(), false);
- }
- mCurrentUserId = userId;
updateUserProfiles(userId);
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ mCurrentUserId = userId;
}
}
@@ -786,6 +752,165 @@
}
}
+ private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+
+ private final String mName;
+ private AbstractLocationProvider mProvider;
+
+ // whether the provider is enabled in location settings
+ private boolean mSettingsEnabled;
+
+ // whether the provider considers itself enabled
+ private volatile boolean mEnabled;
+
+ @Nullable
+ private volatile ProviderProperties mProperties;
+
+ private LocationProvider(String name) {
+ mName = name;
+ // TODO: initialize settings enabled?
+ }
+
+ @Override
+ public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
+ checkState(mProvider == null);
+
+ // the provider is not yet fully constructed at this point, so we may not do anything
+ // except save a reference for later use here. do not call any provider methods.
+ mProvider = provider;
+ mEnabled = initiallyEnabled;
+ mProperties = null;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isEnabled() {
+ return mSettingsEnabled && mEnabled;
+ }
+
+ @Nullable
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ public void setRequest(ProviderRequest request, WorkSource workSource) {
+ mProvider.setRequest(request, workSource);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(mName + " provider:");
+ pw.println(" setting=" + mSettingsEnabled);
+ pw.println(" enabled=" + mEnabled);
+ pw.println(" properties=" + mProperties);
+ mProvider.dump(fd, pw, args);
+ }
+
+ public long getStatusUpdateTime() {
+ return mProvider.getStatusUpdateTime();
+ }
+
+ public int getStatus(Bundle extras) {
+ return mProvider.getStatus(extras);
+ }
+
+ public void sendExtraCommand(String command, Bundle extras) {
+ mProvider.sendExtraCommand(command, extras);
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(Location location) {
+ runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+ mProvider == mPassiveProvider));
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(List<Location> locations) {
+ runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+ }
+
+ // called from any thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ runOnHandler(() -> {
+ if (enabled == mEnabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+
+ if (!mSettingsEnabled) {
+ // this provider was disabled in settings anyways, so a change to it's own
+ // enabled status won't have any affect.
+ return;
+ }
+
+ // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
+ // setting to detect when providers are enabled or disabled (even though they aren't
+ // supposed to). to continue to support this we must force a change to this setting.
+ // we use the fused provider because this is forced to be always enabled in settings
+ // anyways, and so won't have any visible effect beyond triggering content observers
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+
+ synchronized (mLock) {
+ if (!enabled) {
+ // If any provider has been disabled, clear all last locations for all
+ // providers. This is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ }
+
+ updateProviderListenersLocked(mName);
+ }
+
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+ UserHandle.ALL);
+ });
+ }
+
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ runOnHandler(() -> mProperties = properties);
+ }
+
+ private void setSettingsEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (mSettingsEnabled == enabled) {
+ return;
+ }
+
+ mSettingsEnabled = enabled;
+ if (!mSettingsEnabled) {
+ // if any provider has been disabled, clear all last locations for all
+ // providers. this is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateProviderListenersLocked(mName);
+ } else if (mEnabled) {
+ updateProviderListenersLocked(mName);
+ }
+ }
+ }
+
+ private void runOnHandler(Runnable runnable) {
+ if (Looper.myLooper() == mLocationHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mLocationHandler.post(runnable);
+ }
+ }
+ }
+
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
@@ -793,24 +918,24 @@
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
final Identity mIdentity;
- final int mAllowedResolutionLevel; // resolution level allowed to receiver
+ private final int mAllowedResolutionLevel; // resolution level allowed to receiver
- final ILocationListener mListener;
+ private final ILocationListener mListener;
final PendingIntent mPendingIntent;
final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
- final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
- final Object mKey;
+ private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
+ private final Object mKey;
final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
// True if app ops has started monitoring this receiver for locations.
- boolean mOpMonitoring;
+ private boolean mOpMonitoring;
// True if app ops has started monitoring this receiver for high power (gps) locations.
- boolean mOpHighPowerMonitoring;
- int mPendingBroadcasts;
+ private boolean mOpHighPowerMonitoring;
+ private int mPendingBroadcasts;
PowerManager.WakeLock mWakeLock;
- Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
String packageName, WorkSource workSource, boolean hideFromAppOps) {
mListener = listener;
mPendingIntent = intent;
@@ -885,9 +1010,10 @@
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
+ if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+ mCurrentUserId)) {
requestingLocation = true;
- LocationProviderInterface locationProvider
+ LocationManagerService.LocationProvider locationProvider
= mProvidersByName.get(updateRecord.mProvider);
ProviderProperties properties = locationProvider != null
? locationProvider.getProperties() : null;
@@ -1034,7 +1160,7 @@
return true;
}
- public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+ private boolean callProviderEnabledLocked(String provider, boolean enabled) {
// First update AppOp monitoring.
// An app may get/lose location access as providers are enabled/disabled.
updateMonitoring(true);
@@ -1236,7 +1362,7 @@
private class LinkedCallback implements IBinder.DeathRecipient {
private final IBatchedLocationCallback mCallback;
- public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+ private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
mCallback = callback;
}
@@ -1337,7 +1463,7 @@
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+ if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1352,29 +1478,17 @@
}
}
- private void addProviderLocked(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProvider provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProviderLocked(LocationProviderInterface provider) {
- provider.disable();
+ private void removeProviderLocked(LocationProvider provider) {
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
/**
- * Returns "true" if access to the specified location provider is allowed by the current
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- */
- private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
- return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
- }
-
- /**
* Returns "true" if access to the specified location provider is allowed by the specified
* user's settings. Access to all location providers is forbidden to non-location-provider
* processes belonging to background users.
@@ -1383,13 +1497,28 @@
* @param userId the user id to query
*/
private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
- if (mEnabledProviders.contains(provider)) {
- return true;
+ if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
}
- if (mDisabledProviders.contains(provider)) {
- return false;
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
}
- return isLocationProviderEnabledForUser(provider, userId);
+ synchronized (mLock) {
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // Use system settings
+ ContentResolver cr = mContext.getContentResolver();
+ String allowedProviders = Settings.Secure.getStringForUser(
+ cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@@ -1481,9 +1610,11 @@
// network and fused providers are ok with COARSE or FINE
return RESOLUTION_LEVEL_COARSE;
} else {
- // mock providers
- LocationProviderInterface lp = mMockProviders.get(provider);
- if (lp != null) {
+ for (LocationProvider lp : mProviders) {
+ if (!lp.getName().equals(provider)) {
+ continue;
+ }
+
ProviderProperties properties = lp.getProperties();
if (properties != null) {
if (properties.mRequiresSatellite) {
@@ -1496,6 +1627,7 @@
}
}
}
+
return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
@@ -1550,7 +1682,7 @@
}
private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
- switch(allowedResolutionLevel) {
+ switch (allowedResolutionLevel) {
case RESOLUTION_LEVEL_COARSE:
return AppOpsManager.OPSTR_COARSE_LOCATION;
case RESOLUTION_LEVEL_FINE:
@@ -1565,7 +1697,7 @@
}
}
- boolean reportLocationAccessNoThrow(
+ private boolean reportLocationAccessNoThrow(
int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
@@ -1577,7 +1709,8 @@
return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
}
- boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
+ private boolean checkLocationAccess(int pid, int uid, String packageName,
+ int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -1597,7 +1730,7 @@
ArrayList<String> out;
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1623,7 +1756,7 @@
try {
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1633,7 +1766,8 @@
&& !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
continue;
}
- if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ if (criteria != null
+ && !android.location.LocationProvider.propertiesMeetCriteria(
name, provider.getProperties(), criteria)) {
continue;
}
@@ -1658,7 +1792,7 @@
*/
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- String result = null;
+ String result;
List<String> providers = getProviders(criteria, enabledOnly);
if (!providers.isEmpty()) {
@@ -1673,7 +1807,7 @@
return result;
}
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
return null;
}
@@ -1689,51 +1823,32 @@
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- boolean result = LocationProvider.propertiesMeetCriteria(
+ boolean result = android.location.LocationProvider.propertiesMeetCriteria(
p.getName(), p.getProperties(), criteria);
if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
return result;
}
- private void updateProvidersLocked() {
- boolean changesMade = false;
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- boolean isEnabled = p.isEnabled();
- String name = p.getName();
- boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
- if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false);
- // If any provider has been disabled, clear all last locations for all providers.
- // This is to be on the safe side in case a provider has location derived from
- // this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- changesMade = true;
- } else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true);
- changesMade = true;
- }
+ private void updateProvidersSettingsLocked() {
+ for (LocationProvider p : mProviders) {
+ p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
}
- if (changesMade) {
- mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
- UserHandle.ALL);
- mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
- UserHandle.ALL);
- }
+
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
+ UserHandle.ALL);
}
- private void updateProviderListenersLocked(String provider, boolean enabled) {
- int listeners = 0;
-
- LocationProviderInterface p = mProvidersByName.get(provider);
+ private void updateProviderListenersLocked(String provider) {
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
+ boolean enabled = p.isEnabled();
+
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1747,7 +1862,6 @@
}
deadReceivers.add(record.mReceiver);
}
- listeners++;
}
}
}
@@ -1758,16 +1872,11 @@
}
}
- if (enabled) {
- p.enable();
- applyRequirementsLocked(provider);
- } else {
- p.disable();
- }
+ applyRequirementsLocked(provider);
}
private void applyRequirementsLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1779,10 +1888,10 @@
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
- // initialize the low power mode to true and set to false if any of the records requires
- providerRequest.lowPowerMode = true;
- if (records != null) {
+ if (p.isEnabled() && records != null && !records.isEmpty()) {
+ // initialize the low power mode to true and set to false if any of the records requires
+ providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
@@ -1875,7 +1984,7 @@
public String[] getBackgroundThrottlingWhitelist() {
synchronized (mLock) {
return mBackgroundThrottlePackageWhitelist.toArray(
- new String[mBackgroundThrottlePackageWhitelist.size()]);
+ new String[0]);
}
}
@@ -1922,17 +2031,17 @@
private class UpdateRecord {
final String mProvider;
- final LocationRequest mRealRequest; // original request from client
+ private final LocationRequest mRealRequest; // original request from client
LocationRequest mRequest; // possibly throttled version of the request
- final Receiver mReceiver;
- boolean mIsForegroundUid;
- Location mLastFixBroadcast;
- long mLastStatusBroadcast;
+ private final Receiver mReceiver;
+ private boolean mIsForegroundUid;
+ private Location mLastFixBroadcast;
+ private long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
mRealRequest = request;
mRequest = request;
@@ -1958,7 +2067,7 @@
/**
* Method to be called when record changes foreground/background
*/
- void updateForeground(boolean isForeground){
+ private void updateForeground(boolean isForeground) {
mIsForegroundUid = isForeground;
mRequestStatistics.updateForeground(
mReceiver.mIdentity.mPackageName, mProvider, isForeground);
@@ -1967,7 +2076,7 @@
/**
* Method to be called when a record will no longer be used.
*/
- void disposeLocked(boolean removeReceiver) {
+ private void disposeLocked(boolean removeReceiver) {
mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
// remove from mRecordsByProvider
@@ -1980,13 +2089,11 @@
// remove from Receiver#mUpdateRecords
HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
- if (receiverRecords != null) {
- receiverRecords.remove(this.mProvider);
+ receiverRecords.remove(this.mProvider);
- // and also remove the Receiver if it has no more update records
- if (receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
- }
+ // and also remove the Receiver if it has no more update records
+ if (receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
}
}
@@ -2070,7 +2177,7 @@
private void checkPackageName(String packageName) {
if (packageName == null) {
- throw new SecurityException("invalid package name: " + packageName);
+ throw new SecurityException("invalid package name: " + null);
}
int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
@@ -2085,7 +2192,7 @@
private void checkPendingIntent(PendingIntent intent) {
if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + intent);
+ throw new IllegalArgumentException("invalid pending intent: " + null);
}
}
@@ -2137,7 +2244,7 @@
synchronized (mLock) {
Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2145,7 +2252,7 @@
}
private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
- int pid, int uid, String packageName) {
+ int uid, String packageName) {
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2154,7 +2261,7 @@
throw new IllegalArgumentException("provider name must not be null");
}
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) {
throw new IllegalArgumentException("provider doesn't exist: " + name);
}
@@ -2173,8 +2280,7 @@
oldRecord.disposeLocked(false);
}
- boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
- if (isProviderEnabled) {
+ if (provider.isEnabled()) {
applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
@@ -2194,10 +2300,8 @@
final int uid = Binder.getCallingUid();
synchronized (mLock) {
- WorkSource workSource = null;
- boolean hideFromAppOps = false;
Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
+ packageName, null, false);
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
@@ -2236,22 +2340,12 @@
// update provider
for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
- continue;
- }
-
applyRequirementsLocked(provider);
}
}
private void applyAllProviderRequirementsLocked() {
- for (LocationProviderInterface p : mProviders) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
- continue;
- }
-
+ for (LocationProvider p : mProviders) {
applyRequirementsLocked(p.getName());
}
}
@@ -2291,7 +2385,7 @@
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) return null;
if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
@@ -2314,7 +2408,7 @@
location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
if ((locationAgeMs > mLastLocationMaxAgeMs)
&& (mAppOps.unsafeCheckOp(op, uid, packageName)
- == AppOpsManager.MODE_FOREGROUND)) {
+ == AppOpsManager.MODE_FOREGROUND)) {
return null;
}
@@ -2358,7 +2452,7 @@
}
return false;
}
- LocationProviderInterface p = null;
+ LocationProvider p = null;
String provider = location.getProvider();
if (provider != null) {
p = mProvidersByName.get(provider);
@@ -2370,7 +2464,7 @@
return false;
}
synchronized (mLock) {
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
+ if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
if (D) {
Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
}
@@ -2499,11 +2593,13 @@
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
- if (mGnssMeasurementsProvider != null) {
- synchronized (mLock) {
- mGnssMeasurementsListeners.remove(listener.asBinder());
- mGnssMeasurementsProvider.removeListener(listener);
- }
+ if (mGnssMeasurementsProvider == null) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mGnssMeasurementsListeners.remove(listener.asBinder());
+ mGnssMeasurementsProvider.removeListener(listener);
}
}
@@ -2560,10 +2656,11 @@
}
synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
- return p.sendExtraCommand(command, extras);
+ p.sendExtraCommand(command, extras);
+ return true;
}
}
@@ -2588,14 +2685,10 @@
*/
@Override
public ProviderProperties getProviderProperties(String provider) {
- if (mProvidersByName.get(provider) == null) {
- return null;
- }
-
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
p = mProvidersByName.get(provider);
}
@@ -2611,25 +2704,25 @@
*/
@Override
public String getNetworkProviderPackage() {
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
- if (mProvidersByName.get(LocationManager.NETWORK_PROVIDER) == null) {
- return null;
- }
p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
}
- if (p instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p).getConnectedPackageName();
+ if (p == null) {
+ return null;
+ }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
}
return null;
}
/**
- * Returns the current location enabled/disabled status for a user
+ * Returns the current location enabled/disabled status for a user
*
- * @param userId the id of the user
- * @return true if location is enabled
+ * @param userId the id of the user
+ * @return true if location is enabled
*/
@Override
public boolean isLocationEnabledForUser(int userId) {
@@ -2647,7 +2740,7 @@
return false;
}
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
- for(String provider : mRealProviders.keySet()) {
+ for (String provider : mRealProviders.keySet()) {
if (provider.equals(LocationManager.PASSIVE_PROVIDER)
|| provider.equals(LocationManager.FUSED_PROVIDER)) {
continue;
@@ -2664,16 +2757,16 @@
}
/**
- * Enable or disable location for a user
+ * Enable or disable location for a user
*
- * @param enabled true to enable location, false to disable location
- * @param userId the id of the user
+ * @param enabled true to enable location, false to disable location
+ * @param userId the id of the user
*/
@Override
public void setLocationEnabledForUser(boolean enabled, int userId) {
mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
+ android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Requires WRITE_SECURE_SETTINGS permission");
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
@@ -2688,7 +2781,7 @@
allProvidersSet.addAll(allRealProviders);
// When disabling location, disable gps and network provider that could have been
// enabled by location mode api.
- if (enabled == false) {
+ if (!enabled) {
allProvidersSet.add(LocationManager.GPS_PROVIDER);
allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
}
@@ -2723,26 +2816,34 @@
}
/**
- * Returns the current enabled/disabled status of a location provider and user
+ * Returns the current enabled/disabled status of a location provider and user
*
- * @param provider name of the provider
- * @param userId the id of the user
- * @return true if the provider exists and is enabled
+ * @param providerName name of the provider
+ * @param userId the id of the user
+ * @return true if the provider exists and is enabled
*/
@Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
+ public boolean isProviderEnabledForUser(String providerName, int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
+ }
+
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+ if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
- int uid = Binder.getCallingUid();
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLocked(provider, uid, userId);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ LocationProvider provider;
+ synchronized (mLock) {
+ provider = mProvidersByName.get(providerName);
+ }
+ return provider != null && provider.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -2750,66 +2851,13 @@
* Enable or disable a single location provider.
*
* @param provider name of the provider
- * @param enabled true to enable the provider. False to disable the provider
- * @param userId the id of the user to set
+ * @param enabled true to enable the provider. False to disable the provider
+ * @param userId the id of the user to set
* @return true if the value was set, false on errors
*/
@Override
public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // No such provider exists
- if (!mProvidersByName.containsKey(provider)) return false;
-
- // If it is a test provider, do not write to Settings.Secure
- if (mMockProviders.containsKey(provider)) {
- setTestProviderEnabled(provider, enabled);
- return true;
- }
-
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- String providerChange = (enabled ? "+" : "-") + provider;
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- providerChange, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Read location provider status from Settings.Secure
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- private boolean isLocationProviderEnabledForUser(String provider, int userId) {
- long identity = Binder.clearCallingIdentity();
- try {
- // Use system settings
- ContentResolver cr = mContext.getContentResolver();
- String allowedProviders = Settings.Secure.getStringForUser(
- cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
- return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return false;
}
/**
@@ -2941,7 +2989,7 @@
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
@@ -3053,7 +3101,7 @@
LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
long prevStatusUpdateTime = r.mLastStatusBroadcast;
if ((newStatusUpdateTime > prevStatusUpdateTime)
- && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
@@ -3098,8 +3146,8 @@
/**
* Updates last location with the given location
*
- * @param location new location to update
- * @param provider Location provider to update for
+ * @param location new location to update
+ * @param provider Location provider to update for
*/
private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
@@ -3154,13 +3202,11 @@
}
synchronized (mLock) {
- if (isAllowedByCurrentUserSettingsLocked(provider)) {
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
- handleLocationChangedLocked(myLocation, passive);
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(myLocation);
}
+ handleLocationChangedLocked(myLocation, passive);
}
}
@@ -3245,13 +3291,12 @@
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
|| LocationManager.FUSED_PROVIDER.equals(name)) {
- LocationProviderInterface p = mProvidersByName.get(name);
+ LocationProvider p = mProvidersByName.get(name);
if (p != null) {
removeProviderLocked(p);
}
}
addTestProviderLocked(name, properties);
- updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
}
@@ -3260,9 +3305,12 @@
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- MockProvider provider = new MockProvider(name, this, properties);
+
+ LocationProvider provider = new LocationProvider(name);
+ MockProvider mockProvider = new MockProvider(provider, properties);
+
addProviderLocked(provider);
- mMockProviders.put(name, provider);
+ mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@@ -3274,28 +3322,25 @@
}
synchronized (mLock) {
-
- // These methods can't be called after removing the test provider, so first make sure
- // we don't leave anything dangling.
- clearTestProviderEnabled(provider, opPackageName);
- clearTestProviderLocation(provider, opPackageName);
-
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- long identity = Binder.clearCallingIdentity();
- removeProviderLocked(mProvidersByName.get(provider));
- // reinstate real provider if available
- LocationProviderInterface realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProviderLocked(realProvider);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeProviderLocked(mProvidersByName.get(provider));
+
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
}
}
@@ -3325,23 +3370,11 @@
// clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
long identity = Binder.clearCallingIdentity();
- mockProvider.setLocation(mock);
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderLocation(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+ try {
+ mockProvider.setLocation(mock);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mockProvider.clearLocation();
}
}
@@ -3350,36 +3383,6 @@
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- setTestProviderEnabled(provider, enabled);
- }
-
- /** Enable or disable a test location provider. */
- private void setTestProviderEnabled(String provider, boolean enabled) {
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- long identity = Binder.clearCallingIdentity();
- if (enabled) {
- mockProvider.enable();
- mEnabledProviders.add(provider);
- mDisabledProviders.remove(provider);
- } else {
- mockProvider.disable();
- mEnabledProviders.remove(provider);
- mDisabledProviders.add(provider);
- }
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderEnabled(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
@@ -3387,10 +3390,11 @@
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- mEnabledProviders.remove(provider);
- mDisabledProviders.remove(provider);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
+ try {
+ mockProvider.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -3450,10 +3454,11 @@
+ identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
}
pw.println(" Overlay Provider Packages:");
- for (LocationProviderInterface provider : mProviders) {
- if (provider instanceof LocationProviderProxy) {
+ for (LocationProvider provider : mProviders) {
+ if (provider.mProvider instanceof LocationProviderProxy) {
pw.println(" " + provider.getName() + ": "
- + ((LocationProviderProxy) provider).getConnectedPackageName());
+ + ((LocationProviderProxy) provider.mProvider)
+ .getConnectedPackageName());
}
}
pw.println(" Historical Records by Provider:");
@@ -3479,25 +3484,12 @@
mGeofenceManager.dump(pw);
- if (mEnabledProviders.size() > 0) {
- pw.println(" Enabled Providers:");
- for (String i : mEnabledProviders) {
- pw.println(" " + i);
- }
-
- }
- if (mDisabledProviders.size() > 0) {
- pw.println(" Disabled Providers:");
- for (String i : mDisabledProviders) {
- pw.println(" " + i);
- }
- }
pw.append(" ");
mBlacklist.dump(pw);
if (mMockProviders.size() > 0) {
pw.println(" Mock Providers:");
for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
- i.getValue().dump(pw, " ");
+ i.getValue().dump(fd, pw, args);
}
}
@@ -3514,13 +3506,7 @@
if (args.length > 0 && "short".equals(args[0])) {
return;
}
- for (LocationProviderInterface provider : mProviders) {
- pw.print(provider.getName() + " Internal State");
- if (provider instanceof LocationProviderProxy) {
- LocationProviderProxy proxy = (LocationProviderProxy) provider;
- pw.print(" (" + proxy.getConnectedPackageName() + ")");
- }
- pw.println(":");
+ for (LocationProvider provider : mProviders) {
provider.dump(fd, pw, args);
}
if (mGnssBatchingInProgress) {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 574f54a..d09823e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,9 +16,7 @@
package com.android.server;
-import android.annotation.MainThread;
import android.annotation.Nullable;
-import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -168,13 +166,13 @@
// called on handler thread
@GuardedBy("mBindLock")
protected void onBind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
// called on handler thread
@GuardedBy("mBindLock")
protected void onUnbind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
/**
@@ -205,12 +203,12 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
return super.onPackageChanged(packageName, uid, components);
}
}.register(mContext, UserHandle.ALL, true, mHandler);
@@ -243,20 +241,16 @@
return true;
}
- /** Returns thje name of the currently connected package or null. */
+ /** Returns the name of the currently connected package or null. */
@Nullable
public String getCurrentPackageName() {
ComponentName bestComponent = mBestComponent;
return bestComponent == null ? null : bestComponent.getPackageName();
}
- public int getCurrentPackageVersion() {
- return mBestVersion;
- }
-
/**
- * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
- * otherwise. All invocations to runOnBinder are run serially.
+ * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
+ * serially.
*/
public final void runOnBinder(BinderRunner runner) {
synchronized (mBindLock) {
@@ -267,7 +261,7 @@
} catch (Exception e) {
// remote exceptions cannot be allowed to crash system server
Log.e(TAG, "exception while while running " + runner + " on " + service
- + " from " + mBestComponent.toShortString(), e);
+ + " from " + this, e);
}
}
}
@@ -280,14 +274,6 @@
UserHandle.USER_SYSTEM).isEmpty();
}
- /**
- * Searches and binds to the best package, or do nothing if the best package
- * is already bound, unless force rebinding is requested.
- *
- * @param forceRebind Force a rebinding to the best package if it's already
- * bound.
- */
- @WorkerThread
private void bindBestPackage(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -365,7 +351,6 @@
}
}
- @WorkerThread
private void bind(ComponentName component, int version, int userId) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -382,7 +367,6 @@
UserHandle.of(userId));
}
- @WorkerThread
private void unbind() {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -396,7 +380,6 @@
mBestUserId = UserHandle.USER_NULL;
}
- @MainThread
@Override
public final void onServiceConnected(ComponentName component, IBinder binder) {
mHandler.post(() -> {
@@ -410,7 +393,6 @@
});
}
- @MainThread
@Override
public final void onServiceDisconnected(ComponentName component) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
new file mode 100644
index 0000000..4c7c420
--- /dev/null
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * @hide
+ */
+public abstract class AbstractLocationProvider {
+
+ /**
+ * Interface for communicating from a location provider back to the location service.
+ */
+ public interface LocationProviderManager {
+
+ /**
+ * Called on location provider construction to make the location service aware of this
+ * provider and what it's initial enabled/disabled state should be.
+ */
+ void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * enabled/disabled state.
+ */
+ void onSetEnabled(boolean enabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * properties.
+ */
+ void onSetProperties(ProviderProperties properties);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(Location location);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(List<Location> locations);
+ }
+
+ private final LocationProviderManager mLocationProviderManager;
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
+ this(locationProviderManager, true);
+ }
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
+ boolean initiallyEnabled) {
+ mLocationProviderManager = locationProviderManager;
+ mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(Location location) {
+ mLocationProviderManager.onReportLocation(location);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(List<Location> locations) {
+ mLocationProviderManager.onReportLocation(locations);
+ }
+
+ /**
+ * Call this method to report a change in provider enabled/disabled status. May be called from
+ * any thread.
+ */
+ protected void setEnabled(boolean enabled) {
+ mLocationProviderManager.onSetEnabled(enabled);
+ }
+
+ /**
+ * Call this method to report a change in provider properties. May be called from
+ * any thread.
+ */
+ protected void setProperties(ProviderProperties properties) {
+ mLocationProviderManager.onSetProperties(properties);
+ }
+
+ /**
+ * Called when the location service delivers a new request for fulfillment to the provider.
+ * Replaces any previous requests completely.
+ */
+ public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+ /**
+ * Called to dump debug or log information.
+ */
+ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /**
+ * Retrieves the current status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public int getStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
+
+ /**
+ * Retrieves the last update time of the status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /** Sends a custom command to this provider. */
+ public abstract void sendExtraCommand(String command, Bundle extras);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 330d1d5..9e2a94e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -34,7 +34,6 @@
import android.location.IGnssStatusListener;
import android.location.IGnssStatusProvider;
import android.location.IGpsGeofenceHardware;
-import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationListener;
@@ -84,6 +83,10 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -97,8 +100,18 @@
*
* {@hide}
*/
-public class GnssLocationProvider extends LocationProviderInterface
- implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends AbstractLocationProvider implements
+ InjectNtpTimeCallback,
+ GnssSatelliteBlacklistCallback {
+
+ /**
+ * Indicates that this method is a native entry point. Useful purely for IDEs which can
+ * understand entry points, and thus eliminate incorrect warnings about methods not used.
+ */
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface NativeEntryPoint {
+ }
private static final String TAG = "GnssLocationProvider";
@@ -249,7 +262,7 @@
}
public void set(int svCount, int meanCn0, int maxCn0) {
- synchronized(this) {
+ synchronized (this) {
mSvCount = svCount;
mMeanCn0 = meanCn0;
mMaxCn0 = maxCn0;
@@ -258,7 +271,7 @@
}
public void reset() {
- set(0,0,0);
+ set(0, 0, 0);
}
// Also used by outside methods to add to other bundles
@@ -314,7 +327,7 @@
MAX_RETRY_INTERVAL);
// true if we are enabled, protected by this
- private boolean mEnabled;
+ private boolean mEnabled = true;
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
@@ -328,9 +341,6 @@
// true if GPS is navigating
private boolean mNavigating;
- // true if GPS engine is on
- private boolean mEngineOn;
-
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
@@ -380,7 +390,6 @@
private boolean mSuplEsEnabled = false;
private final Context mContext;
- private final ILocationManager mILocationManager;
private final LocationExtras mLocationExtras = new LocationExtras();
private final GnssStatusListenerHelper mListenerHelper;
private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
@@ -479,17 +488,22 @@
return;
}
- if (action.equals(ALARM_WAKEUP)) {
- startNavigating(false);
- } else if (action.equals(ALARM_TIMEOUT)) {
- hibernate();
- } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
- || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)
- || Intent.ACTION_SCREEN_ON.equals(action)) {
- updateLowPowerMode();
- } else if (action.equals(SIM_STATE_CHANGED)) {
- subscriptionOrSimChanged(context);
+ switch (action) {
+ case ALARM_WAKEUP:
+ startNavigating(false);
+ break;
+ case ALARM_TIMEOUT:
+ hibernate();
+ break;
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+ case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ case Intent.ACTION_SCREEN_OFF:
+ case Intent.ACTION_SCREEN_ON:
+ updateLowPowerMode();
+ break;
+ case SIM_STATE_CHANGED:
+ subscriptionOrSimChanged(context);
+ break;
}
}
};
@@ -507,9 +521,7 @@
*/
@Override
public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
- mHandler.post(()->{
- native_set_satellite_blacklist(constellations, svids);
- });
+ mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
}
private void subscriptionOrSimChanged(Context context) {
@@ -572,7 +584,7 @@
}
interface SetCarrierProperty {
- public boolean set(int value);
+ boolean set(int value);
}
private void reloadGpsProperties(Context context, Properties properties) {
@@ -587,7 +599,7 @@
/*
* Overlay carrier properties from a debug configuration file.
*/
- loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
+ loadPropertiesFromFile(properties);
// TODO: we should get rid of C2K specific setting.
setSuplHostPort(properties.getProperty("SUPL_HOST"),
properties.getProperty("SUPL_PORT"));
@@ -603,15 +615,15 @@
if (native_is_gnss_configuration_supported()) {
Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
{
- put("SUPL_VER", (val) -> native_set_supl_version(val));
- put("SUPL_MODE", (val) -> native_set_supl_mode(val));
- put("SUPL_ES", (val) -> native_set_supl_es(val));
- put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+ put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
+ put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
+ put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
+ put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
put("A_GLONASS_POS_PROTOCOL_SELECT",
- (val) -> native_set_gnss_pos_protocol_select(val));
+ GnssLocationProvider::native_set_gnss_pos_protocol_select);
put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
- (val) -> native_set_emergency_supl_pdn(val));
- put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+ GnssLocationProvider::native_set_emergency_supl_pdn);
+ put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
}
};
@@ -622,7 +634,7 @@
try {
int propertyValueInt = Integer.decode(propertyValueString);
boolean result = entry.getValue().set(propertyValueInt);
- if (result == false) {
+ if (!result) {
Log.e(TAG, "Unable to set " + propertyName);
}
} catch (NumberFormatException e) {
@@ -664,10 +676,9 @@
}
}
- private boolean loadPropertiesFromFile(String filename,
- Properties properties) {
+ private void loadPropertiesFromFile(Properties properties) {
try {
- File file = new File(filename);
+ File file = new File(DEBUG_PROPERTIES_FILE);
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
@@ -677,16 +688,15 @@
}
} catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
- return false;
+ if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
}
- return true;
}
- public GnssLocationProvider(Context context, ILocationManager ilocationManager,
+ public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
Looper looper) {
+ super(locationProviderManager, true);
+
mContext = context;
- mILocationManager = ilocationManager;
// Create a wake lock
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -763,19 +773,20 @@
mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
mGnssBatchingProvider = new GnssBatchingProvider();
mGnssGeofenceProvider = new GnssGeofenceProvider();
- }
- /**
- * Returns the name of this provider.
- */
- @Override
- public String getName() {
- return LocationManager.GPS_PROVIDER;
- }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getSendingUserId() == UserHandle.USER_ALL) {
+ mEnabled = false;
+ handleDisable();
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, mHandler);
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
+ setProperties(PROPERTIES);
}
/**
@@ -840,9 +851,9 @@
locationManager.requestLocationUpdates(provider,
LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
locationListener, mHandler.getLooper());
- locationListener.numLocationUpdateRequest++;
+ locationListener.mNumLocationUpdateRequest++;
mHandler.postDelayed(() -> {
- if (--locationListener.numLocationUpdateRequest == 0) {
+ if (--locationListener.mNumLocationUpdateRequest == 0) {
Log.i(TAG,
String.format("Removing location updates from %s provider.", provider));
locationManager.removeUpdates(locationListener);
@@ -905,43 +916,40 @@
// hold wake lock while task runs
mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
- AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
- @Override
- public void run() {
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
- byte[] data = xtraDownloader.downloadXtraData();
- if (data != null) {
- if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
- native_inject_xtra_data(data, data.length);
- mXtraBackOff.reset();
- }
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
+ native_inject_xtra_data(data, data.length);
+ mXtraBackOff.reset();
+ }
- sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
+ sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
- if (data == null) {
- // try again later
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
- mXtraBackOff.nextBackoffMillis());
- }
+ if (data == null) {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+ mXtraBackOff.nextBackoffMillis());
+ }
- // Release wake lock held by task, synchronize on mLock in case multiple
- // download tasks overrun.
- synchronized (mLock) {
- if (mDownloadXtraWakeLock.isHeld()) {
- // This wakelock may have time-out, if a timeout was specified.
- // Catch (and ignore) any timeout exceptions.
- try {
- mDownloadXtraWakeLock.release();
- if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
- } catch (Exception e) {
- Log.i(TAG, "Wakelock timeout & release race exception in "
- + "handleDownloadXtraData()", e);
- }
- } else {
- Log.e(TAG, "WakeLock expired before release in "
- + "handleDownloadXtraData()");
+ // Release wake lock held by task, synchronize on mLock in case multiple
+ // download tasks overrun.
+ synchronized (mLock) {
+ if (mDownloadXtraWakeLock.isHeld()) {
+ // This wakelock may have time-out, if a timeout was specified.
+ // Catch (and ignore) any timeout exceptions.
+ try {
+ mDownloadXtraWakeLock.release();
+ if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+ } catch (Exception e) {
+ Log.i(TAG, "Wakelock timeout & release race exception in "
+ + "handleDownloadXtraData()", e);
}
+ } else {
+ Log.e(TAG, "WakeLock expired before release in "
+ + "handleDownloadXtraData()");
}
}
});
@@ -954,21 +962,6 @@
}
}
- /**
- * Enables this provider. When enabled, calls to getStatus()
- * must be handled. Hardware may be started up
- * when the provider is enabled.
- */
- @Override
- public void enable() {
- synchronized (mLock) {
- if (mEnabled) return;
- mEnabled = true;
- }
-
- sendMessage(ENABLE, 1, null);
- }
-
private void setSuplHostPort(String hostString, String portString) {
if (hostString != null) {
mSuplServerHost = hostString;
@@ -1052,21 +1045,6 @@
}
}
- /**
- * Disables this provider. When disabled, calls to getStatus()
- * need not be handled. Hardware may be shut
- * down while the provider is disabled.
- */
- @Override
- public void disable() {
- synchronized (mLock) {
- if (!mEnabled) return;
- mEnabled = false;
- }
-
- sendMessage(ENABLE, 0, null);
- }
-
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
@@ -1083,7 +1061,6 @@
mGnssNavigationMessageProvider.onGpsEnabledChanged();
}
- @Override
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
@@ -1147,7 +1124,7 @@
updateClientUids(mWorkSource);
mFixInterval = (int) mProviderRequest.interval;
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
// check for overflow
if (mFixInterval != mProviderRequest.interval) {
Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1171,7 +1148,8 @@
// set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
// and our fix interval is not short
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent); }
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
}
} else {
updateClientUids(new WorkSource());
@@ -1220,16 +1198,14 @@
List<WorkChain> goneChains = diffs[1];
if (newChains != null) {
- for (int i = 0; i < newChains.size(); ++i) {
- final WorkChain newChain = newChains.get(i);
+ for (WorkChain newChain : newChains) {
mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
newChain.getAttributionTag());
}
}
if (goneChains != null) {
- for (int i = 0; i < goneChains.size(); i++) {
- final WorkChain goneChain = goneChains.get(i);
+ for (WorkChain goneChain : goneChains) {
mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
goneChain.getAttributionTag());
}
@@ -1262,32 +1238,27 @@
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
+ public void sendExtraCommand(String command, Bundle extras) {
long identity = Binder.clearCallingIdentity();
try {
- boolean result = false;
-
if ("delete_aiding_data".equals(command)) {
- result = deleteAidingData(extras);
+ deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
requestUtcTime();
- result = true;
} else if ("force_xtra_injection".equals(command)) {
if (mSupportsXtra) {
xtraDownloadRequest();
- result = true;
}
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
- return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private boolean deleteAidingData(Bundle extras) {
+ private void deleteAidingData(Bundle extras) {
int flags;
if (extras == null) {
@@ -1311,10 +1282,7 @@
if (flags != 0) {
native_delete_aiding_data(flags);
- return true;
}
-
- return false;
}
private void startNavigating(boolean singleShot) {
@@ -1358,7 +1326,7 @@
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
interval, 0, 0, mLowPowerMode)) {
mStarted = false;
@@ -1415,10 +1383,7 @@
return ((mEngineCapabilities & capability) != 0);
}
-
- /**
- * called from native code to update our position.
- */
+ @NativeEntryPoint
private void reportLocation(boolean hasLatLong, Location location) {
sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
}
@@ -1444,11 +1409,7 @@
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
location.setExtras(mLocationExtras.getBundle());
- try {
- mILocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
if (mStarted) {
mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1482,7 +1443,8 @@
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// For devices that use framework scheduling, a timer may be set to ensure we don't
- // spend too much power searching for a location, when the requested update rate is slow.
+ // spend too much power searching for a location, when the requested update rate is
+ // slow.
// As we just recievied a location, we'll cancel that timer.
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
mAlarmManager.cancel(mTimeoutIntent);
@@ -1502,9 +1464,7 @@
}
}
- /**
- * called from native code to update our status
- */
+ @NativeEntryPoint
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
@@ -1512,16 +1472,13 @@
switch (status) {
case GPS_STATUS_SESSION_BEGIN:
mNavigating = true;
- mEngineOn = true;
break;
case GPS_STATUS_SESSION_END:
mNavigating = false;
break;
case GPS_STATUS_ENGINE_ON:
- mEngineOn = true;
break;
case GPS_STATUS_ENGINE_OFF:
- mEngineOn = false;
mNavigating = false;
break;
}
@@ -1538,17 +1495,15 @@
// Helper class to carry data to handler for reportSvStatus
private static class SvStatusInfo {
- public int mSvCount;
- public int[] mSvidWithFlags;
- public float[] mCn0s;
- public float[] mSvElevations;
- public float[] mSvAzimuths;
- public float[] mSvCarrierFreqs;
+ private int mSvCount;
+ private int[] mSvidWithFlags;
+ private float[] mCn0s;
+ private float[] mSvElevations;
+ private float[] mSvAzimuths;
+ private float[] mSvCarrierFreqs;
}
- /**
- * called from native code to update SV info
- */
+ @NativeEntryPoint
private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
SvStatusInfo svStatusInfo = new SvStatusInfo();
@@ -1622,16 +1577,12 @@
}
}
- /**
- * called from native code to update AGPS status
- */
+ @NativeEntryPoint
private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
}
- /**
- * called from native code to report NMEA data received
- */
+ @NativeEntryPoint
private void reportNmea(long timestamp) {
if (!mItarSpeedLimitExceeded) {
int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
@@ -1640,57 +1591,38 @@
}
}
- /**
- * called from native code - GNSS measurements callback
- */
+ @NativeEntryPoint
private void reportMeasurementData(GnssMeasurementsEvent event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssMeasurementsProvider.onMeasurementsAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
}
}
- /**
- * called from native code - GNSS navigation message callback
- */
+ @NativeEntryPoint
private void reportNavigationMessage(GnssNavigationMessage event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
}
}
- /**
- * called from native code to inform us what the GPS engine capabilities are
- */
+ @NativeEntryPoint
private void setEngineCapabilities(final int capabilities) {
// send to handler thread for fast native return, and in-order handling
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mEngineCapabilities = capabilities;
+ mHandler.post(() -> {
+ mEngineCapabilities = capabilities;
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
- }
-
- mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_MEASUREMENTS));
- mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_NAV_MESSAGES));
- restartRequests();
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+ mNtpTimeHelper.enablePeriodicTimeInjection();
+ requestUtcTime();
}
+
+ mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_MEASUREMENTS));
+ mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_NAV_MESSAGES));
+ restartRequests();
});
}
@@ -1710,27 +1642,21 @@
updateRequirements();
}
- /**
- * Called from native code to inform us the hardware year.
- */
+ @NativeEntryPoint
private void setGnssYearOfHardware(final int yearOfHardware) {
// mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
mHardwareYear = yearOfHardware;
}
- /**
- * Called from native code to inform us the hardware model name.
- */
+ @NativeEntryPoint
private void setGnssHardwareModelName(final String modelName) {
// mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
mHardwareModelName = modelName;
}
- /**
- * Called from native code to inform us GNSS HAL service died.
- */
+ @NativeEntryPoint
private void reportGnssServiceDied() {
if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
mHandler.post(() -> {
@@ -1750,6 +1676,7 @@
* Returns the year of underlying GPS hardware.
*/
int getGnssYearOfHardware();
+
/**
* Returns the model name of underlying GPS hardware.
*/
@@ -1765,6 +1692,7 @@
public int getGnssYearOfHardware() {
return mHardwareYear;
}
+
@Override
public String getGnssHardwareModelName() {
return mHardwareModelName;
@@ -1790,32 +1718,19 @@
* @hide
*/
public GnssMetricsProvider getGnssMetricsProvider() {
- return new GnssMetricsProvider() {
- @Override
- public String getGnssMetricsAsProtoString() {
- return mGnssMetrics.dumpGnssMetricsAsProtoString();
- }
- };
+ return () -> mGnssMetrics.dumpGnssMetricsAsProtoString();
}
- /**
- * called from native code - GNSS location batch callback
- */
+ @NativeEntryPoint
private void reportLocationBatch(Location[] locationArray) {
List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
if (DEBUG) {
Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
}
- try {
- mILocationManager.reportLocationBatch(locations);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocationBatch");
- }
+ reportLocation(locations);
}
- /**
- * called from native code to request XTRA data
- */
+ @NativeEntryPoint
private void xtraDownloadRequest() {
if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
@@ -1843,10 +1758,7 @@
}
}
- /**
- * Called from native to report GPS Geofence transition
- * All geofence callbacks are called on the same thread
- */
+ @NativeEntryPoint
private void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
mHandler.post(() -> {
@@ -1864,9 +1776,7 @@
});
}
- /**
- * called from native code to report GPS status change.
- */
+ @NativeEntryPoint
private void reportGeofenceStatus(int status, Location location) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1884,9 +1794,7 @@
});
}
- /**
- * called from native code - Geofence Add callback
- */
+ @NativeEntryPoint
private void reportGeofenceAddStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1896,9 +1804,7 @@
});
}
- /**
- * called from native code - Geofence Remove callback
- */
+ @NativeEntryPoint
private void reportGeofenceRemoveStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1908,9 +1814,7 @@
});
}
- /**
- * called from native code - Geofence Pause callback
- */
+ @NativeEntryPoint
private void reportGeofencePauseStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1920,9 +1824,7 @@
});
}
- /**
- * called from native code - Geofence Resume callback
- */
+ @NativeEntryPoint
private void reportGeofenceResumeStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1954,7 +1856,8 @@
return mNetInitiatedListener;
}
- // Called by JNI function to report an NI request.
+ /** Reports a NI notification. */
+ @NativeEntryPoint
public void reportNiNotification(
int notificationId,
int niType,
@@ -1997,11 +1900,10 @@
}
/**
- * Called from native code to request set id info.
* We should be careful about receiving null string from the TelephonyManager,
* because sending null String to JNI function would cause a crash.
*/
-
+ @NativeEntryPoint
private void requestSetID(int flags) {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2030,9 +1932,7 @@
native_agps_set_id(type, data);
}
- /**
- * Called from native code to request location info.
- */
+ @NativeEntryPoint
private void requestLocation(boolean independentFromGnss) {
if (DEBUG) {
Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss);
@@ -2040,17 +1940,13 @@
sendMessage(REQUEST_LOCATION, 0, independentFromGnss);
}
- /**
- * Called from native code to request utc time info
- */
+ @NativeEntryPoint
private void requestUtcTime() {
if (DEBUG) Log.d(TAG, "utcTimeRequest");
sendMessage(INJECT_NTP_TIME, 0, null);
}
- /**
- * Called from native code to request reference location info
- */
+ @NativeEntryPoint
private void requestRefLocation() {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2104,11 +2000,7 @@
int message = msg.what;
switch (message) {
case ENABLE:
- if (msg.arg1 == 1) {
- handleEnable();
- } else {
- handleDisable();
- }
+ handleEnable();
break;
case SET_REQUEST:
GpsRequest gpsRequest = (GpsRequest) msg.obj;
@@ -2153,7 +2045,8 @@
}
/**
- * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
+ * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
+ * Looper)}.
* It is in charge of loading properties and registering for events that will be posted to
* this handler.
*/
@@ -2206,12 +2099,11 @@
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
long minTime = 0;
float minDistance = 0;
- boolean oneShot = false;
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
LocationManager.PASSIVE_PROVIDER,
minTime,
minDistance,
- oneShot);
+ false);
// Don't keep track of this request since it's done on behalf of other clients
// (which are kept track of separately).
request.setHideFromAppOps(true);
@@ -2219,11 +2111,14 @@
request,
new NetworkLocationListener(),
getLooper());
+
+ // enable gps provider, it will never be disabled (legacy behavior)
+ sendEmptyMessage(ENABLE);
}
}
private abstract class LocationChangeListener implements LocationListener {
- int numLocationUpdateRequest;
+ private int mNumLocationUpdateRequest;
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
deleted file mode 100644
index 6785964..0000000
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 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.LocationProvider;
-import android.os.Bundle;
-import android.os.WorkSource;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Location Manager's interface for location providers.
- * @hide
- */
-public abstract class LocationProviderInterface {
-
- /** Get name. */
- public abstract String getName();
-
- /** Enable. */
- public abstract void enable();
-
- /** Disable. */
- public abstract void disable();
-
- /** Is enabled. */
- public abstract boolean isEnabled();
-
- /** Set request. */
- public abstract void setRequest(ProviderRequest request, WorkSource source);
-
- /** dump. */
- public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- /** Get properties. */
- public abstract ProviderProperties getProperties();
-
- /**
- * Get status.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public int getStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- /**
- * Get status update time.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public long getStatusUpdateTime() {
- return 0;
- }
-
- /** Send extra command. */
- public abstract boolean sendExtraCommand(String command, Bundle extras);
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b408414..dfcef70 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
@@ -27,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
@@ -41,21 +43,35 @@
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy extends LocationProviderInterface {
+public class LocationProviderProxy extends AbstractLocationProvider {
+
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
- private final ServiceWatcher mServiceWatcher;
-
- private final String mName;
-
// used to ensure that updates to mRequest and mWorkSource are atomic
private final Object mRequestLock = new Object();
+ private final ServiceWatcher mServiceWatcher;
- private volatile boolean mEnabled = false;
- @Nullable
- private volatile ProviderProperties mProperties;
+ private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
+ // executed on binder thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ LocationProviderProxy.this.setEnabled(enabled);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ LocationProviderProxy.this.setProperties(properties);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onReportLocation(Location location) {
+ LocationProviderProxy.this.reportLocation(location);
+ }
+ };
@GuardedBy("mRequestLock")
@Nullable
@@ -69,10 +85,10 @@
*/
@Nullable
public static LocationProviderProxy createAndBind(
- Context context, String name, String action,
+ Context context, LocationProviderManager locationProviderManager, String action,
int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+ LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
action, overlaySwitchResId, defaultServicePackageNameResId,
initialPackageNamesResId);
if (proxy.bind()) {
@@ -82,21 +98,27 @@
}
}
- private LocationProviderProxy(Context context, String name,
+ private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
String action, int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
+ super(locationProviderManager, false);
mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId,
BackgroundThread.getHandler()) {
+
@Override
protected void onBind() {
runOnBinder(LocationProviderProxy.this::initializeService);
}
- };
- mName = name;
- mProperties = null;
+ @Override
+ protected void onUnbind() {
+ setEnabled(false);
+ setProperties(null);
+ }
+ };
+
mRequest = null;
mWorkSource = new WorkSource();
}
@@ -107,84 +129,27 @@
private void initializeService(IBinder binder) {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- if (D) Log.d(TAG, "applying state to connected service");
-
- ProviderProperties[] properties = new ProviderProperties[1];
- ProviderRequest request;
- WorkSource source;
- synchronized (mRequestLock) {
- request = mRequest;
- source = mWorkSource;
- }
+ if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
try {
- // load properties from provider
- properties[0] = service.getProperties();
- if (properties[0] == null) {
- Log.e(TAG, mServiceWatcher.getCurrentPackageName()
- + " has invalid location provider properties");
- }
+ service.setLocationProviderManager(mManager);
- // apply current state to new service
- if (mEnabled) {
- service.enable();
- if (request != null) {
- service.setRequest(request, source);
+ synchronized (mRequestLock) {
+ if (mRequest != null) {
+ service.setRequest(mRequest, mWorkSource);
}
}
} catch (RemoteException e) {
Log.w(TAG, e);
}
-
- mProperties = properties[0];
}
+ @Nullable
public String getConnectedPackageName() {
return mServiceWatcher.getCurrentPackageName();
}
@Override
- public String getName() {
- return mName;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
-
- @Override
- public void enable() {
- mEnabled = true;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.enable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public void disable() {
- mEnabled = false;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.disable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public boolean isEnabled() {
- return mEnabled;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
synchronized (mRequestLock) {
mRequest = request;
@@ -202,60 +167,53 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.append("REMOTE SERVICE");
- pw.append(" name=").append(mName);
- pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
- pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
- pw.append('\n');
+ pw.println(" service=" + mServiceWatcher);
mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- TransferPipe.dumpAsync(service.asBinder(), fd, args);
+ TransferPipe.dumpAsync(binder, fd, args);
} catch (IOException | RemoteException e) {
- pw.println("Failed to dump location provider: " + e);
+ pw.println(" failed to dump location provider: " + e);
}
});
}
@Override
public int getStatus(Bundle extras) {
- int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+ int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatus(extras);
+ status[0] = service.getStatus(extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return status[0];
}
@Override
public long getStatusUpdateTime() {
- long[] result = new long[]{0L};
+ long[] updateTime = new long[] {0L};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatusUpdateTime();
+ updateTime[0] = service.getStatusUpdateTime();
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return updateTime[0];
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- boolean[] result = new boolean[]{false};
+ public void sendExtraCommand(String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.sendExtraCommand(command, extras);
+ service.sendExtraCommand(command, extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
}
}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 145aee3..bfbebf7 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,14 +16,11 @@
package com.android.server.location;
-import android.location.ILocationManager;
+import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -36,63 +33,57 @@
*
* {@hide}
*/
-public class MockProvider extends LocationProviderInterface {
- private final String mName;
- private final ProviderProperties mProperties;
- private final ILocationManager mLocationManager;
+public class MockProvider extends AbstractLocationProvider {
- private final Location mLocation;
-
- private boolean mHasLocation;
private boolean mEnabled;
-
-
+ @Nullable private Location mLocation;
private int mStatus;
private long mStatusUpdateTime;
private Bundle mExtras;
- private static final String TAG = "MockProvider";
+ public MockProvider(
+ LocationProviderManager locationProviderManager, ProviderProperties properties) {
+ super(locationProviderManager, true);
- public MockProvider(String name, ILocationManager locationManager,
- ProviderProperties properties) {
- if (properties == null) throw new NullPointerException("properties is null");
-
- mName = name;
- mLocationManager = locationManager;
- mProperties = properties;
- mLocation = new Location(name);
-
- mStatus = LocationProvider.AVAILABLE;
- mStatusUpdateTime = 0L;
- mExtras = null;
- }
-
- @Override
- public String getName() {
- return mName;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
-
- @Override
- public void disable() {
- mEnabled = false;
- }
-
- @Override
- public void enable() {
mEnabled = true;
+ mLocation = null;
+ mStatus = LocationProvider.AVAILABLE;
+ mStatusUpdateTime = 0;
+ mExtras = null;
+
+ setProperties(properties);
+ }
+
+ /** Sets the enabled state of this mock provider. */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ super.setEnabled(enabled);
+ }
+
+ /** Sets the location to report for this mock provider. */
+ public void setLocation(Location l) {
+ mLocation = new Location(l);
+ if (mEnabled) {
+ reportLocation(l);
+ }
+ }
+
+ /** Sets the status for this mock provider. */
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras = extras;
}
@Override
- public boolean isEnabled() {
- return mEnabled;
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(" last location=" + mLocation);
}
@Override
+ public void setRequest(ProviderRequest request, WorkSource source) {}
+
+ @Override
public int getStatus(Bundle extras) {
if (mExtras != null) {
extras.clear();
@@ -107,50 +98,6 @@
return mStatusUpdateTime;
}
- public void setLocation(Location l) {
- mLocation.set(l);
- mHasLocation = true;
- if (mEnabled) {
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
- }
- }
-
- public void clearLocation() {
- mHasLocation = false;
- }
-
- /**
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public void setStatus(int status, Bundle extras, long updateTime) {
- mStatus = status;
- mStatusUpdateTime = updateTime;
- mExtras = extras;
- }
-
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dump(pw, "");
- }
-
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + mName);
- pw.println(prefix + "mHasLocation=" + mHasLocation);
- pw.println(prefix + "mLocation:");
- mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mExtras=" + mExtras);
- }
-
- @Override
- public void setRequest(ProviderRequest request, WorkSource source) { }
-
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 99c9214..70d64b0 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -17,13 +17,9 @@
package com.android.server.location;
import android.location.Criteria;
-import android.location.ILocationManager;
import android.location.Location;
-import android.location.LocationManager;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -38,41 +34,20 @@
*
* {@hide}
*/
-public class PassiveProvider extends LocationProviderInterface {
- private static final String TAG = "PassiveProvider";
+public class PassiveProvider extends AbstractLocationProvider {
private static final ProviderProperties PROPERTIES = new ProviderProperties(
false, false, false, false, false, false, false,
Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
- private final ILocationManager mLocationManager;
private boolean mReportLocation;
- public PassiveProvider(ILocationManager locationManager) {
- mLocationManager = locationManager;
- }
+ public PassiveProvider(LocationProviderManager locationProviderManager) {
+ super(locationProviderManager, true);
- @Override
- public String getName() {
- return LocationManager.PASSIVE_PROVIDER;
- }
+ mReportLocation = false;
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- public void enable() {
- }
-
- @Override
- public void disable() {
+ setProperties(PROPERTIES);
}
@Override
@@ -82,22 +57,15 @@
public void updateLocation(Location location) {
if (mReportLocation) {
- try {
- // pass the location back to the location manager
- mLocationManager.reportLocation(location, true);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
}
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mReportLocation=" + mReportLocation);
+ pw.println(" report location=" + mReportLocation);
}
}