diff options
| author | 2017-01-12 13:57:38 -0800 | |
|---|---|---|
| committer | 2017-02-07 14:55:21 -0800 | |
| commit | cf879db3666523404ff7e15e77b11434732b4a06 (patch) | |
| tree | b479463245b8ad1f2c70862856ace256ada7f619 | |
| parent | 85adc2148854ed63569457da651e0213eaeaf0a4 (diff) | |
GNSS Batching - Default Implementation
Connections from .hal layer, through to
Location Manager System APIs
Bug: 31974439
Test: builds, boots, ordinary GNSS & new GNSS batching
basic functional checks on Marlin
Change-Id: If75118c27b5ed34cfc16c9f817b60a3be5485095
11 files changed, 777 insertions, 61 deletions
diff --git a/Android.mk b/Android.mk index 22323c5e6772..59a00a1aaa70 100644 --- a/Android.mk +++ b/Android.mk @@ -383,6 +383,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ keystore/java/android/security/IKeyChainAliasCallback.aidl \ keystore/java/android/security/IKeyChainService.aidl \ + location/java/android/location/IBatchedLocationCallback.aidl \ location/java/android/location/ICountryDetector.aidl \ location/java/android/location/ICountryListener.aidl \ location/java/android/location/IFusedProvider.aidl \ diff --git a/api/system-current.txt b/api/system-current.txt index a0876e17bd98..02b3517f0d96 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -21319,6 +21319,11 @@ package android.location { field public static final android.os.Parcelable.Creator<android.location.Address> CREATOR; } + public abstract class BatchedLocationCallback { + ctor public BatchedLocationCallback(); + method public void onLocationBatch(java.util.List<android.location.Location>); + } + public class Criteria implements android.os.Parcelable { ctor public Criteria(); ctor public Criteria(android.location.Criteria); @@ -21857,14 +21862,17 @@ package android.location { method public void clearTestProviderEnabled(java.lang.String); method public void clearTestProviderLocation(java.lang.String); method public void clearTestProviderStatus(java.lang.String); + method public void flushGnssBatch(); method public java.util.List<java.lang.String> getAllProviders(); method public java.lang.String getBestProvider(android.location.Criteria, boolean); + method public int getGnssBatchSize(); method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus); method public android.location.Location getLastKnownLocation(java.lang.String); method public android.location.LocationProvider getProvider(java.lang.String); method public java.util.List<java.lang.String> getProviders(boolean); method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean); method public boolean isProviderEnabled(java.lang.String); + method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler); method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback); method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler); method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback); @@ -21895,6 +21903,7 @@ package android.location { method public void setTestProviderEnabled(java.lang.String, boolean); method public void setTestProviderLocation(java.lang.String, android.location.Location); method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long); + method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback); method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback); diff --git a/location/java/android/location/BatchedLocationCallback.java b/location/java/android/location/BatchedLocationCallback.java new file mode 100644 index 000000000000..f1c40ae256b7 --- /dev/null +++ b/location/java/android/location/BatchedLocationCallback.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.annotation.IntDef; +import android.annotation.SystemApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Used for receiving notifications from the LocationManager when + * the a batch of location is ready. These methods are called if the + * BatchedLocationCallback has been registered with the location manager service + * using the + * {@link LocationManager#registerGnssBatchedLocationCallback#startGnssBatch(long, + * boolean, BatchedLocationCallback, android.os.Handler)} method. + * @hide + */ +@SystemApi +public abstract class BatchedLocationCallback { + + /** + * Called when a new batch of locations is ready + * + * @param locations A list of all new locations (possibly zero of them.) + */ + public void onLocationBatch(List<Location> locations) {} +} diff --git a/location/java/android/location/BatchedLocationCallbackTransport.java b/location/java/android/location/BatchedLocationCallbackTransport.java new file mode 100644 index 000000000000..e00f855e9302 --- /dev/null +++ b/location/java/android/location/BatchedLocationCallbackTransport.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.content.Context; +import android.os.RemoteException; + +import java.util.List; + +/** + * A handler class to manage transport callbacks for {@link BatchedLocationCallback}. + * + * @hide + */ +class BatchedLocationCallbackTransport + extends LocalListenerHelper<BatchedLocationCallback> { + private final ILocationManager mLocationManager; + + private final IBatchedLocationCallback mCallbackTransport = new CallbackTransport(); + + public BatchedLocationCallbackTransport(Context context, ILocationManager locationManager) { + super(context, "BatchedLocationCallbackTransport"); + mLocationManager = locationManager; + } + + @Override + protected boolean registerWithServer() throws RemoteException { + return mLocationManager.addGnssBatchingCallback( + mCallbackTransport, + getContext().getPackageName()); + } + + @Override + protected void unregisterFromServer() throws RemoteException { + mLocationManager.removeGnssBatchingCallback(); + } + + private class CallbackTransport extends IBatchedLocationCallback.Stub { + @Override + public void onLocationBatch(final List<Location> locations) { + ListenerOperation<BatchedLocationCallback> operation = + new ListenerOperation<BatchedLocationCallback>() { + @Override + public void execute(BatchedLocationCallback callback) + throws RemoteException { + callback.onLocationBatch(locations); + } + }; + foreach(operation); + } + } +} diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java index aa26111a73e7..c7188aa34522 100644 --- a/location/java/android/location/GnssNavigationMessage.java +++ b/location/java/android/location/GnssNavigationMessage.java @@ -89,7 +89,7 @@ public final class GnssNavigationMessage implements Parcelable { */ public static abstract class Callback { /** - * The status of GNSS measurements event. + * The status of GNSS Navigation Message event. * @hide */ @Retention(RetentionPolicy.SOURCE) diff --git a/location/java/android/location/IBatchedLocationCallback.aidl b/location/java/android/location/IBatchedLocationCallback.aidl new file mode 100644 index 000000000000..dce9f964c010 --- /dev/null +++ b/location/java/android/location/IBatchedLocationCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.Location; + +import java.util.List; + +/** + * {@hide} + */ +oneway interface IBatchedLocationCallback +{ + void onLocationBatch(in List<Location> locations); +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index bc8b026222ed..fc31f326354f 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -21,6 +21,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.IBatchedLocationCallback; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; import android.location.IGnssNavigationMessageListener; @@ -71,6 +72,13 @@ interface ILocationManager int getGnssYearOfHardware(); + int getGnssBatchSize(String packageName); + boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName); + void removeGnssBatchingCallback(); + boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName); + void flushGnssBatch(String packageName); + boolean stopGnssBatch(); + // --- deprecated --- List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); @@ -99,6 +107,9 @@ interface ILocationManager // it need not be shared with other providers. 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 void locationCallbackFinished(ILocationListener listener); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 4e146268b256..f9385c6d3b7f 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -18,6 +18,7 @@ package android.location; import com.android.internal.location.ProviderProperties; +import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -59,13 +60,15 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION; * return location results, but the update rate will be throttled and the exact * location will be obfuscated to a coarse level of accuracy. */ -public class LocationManager { +public class LocationManager +{ private static final String TAG = "LocationManager"; private final Context mContext; private final ILocationManager mService; private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport; private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport; + private final BatchedLocationCallbackTransport mBatchedLocationCallbackTransport; private final HashMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners = new HashMap<>(); private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners = @@ -321,9 +324,13 @@ public class LocationManager { public LocationManager(Context context, ILocationManager service) { mService = service; mContext = context; - mGnssMeasurementCallbackTransport = new GnssMeasurementCallbackTransport(mContext, mService); + mGnssMeasurementCallbackTransport = + new GnssMeasurementCallbackTransport(mContext, mService); mGnssNavigationMessageCallbackTransport = new GnssNavigationMessageCallbackTransport(mContext, mService); + mBatchedLocationCallbackTransport = + new BatchedLocationCallbackTransport(mContext, mService); + } private LocationProvider createProvider(String name, ProviderProperties properties) { @@ -1878,7 +1885,8 @@ public class LocationManager { * No-op method to keep backward-compatibility. * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead. * @hide - * @deprecated use {@link #unregisterGnssNavigationMessageCallback(GnssMeasurements.Callback)} + * @deprecated use + * {@link #unregisterGnssNavigationMessageCallback(GnssNavigationMessage.Callback)} * instead */ @Deprecated @@ -1960,6 +1968,96 @@ public class LocationManager { } /** + * Returns the batch size (in number of Location objects) that are supported by the batching + * interface. + * + * @return Maximum number of location objects that can be returned + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public int getGnssBatchSize() { + try { + return mService.getGnssBatchSize(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Start hardware-batching of GNSS locations. This API is primarily used when the AP is + * asleep and the device can batch GNSS locations in the hardware. + * + * Note this is designed (as was the fused location interface before it) for a single user + * SystemApi - requests are not consolidated. Care should be taken when the System switches + * users that may have different batching requests, to stop hardware batching for one user, and + * restart it for the next. + * + * @param periodNanos Time interval, in nanoseconds, that the GNSS locations are requested + * within the batch + * @param wakeOnFifoFull True if the hardware batching should flush the locations in a + * a callback to the listener, when it's internal buffer is full. If + * set to false, the oldest location information is, instead, + * dropped when the buffer is full. + * @param callback The listener on which to return the batched locations + * @param handler The handler on which to process the callback + * + * @return True if batching was successfully started + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public boolean registerGnssBatchedLocationCallback(long periodNanos, boolean wakeOnFifoFull, + BatchedLocationCallback callback, Handler handler) { + mBatchedLocationCallbackTransport.add(callback, handler); + + try { + return mService.startGnssBatch(periodNanos, wakeOnFifoFull, mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Flush the batched GNSS locations. + * All GNSS locations currently ready in the batch are returned via the callback sent in + * startGnssBatch(), and the buffer containing the batched locations is cleared. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public void flushGnssBatch() { + try { + mService.flushGnssBatch(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Stop batching locations. This API is primarily used when the AP is + * asleep and the device can batch locations in the hardware. + * + * @param callback the specific callback class to remove from the transport layer + * + * @return True if batching was successfully started + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public boolean unregisterGnssBatchedLocationCallback(BatchedLocationCallback callback) { + + mBatchedLocationCallbackTransport.remove(callback); + + try { + return mService.stopGnssBatch(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Sends additional commands to a location provider. * Can be used to support provider specific extensions to the Location Manager API * diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 4ab894f05126..4a72c1f87db6 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import android.app.ActivityManager; +import android.annotation.NonNull; import android.content.pm.PackageManagerInternal; import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; @@ -62,6 +63,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.IBatchedLocationCallback; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; import android.location.IGnssStatusProvider; @@ -101,6 +103,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; /** @@ -229,6 +232,11 @@ public class LocationManagerService extends ILocationManager.Stub { private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider; + private GnssLocationProvider.GnssBatchingProvider mGnssBatchingProvider; + private IBatchedLocationCallback mGnssBatchingCallback; + private LinkedCallback mGnssBatchingDeathCallback; + private boolean mGnssBatchingInProgress = false; + public LocationManagerService(Context context) { super(); mContext = context; @@ -545,6 +553,7 @@ public class LocationManagerService extends ILocationManager.Stub { GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this, mLocationHandler.getLooper()); mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider(); + mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider(); mGnssStatusProvider = gnssProvider.getGnssStatusProvider(); mNetInitiatedListener = gnssProvider.getNetInitiatedListener(); addProviderLocked(gnssProvider); @@ -1073,13 +1082,197 @@ public class LocationManagerService extends ILocationManager.Stub { */ @Override public int getGnssYearOfHardware() { - if (mGnssNavigationMessageProvider != null) { + if (mGnssSystemInfoProvider != null) { return mGnssSystemInfoProvider.getGnssYearOfHardware(); } else { return 0; } } + /** + * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly + * (try to) access GNSS information at this layer. + */ + private boolean hasGnssPermissions(String packageName) { + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForProviderUse( + allowedResolutionLevel, + LocationManager.GPS_PROVIDER); + + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + boolean hasLocationAccess; + try { + hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); + } finally { + Binder.restoreCallingIdentity(identity); + } + + return hasLocationAccess; + } + + /** + * Returns the GNSS batching size, if available. + */ + @Override + public int getGnssBatchSize(String packageName) { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware batching"); + + if (hasGnssPermissions(packageName) && mGnssBatchingProvider != null) { + return mGnssBatchingProvider.getSize(); + } else { + return 0; + } + } + + /** + * Adds a callback for GNSS Batching events, if permissions allow, which are transported + * to potentially multiple listeners by the BatchedLocationCallbackTransport above this. + */ + @Override + public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware batching"); + + if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) { + return false; + } + + mGnssBatchingCallback = callback; + mGnssBatchingDeathCallback = new LinkedCallback(callback); + try { + callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */); + } catch (RemoteException e) { + // if the remote process registering the listener is already dead, just swallow the + // exception and return + Log.e(TAG, "Remote listener already died.", e); + return false; + } + + return true; + } + + private class LinkedCallback implements IBinder.DeathRecipient { + private final IBatchedLocationCallback mCallback; + + public LinkedCallback(@NonNull IBatchedLocationCallback callback) { + mCallback = callback; + } + + @NonNull + public IBatchedLocationCallback getUnderlyingListener() { + return mCallback; + } + + @Override + public void binderDied() { + Log.d(TAG, "Remote Batching Callback died: " + mCallback); + stopGnssBatch(); + removeGnssBatchingCallback(); + } + } + + /** + * Removes callback for GNSS batching + */ + @Override + public void removeGnssBatchingCallback() { + try { + mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback, + 0 /* flags */); + } catch (NoSuchElementException e) { + // if the death callback isn't connected (it should be...), log error, swallow the + // exception and return + Log.e(TAG, "Couldn't unlink death callback.", e); + } + mGnssBatchingCallback = null; + mGnssBatchingDeathCallback = null; + } + + + /** + * Starts GNSS batching, if available. + */ + @Override + public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware batching"); + + if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) { + return false; + } + + if (mGnssBatchingInProgress) { + // Current design does not expect multiple starts to be called repeatedly + Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch"); + // Try to clean up anyway, and continue + stopGnssBatch(); + } + + mGnssBatchingInProgress = true; + return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull); + } + + /** + * Flushes a GNSS batch in progress + */ + @Override + public void flushGnssBatch(String packageName) { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware batching"); + + if (!hasGnssPermissions(packageName)) { + Log.e(TAG, "flushGnssBatch called without GNSS permissions"); + return; + } + + if (!mGnssBatchingInProgress) { + Log.w(TAG, "flushGnssBatch called with no batch in progress"); + } + + if (mGnssBatchingProvider != null) { + mGnssBatchingProvider.flush(); + } + } + + /** + * Stops GNSS batching + */ + @Override + public boolean stopGnssBatch() { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware batching"); + + if (mGnssBatchingProvider != null) { + mGnssBatchingInProgress = false; + return mGnssBatchingProvider.stop(); + } else { + return false; + } + } + + @Override + public void reportLocationBatch(List<Location> locations) { + checkCallerIsProvider(); + + // Currently used only for GNSS locations - update permissions check if changed + if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) { + if (mGnssBatchingCallback == null) { + Slog.e(TAG, "reportLocationBatch() called without active Callback"); + return; + } + try { + mGnssBatchingCallback.onLocationBatch(locations); + } catch (RemoteException e) { + Slog.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e); + } + } else { + Slog.w(TAG, "reportLocationBatch() called without user permission, locations blocked"); + } + } + private void addProviderLocked(LocationProviderInterface provider) { mProviders.add(provider); mProvidersByName.put(provider.getName(), provider); @@ -2000,22 +2193,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) { - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, - LocationManager.GPS_PROVIDER); - - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long ident = Binder.clearCallingIdentity(); - try { - if (!checkLocationAccess(pid, uid, packageName, allowedResolutionLevel)) { - return false; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - if (mGnssStatusProvider == null) { + if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { return false; } @@ -2043,22 +2221,7 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean addGnssMeasurementsListener( IGnssMeasurementsListener listener, String packageName) { - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkResolutionLevelIsSufficientForProviderUse( - allowedResolutionLevel, - LocationManager.GPS_PROVIDER); - - int pid = Binder.getCallingPid(); - int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - boolean hasLocationAccess; - try { - hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); - } finally { - Binder.restoreCallingIdentity(identity); - } - - if (!hasLocationAccess || mGnssMeasurementsProvider == null) { + if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { return false; } return mGnssMeasurementsProvider.addListener(listener); @@ -2075,22 +2238,7 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName) { - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkResolutionLevelIsSufficientForProviderUse( - allowedResolutionLevel, - LocationManager.GPS_PROVIDER); - - int pid = Binder.getCallingPid(); - int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - boolean hasLocationAccess; - try { - hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); - } finally { - Binder.restoreCallingIdentity(identity); - } - - if (!hasLocationAccess || mGnssNavigationMessageProvider == null) { + if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { return false; } return mGnssNavigationMessageProvider.addListener(listener); @@ -2866,6 +3014,9 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(":"); provider.dump(fd, pw, args); } + if (mGnssBatchingInProgress) { + pw.println(" GNSS batching in progress"); + } } } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index eb8f8fc9af96..17b005d8c890 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -72,7 +72,6 @@ import android.os.WorkSource; import android.provider.Settings; import android.provider.Telephony.Carriers; import android.provider.Telephony.Sms.Intents; -import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; @@ -82,17 +81,17 @@ import android.text.TextUtils; import android.util.Log; import android.util.NtpTrustedTime; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; -import java.io.StringReader; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Map.Entry; import java.util.Properties; import java.util.Map; @@ -411,7 +410,6 @@ public class GnssLocationProvider implements LocationProviderInterface { private WorkSource mClientSource = new WorkSource(); private GeofenceHardwareImpl mGeofenceHardwareImpl; - private int mYearOfHardware = 0; // Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL @@ -1125,6 +1123,7 @@ public class GnssLocationProvider implements LocationProviderInterface { mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); + enableBatching(); } else { synchronized (mLock) { mEnabled = false; @@ -1156,6 +1155,7 @@ public class GnssLocationProvider implements LocationProviderInterface { mAlarmManager.cancel(mWakeupIntent); mAlarmManager.cancel(mTimeoutIntent); + disableBatching(); // do this before releasing wakelock native_cleanup(); @@ -1791,6 +1791,84 @@ public class GnssLocationProvider implements LocationProviderInterface { }; } + public interface GnssBatchingProvider { + /** + * Returns the GNSS batching size + */ + int getSize(); + /** + * Starts the hardware batching operation + */ + boolean start(long periodNanos, boolean wakeOnFifoFull); + /** + * Forces a flush of existing locations from the hardware batching + */ + void flush(); + /** + * Stops the batching operation + */ + boolean stop(); + } + + /** + * @hide + */ + public GnssBatchingProvider getGnssBatchingProvider() { + return new GnssBatchingProvider() { + @Override + public int getSize() { + return native_get_batch_size(); + } + @Override + public boolean start(long periodNanos, boolean wakeOnFifoFull) { + if (periodNanos <= 0) { + Log.e(TAG, "Invalid periodNanos " + periodNanos + + "in batching request, not started"); + return false; + } + return native_start_batch(periodNanos, wakeOnFifoFull); + } + @Override + public void flush() { + native_flush_batch(); + } + @Override + public boolean stop() { + return native_stop_batch(); + } + }; + } + + /** + * Initialize Batching if enabled + */ + private void enableBatching() { + if (!native_init_batching()) { + Log.e(TAG, "Failed to initialize GNSS batching"); + }; + } + + /** + * Disable batching + */ + private void disableBatching() { + native_stop_batch(); + native_cleanup_batching(); + } + + /** + * called from native code - GNSS location batch callback + */ + 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"); + } + } + /** * called from native code to request XTRA data */ @@ -2117,7 +2195,10 @@ public class GnssLocationProvider implements LocationProviderInterface { // note that this assumes the message will not be removed from the queue before // it is handled (otherwise the wake lock would be leaked). mWakeLock.acquire(); - Log.i(TAG, "WakeLock acquired by sendMessage(" + message + ", " + arg + ", " + obj + ")"); + if (Log.isLoggable(TAG, Log.INFO)) { + Log.i(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg + + ", " + obj + ")"); + } mHandler.obtainMessage(message, arg, 1, obj).sendToTarget(); } @@ -2175,8 +2256,10 @@ public class GnssLocationProvider implements LocationProviderInterface { if (msg.arg2 == 1) { // wakelock was taken for this message, release it mWakeLock.release(); - Log.i(TAG, "WakeLock released by handleMessage(" + message + ", " + msg.arg1 + ", " - + msg.obj + ")"); + if (Log.isLoggable(TAG, Log.INFO)) { + Log.i(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message) + + ", " + msg.arg1 + ", " + msg.obj + ")"); + } } } @@ -2424,6 +2507,40 @@ public class GnssLocationProvider implements LocationProviderInterface { } } + /** + * @return A string representing the given message ID. + */ + private String messageIdAsString(int message) { + switch (message) { + case ENABLE: + return "ENABLE"; + case SET_REQUEST: + return "SET_REQUEST"; + case UPDATE_NETWORK_STATE: + return "UPDATE_NETWORK_STATE"; + case REQUEST_SUPL_CONNECTION: + return "REQUEST_SUPL_CONNECTION"; + case RELEASE_SUPL_CONNECTION: + return "RELEASE_SUPL_CONNECTION"; + case INJECT_NTP_TIME: + return "INJECT_NTP_TIME"; + case DOWNLOAD_XTRA_DATA: + return "DOWNLOAD_XTRA_DATA"; + case INJECT_NTP_TIME_FINISHED: + return "INJECT_NTP_TIME_FINISHED"; + case DOWNLOAD_XTRA_DATA_FINISHED: + return "DOWNLOAD_XTRA_DATA_FINISHED"; + case UPDATE_LOCATION: + return "UPDATE_LOCATION"; + case SUBSCRIPTION_OR_SIM_CHANGED: + return "SUBSCRIPTION_OR_SIM_CHANGED"; + case INITIALIZE_HANDLER: + return "INITIALIZE_HANDLER"; + default: + return "<Unknown>"; + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { StringBuilder s = new StringBuilder(); @@ -2441,7 +2558,9 @@ public class GnssLocationProvider implements LocationProviderInterface { if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) s.append("NAV_MESSAGES "); s.append(")\n"); - s.append(native_get_internal_state()); + s.append(" internal state: ").append(native_get_internal_state()); + s.append("\n"); + pw.append(s); } @@ -2562,4 +2681,12 @@ public class GnssLocationProvider implements LocationProviderInterface { private static native boolean native_set_gps_lock(int gpsLock); private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); + // GNSS Batching + private static native int native_get_batch_size(); + private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull); + private static native void native_flush_batch(); + private static native boolean native_stop_batch(); + private static native boolean native_init_batching(); + private static native void native_cleanup_batching(); + } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 01a1efc3506e..1578562a4faf 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -58,6 +58,7 @@ static jmethodID method_reportGeofencePauseStatus; static jmethodID method_reportGeofenceResumeStatus; static jmethodID method_reportMeasurementData; static jmethodID method_reportNavigationMessages; +static jmethodID method_reportLocationBatch; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -80,6 +81,8 @@ using android::hardware::gnss::V1_0::IAGnssCallback; using android::hardware::gnss::V1_0::IAGnssRil; using android::hardware::gnss::V1_0::IAGnssRilCallback; using android::hardware::gnss::V1_0::IGnss; +using android::hardware::gnss::V1_0::IGnssBatching; +using android::hardware::gnss::V1_0::IGnssBatchingCallback; using android::hardware::gnss::V1_0::IGnssCallback; using android::hardware::gnss::V1_0::IGnssConfiguration; using android::hardware::gnss::V1_0::IGnssDebug; @@ -100,6 +103,7 @@ sp<IGnssXtra> gnssXtraIface = nullptr; sp<IAGnssRil> agnssRilIface = nullptr; sp<IGnssGeofencing> gnssGeofencingIface = nullptr; sp<IAGnss> agnssIface = nullptr; +sp<IGnssBatching> gnssBatchingIface = nullptr; sp<IGnssDebug> gnssDebugIface = nullptr; sp<IGnssConfiguration> gnssConfigurationIface = nullptr; sp<IGnssNi> gnssNiIface = nullptr; @@ -139,6 +143,7 @@ void JavaMethodHelper<T>::callJavaMethod( class JavaObject { public: JavaObject(JNIEnv* env, const char* class_name); + JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1); virtual ~JavaObject(); template<class T> @@ -159,6 +164,12 @@ JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) { object_ = env_->NewObject(clazz_, ctor); } +JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) { + clazz_ = env_->FindClass(class_name); + jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V"); + object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1)); +} + JavaObject::~JavaObject() { env_->DeleteLocalRef(clazz_); } @@ -591,6 +602,7 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessage); + checkAndClearExceptionFromCallback(env, __FUNCTION__); env->DeleteLocalRef(navigationMessage); return Void(); } @@ -925,6 +937,81 @@ Return<void> AGnssRilCallback::requestRefLocCb() { return Void(); } +/* + * GnssBatchingCallback interface implements the callback methods + * required by the IGnssBatching interface. + */ +struct GnssBatchingCallback : public IGnssBatchingCallback { + /* + * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback + * follow. + */ + Return<void> gnssLocationBatchCb( + const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) + override; + private: + jobject translateLocation( + JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location); +}; + +Return<void> GnssBatchingCallback::gnssLocationBatchCb( + const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) { + JNIEnv* env = getJniEnv(); + + jobjectArray jLocations = env->NewObjectArray(locations.size(), + env->FindClass("android/location/Location"), nullptr); + + for (uint16_t i = 0; i < locations.size(); ++i) { + jobject jLocation = translateLocation(env, &locations[i]); + env->SetObjectArrayElement(jLocations, i, jLocation); + env->DeleteLocalRef(jLocation); + } + + env->CallVoidMethod(mCallbacksObj, method_reportLocationBatch, jLocations); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + + env->DeleteLocalRef(jLocations); + + return Void(); +} + +// TODO: Use this common code to translate location for Geofencing and regular Location +jobject GnssBatchingCallback::translateLocation( + JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location) { + JavaObject object(env, "android/location/Location", "gps"); + + uint16_t flags = static_cast<uint32_t>(location->gnssLocationFlags); + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) { + SET(Latitude, location->latitudeDegrees); + SET(Longitude, location->longitudeDegrees); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) { + SET(Altitude, location->altitudeMeters); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) { + SET(Speed, location->speedMetersPerSec); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) { + SET(Bearing, location->bearingDegrees); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) { + SET(Accuracy, location->horizontalAccuracyMeters); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) { + SET(VerticalAccuracyMeters, location->verticalAccuracyMeters); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) { + SET(SpeedAccuracyMetersPerSecond, location->speedAccuracyMetersPerSecond); + } + if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) { + SET(BearingAccuracyDegrees, location->bearingAccuracyDegrees); + } + SET(Time, location->timestamp); + + return object.get(); +} + + static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFFFFJ)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); @@ -959,6 +1046,10 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, clazz, "reportNavigationMessage", "(Landroid/location/GnssNavigationMessage;)V"); + method_reportLocationBatch = env->GetMethodID( + clazz, + "reportLocationBatch", + "([Landroid/location/Location;)V"); /* * Save a pointer to JVM. @@ -1033,6 +1124,13 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, } else { gnssGeofencingIface = gnssGeofencing; } + + auto gnssBatching = gnssHal->getExtensionGnssBatching(); + if (!gnssBatching.isOk()) { + ALOGD("Unable to get a handle to gnssBatching"); + } else { + gnssBatchingIface = gnssBatching; + } } else { ALOGE("Unable to get GPS service\n"); } @@ -1704,6 +1802,67 @@ static jboolean android_location_GnssLocationProvider_set_gnss_pos_protocol_sele } } +static jint android_location_GnssLocationProvider_get_batch_size(JNIEnv*, jclass) { + if (gnssBatchingIface == nullptr) { + return 0; // batching not supported, size = 0 + } + auto result = gnssBatchingIface->getBatchSize(); + if (result.isOk()) { + return static_cast<jint>(result); + } else { + return 0; // failure in binder, don't support batching + } +} + +static jboolean android_location_GnssLocationProvider_init_batching(JNIEnv*, jclass) { + if (gnssBatchingIface == nullptr) { + return JNI_FALSE; // batching not supported + } + sp<IGnssBatchingCallback> gnssBatchingCbIface = new GnssBatchingCallback(); + + return static_cast<jboolean>(gnssBatchingIface->init(gnssBatchingCbIface)); +} + +static void android_location_GnssLocationProvider_cleanup_batching(JNIEnv*, jclass) { + if (gnssBatchingIface == nullptr) { + return; // batching not supported + } + gnssBatchingIface->cleanup(); +} + +static jboolean android_location_GnssLocationProvider_start_batch(JNIEnv*, jclass, + jlong periodNanos, jboolean wakeOnFifoFull) { + if (gnssBatchingIface == nullptr) { + return JNI_FALSE; // batching not supported + } + + IGnssBatching::Options options; + options.periodNanos = periodNanos; + if (wakeOnFifoFull) { + options.flags = static_cast<uint8_t>(IGnssBatching::Flag::WAKEUP_ON_FIFO_FULL); + } else { + options.flags = 0; + } + + return static_cast<jboolean>(gnssBatchingIface->start(options)); +} + +static void android_location_GnssLocationProvider_flush_batch(JNIEnv*, jclass) { + if (gnssBatchingIface == nullptr) { + return; // batching not supported + } + + gnssBatchingIface->flush(); +} + +static jboolean android_location_GnssLocationProvider_stop_batch(JNIEnv*, jclass) { + if (gnssBatchingIface == nullptr) { + return JNI_FALSE; // batching not supported + } + + return gnssBatchingIface->stop(); +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", reinterpret_cast<void *>( @@ -1829,6 +1988,27 @@ static const JNINativeMethod sMethods[] = { {"native_set_emergency_supl_pdn", "(I)Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)}, + {"native_get_batch_size", + "()I", + reinterpret_cast<void *>(android_location_GnssLocationProvider_get_batch_size)}, + {"native_init_batching", + "()Z", + reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)}, + {"native_start_batch", + "(JZ)Z", + reinterpret_cast<void *>(android_location_GnssLocationProvider_start_batch)}, + {"native_flush_batch", + "()V", + reinterpret_cast<void *>(android_location_GnssLocationProvider_flush_batch)}, + {"native_stop_batch", + "()Z", + reinterpret_cast<void *>(android_location_GnssLocationProvider_stop_batch)}, + {"native_init_batching", + "()Z", + reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)}, + {"native_cleanup_batching", + "()V", + reinterpret_cast<void *>(android_location_GnssLocationProvider_cleanup_batching)}, }; int register_android_server_location_GnssLocationProvider(JNIEnv* env) { |