Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 183beb34 authored by Ram Periathiruvadi's avatar Ram Periathiruvadi Committed by Android (Google) Code Review
Browse files

Merge "Add a hidden API to inject location."

parents 373ea905 8671feab
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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();
+29 −0
Original line number Diff line number Diff line
@@ -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) {
+87 −17
Original line number Diff line number Diff line
@@ -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);