Merge "GNSS Batching - Default Implementation"

This commit is contained in:
Wyatt Riley
2017-02-08 02:08:01 +00:00
committed by Android (Google) Code Review
11 changed files with 777 additions and 61 deletions

View File

@@ -388,6 +388,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 \

View File

@@ -21413,6 +21413,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);
@@ -21951,14 +21956,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);
@@ -21989,6 +21997,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);

View File

@@ -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) {}
}

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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
@@ -1959,6 +1967,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

View File

@@ -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");
}
}
}
}

View File

@@ -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();
}

View File

@@ -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) {