diff options
author | Soonil Nagarkar <sooniln@google.com> | 2018-10-24 17:54:54 -0700 |
---|---|---|
committer | Soonil Nagarkar <sooniln@google.com> | 2018-12-10 10:29:17 -0800 |
commit | 1575a04e7bc830a8fc15de34dea6362b10c563eb (patch) | |
tree | db5be622ac5eaa5dcc3eb681258b48ec70d85ca9 | |
parent | 4c47901848ccc14b0e4ced7f971a163e2c734d8a (diff) |
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
22 files changed, 1149 insertions, 1286 deletions
diff --git a/Android.bp b/Android.bp index 55686734e8ea..d64e7a26a7f6 100644 --- a/Android.bp +++ b/Android.bp @@ -451,6 +451,7 @@ java_defaults { "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 fe91ba12e28d..da64a134b43d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22732,8 +22732,8 @@ package android.location { 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 01c70286cff3..56dc4c1c0710 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -1849,13 +1849,16 @@ Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lco 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 ae87998a1615..32c752064a69 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -96,9 +96,7 @@ interface ILocationManager 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 @@ interface ILocationManager // --- 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 3bf98b352b40..334170e5ce03 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1537,14 +1537,11 @@ public class LocationManager { * 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 @@ public class LocationManager { * 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 39c2d92bf278..71b54fb65ae5 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 000000000000..b1b8f0c7c3f7 --- /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 88919f628638..a45c20d9d09d 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 final class ProviderRequest implements Parcelable { 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 d19559e8cccd..10c344775019 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -8,14 +8,18 @@ package com.android.location.provider { 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 @@ package com.android.location.provider { } 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 d45a4bac8f96..5bcec92c4fba 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.os.WorkSource; 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 @@ import java.io.PrintWriter; * 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 abstract class LocationProviderBase { */ 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 @@ public abstract class LocationProviderBase { } /** - * 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 @@ public abstract class LocationProviderBase { * <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 @@ public abstract class LocationProviderBase { * * @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 6a8e61877e46..b825b58cd3e9 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 @@ public final class ProviderRequestUnbundled { 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 87d6e4a137ac..be817d60e55b 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.content.IntentFilter; 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 com.android.location.provider.ProviderRequestUnbundled; 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; + private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + mEngine.switchUser(); + } } - } + }; - public FusedLocationProvider(Context context) { + 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); + mContext = context; + mHandler = new Handler(Looper.myLooper()); + mEngine = new FusionEngine(context, Looper.myLooper(), this); } - /** - * For serializing requests to mEngine. - */ - private Handler mHandler = new Handler() { - @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; - } - } - } - }; - - @Override - public void onEnable() { - mHandler.sendEmptyMessage(MSG_ENABLE); + void init() { + // listen for user change + mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL, + new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); } - @Override - public void onDisable() { - mHandler.sendEmptyMessage(MSG_DISABLE); + 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 12966cfab888..75bb5eceab6d 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.content.Intent; 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 + public void onDestroy() { if (mProvider != null) { - mProvider.onDisable(); + mProvider.destroy(); + mProvider = null; } - return false; - } - - @Override - public void onDestroy() { - 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 7a4952484e46..e4610cf44636 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.Bundle; 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 class FusionEngine implements LocationListener { 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 class FusionEngine implements LocationListener { public boolean requested; public long requestTime; public long minTime; + @Override public String toString() { return (requested ? " REQUESTED" : " ---"); @@ -154,7 +117,7 @@ public class FusionEngine implements LocationListener { } private void updateRequirements() { - if (!mEnabled || mRequest == null) { + if (mRequest == null || !mRequest.getReportLocation()) { mRequest = null; disableProvider(NETWORK); disableProvider(GPS); @@ -200,29 +163,30 @@ public class FusionEngine implements LocationListener { * 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; - } - - // 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(); + 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(); } private void updateFusedLocation() { @@ -252,9 +216,9 @@ public class FusionEngine implements LocationListener { } 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 @@ public class FusionEngine implements LocationListener { /** 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 cc7bf3373bdd..db445c1733fd 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.ILocationManager; 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.location.ProviderRequest; 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.GnssMeasurementsProvider; 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // prepare providers loadProvidersLocked(); - updateProvidersLocked(); + updateProvidersSettingsLocked(); + for (LocationProvider provider : mProviders) { + applyRequirementsLocked(provider.getName()); + } } // listen for settings changes @@ -366,7 +345,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onChange(boolean selfChange) { synchronized (mLock) { - updateProvidersLocked(); + updateProvidersSettingsLocked(); } } }, UserHandle.USER_ALL); @@ -377,7 +356,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onChange(boolean selfChange) { synchronized (mLock) { - updateProvidersLocked(); + for (LocationProvider provider : mProviders) { + applyRequirementsLocked(provider.getName()); + } } } }, UserHandle.USER_ALL); @@ -402,7 +383,9 @@ public class LocationManagerService extends ILocationManager.Stub { public void onChange(boolean selfChange) { synchronized (mLock) { updateBackgroundThrottlingWhitelistLocked(); - updateProvidersLocked(); + for (LocationProvider provider : mProviders) { + applyRequirementsLocked(provider.getName()); + } } } }, UserHandle.USER_ALL); @@ -414,7 +397,6 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } } + 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } } - 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 @@ public class LocationManagerService extends ILocationManager.Stub { * @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); + } + 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); } - return isLocationProviderEnabledForUser(provider, userId); } @@ -1481,9 +1610,11 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { } } } + return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE } @@ -1550,7 +1682,7 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { } } - boolean reportLocationAccessNoThrow( + private boolean reportLocationAccessNoThrow( int pid, int uid, String packageName, int allowedResolutionLevel) { int op = resolutionLevelToOp(allowedResolutionLevel); if (op >= 0) { @@ -1577,7 +1709,8 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { && !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 @@ public class LocationManagerService extends ILocationManager.Stub { */ @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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { @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; - } - } - if (changesMade) { - mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION), - UserHandle.ALL); - mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION), - UserHandle.ALL); + private void updateProvidersSettingsLocked() { + for (LocationProvider p : mProviders) { + p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId)); } - } - private void updateProviderListenersLocked(String provider, boolean enabled) { - int listeners = 0; + mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION), + UserHandle.ALL); + } - 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 @@ public class LocationManagerService extends ILocationManager.Stub { } deadReceivers.add(record.mReceiver); } - listeners++; } } } @@ -1758,16 +1872,11 @@ public class LocationManagerService extends ILocationManager.Stub { } } - 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 class LocationManagerService extends ILocationManager.Stub { public String[] getBackgroundThrottlingWhitelist() { synchronized (mLock) { return mBackgroundThrottlePackageWhitelist.toArray( - new String[mBackgroundThrottlePackageWhitelist.size()]); + new String[0]); } } @@ -1922,17 +2031,17 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { /** * 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 @@ public class LocationManagerService extends ILocationManager.Stub { /** * 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } return false; } - LocationProviderInterface p = null; + LocationProvider p = null; String provider = location.getProvider(); if (provider != null) { p = mProvidersByName.get(provider); @@ -2370,7 +2464,7 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { @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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { */ @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 @@ public class LocationManagerService extends ILocationManager.Stub { */ @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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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,93 +2816,48 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * 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); - // 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; - - int uid = Binder.getCallingUid(); - synchronized (mLock) { - LocationProviderInterface p = mProvidersByName.get(provider); - return p != null - && isAllowedByUserSettingsLocked(provider, uid, userId); + if (!isLocationEnabledForUser(userId)) { + return false; } - } - - /** - * 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 - * @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; + if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false; long identity = Binder.clearCallingIdentity(); try { + LocationProvider provider; 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); + provider = mProvidersByName.get(providerName); } + return provider != null && provider.isEnabled(); } finally { Binder.restoreCallingIdentity(identity); } } /** - * Read location provider status from Settings.Secure + * Enable or disable a single location provider. * - * @param provider the location provider to query - * @param userId the user id to query - * @return true if the provider is enabled + * @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 + * @return true if the value was set, false on errors */ - 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); - } + @Override + public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) { + return false; } /** @@ -2941,7 +2989,7 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { /** * 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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)); + try { + removeProviderLocked(mProvidersByName.get(provider)); - // reinstate real provider if available - LocationProviderInterface realProvider = mRealProviders.get(provider); - if (realProvider != null) { - addProviderLocked(realProvider); + // 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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,47 +3383,18 @@ public class LocationManagerService extends ILocationManager.Stub { 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); - if (mockProvider == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + try { + mockProvider.setEnabled(enabled); + } finally { + Binder.restoreCallingIdentity(identity); } - long identity = Binder.clearCallingIdentity(); - mEnabledProviders.remove(provider); - mDisabledProviders.remove(provider); - updateProvidersLocked(); - Binder.restoreCallingIdentity(identity); } } @@ -3450,10 +3454,11 @@ public class LocationManagerService extends ILocationManager.Stub { + 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 574f54a51eb1..d09823efb6fa 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 @@ public class ServiceWatcher implements ServiceConnection { // 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 @@ public class ServiceWatcher implements ServiceConnection { @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 @@ public class ServiceWatcher implements ServiceConnection { 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 @@ public class ServiceWatcher implements ServiceConnection { } 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 @@ public class ServiceWatcher implements ServiceConnection { 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 @@ public class ServiceWatcher implements ServiceConnection { } } - @WorkerThread private void bind(ComponentName component, int version, int userId) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); @@ -382,7 +367,6 @@ public class ServiceWatcher implements ServiceConnection { UserHandle.of(userId)); } - @WorkerThread private void unbind() { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); @@ -396,7 +380,6 @@ public class ServiceWatcher implements ServiceConnection { mBestUserId = UserHandle.USER_NULL; } - @MainThread @Override public final void onServiceConnected(ComponentName component, IBinder binder) { mHandler.post(() -> { @@ -410,7 +393,6 @@ public class ServiceWatcher implements ServiceConnection { }); } - @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 000000000000..4c7c420214bd --- /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 330d1d5de1aa..9e2a94e30fce 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.GnssStatus; 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.FileDescriptor; 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 @@ import java.util.Properties; * * {@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 class GnssLocationProvider extends LocationProviderInterface } public void set(int svCount, int meanCn0, int maxCn0) { - synchronized(this) { + synchronized (this) { mSvCount = svCount; mMeanCn0 = meanCn0; mMaxCn0 = maxCn0; @@ -258,7 +271,7 @@ public class GnssLocationProvider extends LocationProviderInterface } 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface // 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface */ @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 @@ public class GnssLocationProvider extends LocationProviderInterface } interface SetCarrierProperty { - public boolean set(int value); + boolean set(int value); } private void reloadGpsProperties(Context context, Properties properties) { @@ -587,7 +599,7 @@ public class GnssLocationProvider extends LocationProviderInterface /* * 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface // 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface mGnssNavigationMessageProvider.onGpsEnabledChanged(); } - @Override public boolean isEnabled() { synchronized (mLock) { return mEnabled; @@ -1147,7 +1124,7 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface // 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface } @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 @@ public class GnssLocationProvider extends LocationProviderInterface if (flags != 0) { native_delete_aiding_data(flags); - return true; } - - return false; } private void startNavigating(boolean singleShot) { @@ -1358,7 +1326,7 @@ public class GnssLocationProvider extends LocationProviderInterface } 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface // 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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; - - if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) { - mNtpTimeHelper.enablePeriodicTimeInjection(); - requestUtcTime(); - } + mHandler.post(() -> { + mEngineCapabilities = capabilities; - 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface * Returns the year of underlying GPS hardware. */ int getGnssYearOfHardware(); + /** * Returns the model name of underlying GPS hardware. */ @@ -1765,6 +1692,7 @@ public class GnssLocationProvider extends LocationProviderInterface public int getGnssYearOfHardware() { return mHardwareYear; } + @Override public String getGnssHardwareModelName() { return mHardwareModelName; @@ -1790,32 +1718,19 @@ public class GnssLocationProvider extends LocationProviderInterface * @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 @@ public class GnssLocationProvider extends LocationProviderInterface } } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface }); } - /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface }); } - /** - * called from native code - Geofence Add callback - */ + @NativeEntryPoint private void reportGeofenceAddStatus(int geofenceId, int status) { mHandler.post(() -> { if (mGeofenceHardwareImpl == null) { @@ -1896,9 +1804,7 @@ public class GnssLocationProvider extends LocationProviderInterface }); } - /** - * called from native code - Geofence Remove callback - */ + @NativeEntryPoint private void reportGeofenceRemoveStatus(int geofenceId, int status) { mHandler.post(() -> { if (mGeofenceHardwareImpl == null) { @@ -1908,9 +1814,7 @@ public class GnssLocationProvider extends LocationProviderInterface }); } - /** - * called from native code - Geofence Pause callback - */ + @NativeEntryPoint private void reportGeofencePauseStatus(int geofenceId, int status) { mHandler.post(() -> { if (mGeofenceHardwareImpl == null) { @@ -1920,9 +1824,7 @@ public class GnssLocationProvider extends LocationProviderInterface }); } - /** - * called from native code - Geofence Resume callback - */ + @NativeEntryPoint private void reportGeofenceResumeStatus(int geofenceId, int status) { mHandler.post(() -> { if (mGeofenceHardwareImpl == null) { @@ -1954,7 +1856,8 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface } /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 @@ public class GnssLocationProvider extends LocationProviderInterface } /** - * 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 @@ public class GnssLocationProvider extends LocationProviderInterface (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 @@ public class GnssLocationProvider extends LocationProviderInterface 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 678596445fe3..000000000000 --- 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 b40841467946..dfcef70c8248 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 @@ package com.android.server.location; 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 android.util.Log; 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 @@ import java.io.PrintWriter; /** * 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 @@ public class LocationProviderProxy extends LocationProviderInterface { */ @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 @@ public class LocationProviderProxy extends LocationProviderInterface { } } - 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); } + + @Override + protected void onUnbind() { + setEnabled(false); + setProperties(null); + } }; - mName = name; - mProperties = null; mRequest = null; mWorkSource = new WorkSource(); } @@ -107,84 +129,27 @@ public class LocationProviderProxy extends LocationProviderInterface { 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 @@ public class LocationProviderProxy extends LocationProviderInterface { @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 145aee3a9e6a..bfbebf74e93d 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,61 +33,55 @@ import java.io.PrintWriter; * * {@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(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); + public MockProvider( + LocationProviderManager locationProviderManager, ProviderProperties properties) { + super(locationProviderManager, true); + mEnabled = true; + mLocation = null; mStatus = LocationProvider.AVAILABLE; - mStatusUpdateTime = 0L; + mStatusUpdateTime = 0; mExtras = null; + + setProperties(properties); } - @Override - public String getName() { - return mName; + /** Sets the enabled state of this mock provider. */ + public void setEnabled(boolean enabled) { + mEnabled = enabled; + super.setEnabled(enabled); } - @Override - public ProviderProperties getProperties() { - return mProperties; + /** Sets the location to report for this mock provider. */ + public void setLocation(Location l) { + mLocation = new Location(l); + if (mEnabled) { + reportLocation(l); + } } - @Override - public void disable() { - mEnabled = false; + /** Sets the status for this mock provider. */ + public void setStatus(int status, Bundle extras, long updateTime) { + mStatus = status; + mStatusUpdateTime = updateTime; + mExtras = extras; } @Override - public void enable() { - mEnabled = true; + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(" last location=" + mLocation); } @Override - public boolean isEnabled() { - return mEnabled; - } + public void setRequest(ProviderRequest request, WorkSource source) {} @Override public int getStatus(Bundle extras) { @@ -107,50 +98,6 @@ public class MockProvider extends LocationProviderInterface { 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 99c92149fa1b..70d64b02e4b4 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 @@ import java.io.PrintWriter; * * {@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; - } - - @Override - public String getName() { - return LocationManager.PASSIVE_PROVIDER; - } - - @Override - public ProviderProperties getProperties() { - return PROPERTIES; - } + public PassiveProvider(LocationProviderManager locationProviderManager) { + super(locationProviderManager, true); - @Override - public boolean isEnabled() { - return true; - } + mReportLocation = false; - @Override - public void enable() { - } - - @Override - public void disable() { + setProperties(PROPERTIES); } @Override @@ -82,22 +57,15 @@ public class PassiveProvider extends LocationProviderInterface { 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); } } |