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

Commit be93e9cc authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

Add location settings piercing in emergencies

Add new field to LocationRequest that allows clients to ignore location
settings, and validate this field. Support returning location to clients
with this field even when location settings are turned off.

Bug: 118883513
Test: Manual + CTS
Change-Id: Idf4449ae7c20d03f42410936c35fadc602e83d11
parent 6f19cbdd
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3209,12 +3209,14 @@ package android.location {
    method public int getQuality();
    method public float getSmallestDisplacement();
    method public android.os.WorkSource getWorkSource();
    method public boolean isLocationSettingsIgnored();
    method public boolean isLowPowerMode();
    method public android.location.LocationRequest setExpireAt(long);
    method public android.location.LocationRequest setExpireIn(long);
    method public android.location.LocationRequest setFastestInterval(long);
    method public void setHideFromAppOps(boolean);
    method public android.location.LocationRequest setInterval(long);
    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
    method public android.location.LocationRequest setLowPowerMode(boolean);
    method public android.location.LocationRequest setNumUpdates(int);
    method public android.location.LocationRequest setProvider(String);
+30 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.location;

import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -161,6 +163,7 @@ public final class LocationRequest implements Parcelable {
    private WorkSource mWorkSource = null;
    @UnsupportedAppUsage
    private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
    private boolean mLocationSettingsIgnored = false;

    @UnsupportedAppUsage
    private String mProvider = LocationManager.FUSED_PROVIDER;
@@ -261,6 +264,7 @@ public final class LocationRequest implements Parcelable {
        mWorkSource = src.mWorkSource;
        mHideFromAppOps = src.mHideFromAppOps;
        mLowPowerMode = src.mLowPowerMode;
        mLocationSettingsIgnored = src.mLocationSettingsIgnored;
    }

    /**
@@ -374,6 +378,32 @@ public final class LocationRequest implements Parcelable {
        return mLowPowerMode;
    }

    /**
     * Requests that user location settings be ignored in order to satisfy this request. This API
     * is only for use in extremely rare scenarios where it is appropriate to ignore user location
     * settings, such as a user initiated emergency (dialing 911 for instance).
     *
     * @param locationSettingsIgnored Whether to ignore location settings
     * @return the same object, so that setters can be chained
     * @hide
     */
    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
    @SystemApi
    public LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
        mLocationSettingsIgnored = locationSettingsIgnored;
        return this;
    }

    /**
     * Returns true if location settings will be ignored in order to satisfy this request.
     *
     * @hide
     */
    @SystemApi
    public boolean isLocationSettingsIgnored() {
        return mLocationSettingsIgnored;
    }

    /**
     * Explicitly set the fastest interval for location updates, in
     * milliseconds.
+89 −68
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -87,7 +86,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -246,7 +244,7 @@ public class LocationManagerService extends ILocationManager.Stub {
    public LocationManagerService(Context context) {
        super();
        mContext = context;
        mHandler = BackgroundThread.getHandler();
        mHandler = FgThread.getHandler();

        // Let the package manager query which are the default location
        // providers as they get certain permissions granted by default.
@@ -954,7 +952,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        public void onReportLocation(Location location) {
            // no security check necessary because this is coming from an internal-only interface
            // move calls coming from below LMS onto a different thread to avoid deadlock
            runInternal(() -> {
            mHandler.post(() -> {
                synchronized (mLock) {
                    handleLocationChangedLocked(location, this);
                }
@@ -965,7 +963,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        @Override
        public void onReportLocation(List<Location> locations) {
            // move calls coming from below LMS onto a different thread to avoid deadlock
            runInternal(() -> {
            mHandler.post(() -> {
                synchronized (mLock) {
                    LocationProvider gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
                    if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
@@ -991,7 +989,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        @Override
        public void onSetEnabled(boolean enabled) {
            // move calls coming from below LMS onto a different thread to avoid deadlock
            runInternal(() -> {
            mHandler.post(() -> {
                synchronized (mLock) {
                    if (enabled == mEnabled) {
                        return;
@@ -1113,16 +1111,6 @@ public class LocationManagerService extends ILocationManager.Stub {
            mUseable = false;
            updateProviderUseableLocked(this);
        }

        // binder transactions coming from below LMS (ie location providers) need to be moved onto
        // a different thread to avoid potential deadlock as code reenters the location providers
        private void runInternal(Runnable runnable) {
            if (Looper.myLooper() == mHandler.getLooper()) {
                runnable.run();
            } else {
                mHandler.post(runnable);
            }
        }
    }

    private class MockLocationProvider extends LocationProvider {
@@ -1278,7 +1266,11 @@ public class LocationManagerService extends ILocationManager.Stub {
                // are high power (has a high power provider with an interval under a threshold).
                for (UpdateRecord updateRecord : mUpdateRecords.values()) {
                    LocationProvider provider = getLocationProviderLocked(updateRecord.mProvider);
                    if (provider == null || !provider.isUseableLocked()) {
                    if (provider == null) {
                        continue;
                    }
                    if (!provider.isUseableLocked()
                            && !updateRecord.mRealRequest.isLocationSettingsIgnored()) {
                        continue;
                    }

@@ -1960,7 +1952,16 @@ public class LocationManagerService extends ILocationManager.Stub {
        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
        if (records != null) {
            for (UpdateRecord record : records) {
                if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                if (!isCurrentProfileLocked(
                        UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                    continue;
                }

                // requests that ignore location settings will never provider notifications
                if (record.mRealRequest.isLocationSettingsIgnored()) {
                    continue;
                }

                // Sends a notification message to the receiver
                if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) {
                    if (deadReceivers == null) {
@@ -1970,7 +1971,6 @@ public class LocationManagerService extends ILocationManager.Stub {
                }
            }
        }
        }

        if (deadReceivers != null) {
            for (int i = deadReceivers.size() - 1; i >= 0; i--) {
@@ -2007,16 +2007,26 @@ public class LocationManagerService extends ILocationManager.Stub {
            Binder.restoreCallingIdentity(identity);
        }

        if (provider.isUseableLocked() && records != null && !records.isEmpty()) {
        if (records != null && !records.isEmpty()) {
            // initialize the low power mode to true and set to false if any of the records requires
            providerRequest.lowPowerMode = true;
            for (UpdateRecord record : records) {
                if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                    if (checkLocationAccess(
                if (!isCurrentProfileLocked(
                        UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                    continue;
                }
                if (!checkLocationAccess(
                        record.mReceiver.mIdentity.mPid,
                        record.mReceiver.mIdentity.mUid,
                        record.mReceiver.mIdentity.mPackageName,
                        record.mReceiver.mAllowedResolutionLevel)) {
                    continue;
                }
                if (!provider.isUseableLocked()
                        && !record.mRealRequest.isLocationSettingsIgnored()) {
                    continue;
                }

                LocationRequest locationRequest = record.mRealRequest;
                long interval = locationRequest.getInterval();

@@ -2040,8 +2050,6 @@ public class LocationManagerService extends ILocationManager.Stub {
                    providerRequest.interval = interval;
                }
            }
                }
            }

            if (providerRequest.reportLocation) {
                // calculate who to blame for power
@@ -2307,6 +2315,10 @@ public class LocationManagerService extends ILocationManager.Stub {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.UPDATE_APP_OPS_STATS, null);
            }
            if (request.isLocationSettingsIgnored()) {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.WRITE_SECURE_SETTINGS, null);
            }
            boolean callerHasLocationHardwarePermission =
                    mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
                            == PERMISSION_GRANTED;
@@ -2376,12 +2388,14 @@ public class LocationManagerService extends ILocationManager.Stub {
            oldRecord.disposeLocked(false);
        }

        if (provider.isUseableLocked()) {
            applyRequirementsLocked(name);
        } else {
            // Notify the listener that updates are currently disabled
        if (!provider.isUseableLocked() && !request.isLocationSettingsIgnored()) {
            // Notify the listener that updates are currently disabled - but only if the request
            // does not ignore location settings
            receiver.callProviderEnabledLocked(name, false);
        }

        applyRequirementsLocked(name);

        // Update the monitoring here just in case multiple location requests were added to the
        // same receiver (this request may be high power and the initial might not have been).
        receiver.updateMonitoring(true);
@@ -2981,27 +2995,30 @@ public class LocationManagerService extends ILocationManager.Stub {
            return;
        }

        // only notify passive provider and update last location for locations that come from
        // useable providers
        if (provider.isUseableLocked()) {
            if (!provider.isPassiveLocked()) {
            // notify passive provider of the new location
                mPassiveProvider.updateLocation(location);
            }
        }

        if (D) Log.d(TAG, "incoming location: " + location);
        long now = SystemClock.elapsedRealtime();
        if (provider.isUseableLocked()) {
            updateLastLocationLocked(location, provider.getName());
        // mLastLocation should have been updated from the updateLastLocationLocked call above.
        Location lastLocation = mLastLocation.get(provider.getName());
        if (lastLocation == null) {
            Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
            return;
        }

        // Update last known coarse interval location if enough time has passed.
        Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider.getName());
        Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(
                provider.getName());
        if (lastLocationCoarseInterval == null) {
            lastLocationCoarseInterval = new Location(location);

            if (provider.isUseableLocked()) {
                mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
            }
        }
        long timeDiffNanos = location.getElapsedRealtimeNanos()
                - lastLocationCoarseInterval.getElapsedRealtimeNanos();
        if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
@@ -3031,6 +3048,10 @@ public class LocationManagerService extends ILocationManager.Stub {
            Receiver receiver = r.mReceiver;
            boolean receiverDead = false;

            if (!provider.isUseableLocked() && !r.mRealRequest.isLocationSettingsIgnored()) {
                continue;
            }

            int receiverUserId = UserHandle.getUserId(receiver.mIdentity.mUid);
            if (!isCurrentProfileLocked(receiverUserId)
                    && !isLocationProviderLocked(receiver.mIdentity.mUid)) {
@@ -3066,7 +3087,7 @@ public class LocationManagerService extends ILocationManager.Stub {
            if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
                notifyLocation = coarseLocation;  // use coarse location
            } else {
                notifyLocation = lastLocation;  // use fine location
                notifyLocation = location;  // use fine location
            }
            if (notifyLocation != null) {
                Location lastLoc = r.mLastFixBroadcast;