Add a hidden API to inject location.
New API allows system clients with LOCATION_HARDWARE and ACCESS_FINE_LOCATION to inject a location to the Location Manager. This is useful in products like Auto, where the location needs to be cached across reboots. Bug: b/64125396 Test: Foll. were tested: 1. location can be injected when lastLocation is not available 2. location cannot be injected when lastLocation is already available Also, tested location availability on different devices for sanity. Change-Id: I1fd060caafed0431074ae64439ac52e99e0d6032
This commit is contained in:
@@ -79,6 +79,7 @@ interface ILocationManager
|
||||
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
|
||||
void flushGnssBatch(String packageName);
|
||||
boolean stopGnssBatch();
|
||||
boolean injectLocation(in Location location);
|
||||
|
||||
// --- deprecated ---
|
||||
List<String> getAllProviders();
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.List;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
|
||||
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
||||
import static android.Manifest.permission.LOCATION_HARDWARE;
|
||||
|
||||
/**
|
||||
* This class provides access to the system location services. These
|
||||
@@ -882,6 +883,34 @@ public class LocationManager {
|
||||
requestLocationUpdates(request, null, null, intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last known location with a new location.
|
||||
*
|
||||
* <p>A privileged client can inject a {@link Location} if it has a better estimate of what
|
||||
* the recent location is. This is especially useful when the device boots up and the GPS
|
||||
* chipset is in the process of getting the first fix. If the client has cached the location,
|
||||
* it can inject the {@link Location}, so if an app requests for a {@link Location} from {@link
|
||||
* #getLastKnownLocation(String)}, the location information is still useful before getting
|
||||
* the first fix.</p>
|
||||
*
|
||||
* <p> Useful in products like Auto.
|
||||
*
|
||||
* @param newLocation newly available {@link Location} object
|
||||
* @return true if update was successful, false if not
|
||||
*
|
||||
* @throws SecurityException if no suitable permission is present
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
|
||||
public boolean injectLocation(Location newLocation) {
|
||||
try {
|
||||
return mService.injectLocation(newLocation);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
|
||||
if (listener == null) return null;
|
||||
synchronized (mListeners) {
|
||||
|
||||
@@ -2254,6 +2254,64 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an interface to inject and set the last location if location is not available
|
||||
* currently.
|
||||
*
|
||||
* This helps in cases where the product (Cars for example) has saved the last known location
|
||||
* before powering off. This interface lets the client inject the saved location while the GPS
|
||||
* chipset is getting its first fix, there by improving user experience.
|
||||
*
|
||||
* @param location - Location object to inject
|
||||
* @return true if update was successful, false if not
|
||||
*/
|
||||
@Override
|
||||
public boolean injectLocation(Location location) {
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
|
||||
"Location Hardware permission not granted to inject location");
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
"Access Fine Location permission not granted to inject Location");
|
||||
|
||||
if (location == null) {
|
||||
if (D) {
|
||||
Log.d(TAG, "injectLocation(): called with null location");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
LocationProviderInterface p = null;
|
||||
String provider = location.getProvider();
|
||||
if (provider != null) {
|
||||
p = mProvidersByName.get(provider);
|
||||
}
|
||||
if (p == null) {
|
||||
if (D) {
|
||||
Log.d(TAG, "injectLocation(): unknown provider");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!isAllowedByCurrentUserSettingsLocked(provider)) {
|
||||
if (D) {
|
||||
Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// NOTE: If last location is already available, location is not injected. If
|
||||
// provider's normal source (like a GPS chipset) have already provided an output,
|
||||
// there is no need to inject this location.
|
||||
if (mLastLocation.get(provider) == null) {
|
||||
updateLastLocationLocked(location, provider);
|
||||
} else {
|
||||
if (D) {
|
||||
Log.d(TAG, "injectLocation(): Location exists. Not updating");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
|
||||
String packageName) {
|
||||
@@ -2614,30 +2672,18 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
private void handleLocationChangedLocked(Location location, boolean passive) {
|
||||
if (D) Log.d(TAG, "incoming location: " + location);
|
||||
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
|
||||
|
||||
// Skip if the provider is unknown.
|
||||
LocationProviderInterface p = mProvidersByName.get(provider);
|
||||
if (p == null) return;
|
||||
|
||||
// Update last known locations
|
||||
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
|
||||
Location lastNoGPSLocation;
|
||||
updateLastLocationLocked(location, provider);
|
||||
// mLastLocation should have been updated from the updateLastLocationLocked call above.
|
||||
Location lastLocation = mLastLocation.get(provider);
|
||||
if (lastLocation == null) {
|
||||
lastLocation = new Location(provider);
|
||||
mLastLocation.put(provider, lastLocation);
|
||||
} else {
|
||||
lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
|
||||
if (noGPSLocation == null && lastNoGPSLocation != null) {
|
||||
// New location has no no-GPS location: adopt last no-GPS location. This is set
|
||||
// directly into location because we do not want to notify COARSE clients.
|
||||
location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
|
||||
}
|
||||
Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
|
||||
return;
|
||||
}
|
||||
lastLocation.set(location);
|
||||
|
||||
// Update last known coarse interval location if enough time has passed.
|
||||
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
|
||||
@@ -2653,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
// Don't ever return a coarse location that is more recent than the allowed update
|
||||
// interval (i.e. don't allow an app to keep registering and unregistering for
|
||||
// location updates to overcome the minimum interval).
|
||||
noGPSLocation =
|
||||
Location noGPSLocation =
|
||||
lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
|
||||
|
||||
// Skip if there are no UpdateRecords for this provider.
|
||||
@@ -2778,6 +2824,30 @@ 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
|
||||
*/
|
||||
private void updateLastLocationLocked(Location location, String provider) {
|
||||
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
|
||||
Location lastNoGPSLocation;
|
||||
Location lastLocation = mLastLocation.get(provider);
|
||||
if (lastLocation == null) {
|
||||
lastLocation = new Location(provider);
|
||||
mLastLocation.put(provider, lastLocation);
|
||||
} else {
|
||||
lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
|
||||
if (noGPSLocation == null && lastNoGPSLocation != null) {
|
||||
// New location has no no-GPS location: adopt last no-GPS location. This is set
|
||||
// directly into location because we do not want to notify COARSE clients.
|
||||
location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
|
||||
}
|
||||
}
|
||||
lastLocation.set(location);
|
||||
}
|
||||
|
||||
private class LocationWorkerHandler extends Handler {
|
||||
public LocationWorkerHandler(Looper looper) {
|
||||
super(looper, null, true);
|
||||
|
||||
Reference in New Issue
Block a user