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

Commit 8fa8deee authored by Soonil Nagarkar's avatar Soonil Nagarkar Committed by Android (Google) Code Review
Browse files

Merge "Allow LOCATION_BYPASS permission to be used for location" into main

parents 57fdb8f6 a4e75a93
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -7,6 +7,13 @@ flag {
    bug: "229872126"
}

flag {
    name: "enable_location_bypass"
    namespace: "location"
    description: "Enable location bypass feature"
    bug: "301150056"
}

flag {
    name: "location_bypass"
    is_exported: true
+55 −8
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.location;

import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.LOCATION_BYPASS;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -34,6 +35,7 @@ import static android.location.provider.LocationProviderBase.ACTION_NETWORK_PROV

import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;

import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -73,6 +75,7 @@ import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.location.flags.Flags;
import android.location.provider.ForwardGeocodeRequest;
import android.location.provider.IGeocodeCallback;
import android.location.provider.IProviderRequestListener;
@@ -776,8 +779,19 @@ public class LocationManagerService extends ILocationManager.Stub implements
                listenerId);
        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
                identity.getPid());
        if (Flags.enableLocationBypass()) {
            if (permissionLevel == PERMISSION_NONE) {
                if (mContext.checkCallingPermission(LOCATION_BYPASS) != PERMISSION_GRANTED) {
                    LocationPermissions.enforceLocationPermission(
                            identity.getUid(), permissionLevel, PERMISSION_COARSE);
                } else {
                    permissionLevel = PERMISSION_FINE;
                }
            }
        } else {
            LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                    PERMISSION_COARSE);
        }

        // clients in the system process must have an attribution tag set
        Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
@@ -805,8 +819,19 @@ public class LocationManagerService extends ILocationManager.Stub implements
                listenerId);
        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
                identity.getPid());
        if (Flags.enableLocationBypass()) {
            if (permissionLevel == PERMISSION_NONE) {
                if (mContext.checkCallingPermission(LOCATION_BYPASS) != PERMISSION_GRANTED) {
                    LocationPermissions.enforceLocationPermission(
                            identity.getUid(), permissionLevel, PERMISSION_COARSE);
                } else {
                    permissionLevel = PERMISSION_FINE;
                }
            }
        } else {
            LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                    PERMISSION_COARSE);
        }

        // clients in the system process should have an attribution tag set
        if (identity.getPid() == Process.myPid() && attributionTag == null) {
@@ -830,8 +855,19 @@ public class LocationManagerService extends ILocationManager.Stub implements
                AppOpsManager.toReceiverId(pendingIntent));
        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
                identity.getPid());
        if (Flags.enableLocationBypass()) {
            if (permissionLevel == PERMISSION_NONE) {
                if (mContext.checkCallingPermission(LOCATION_BYPASS) != PERMISSION_GRANTED) {
                    LocationPermissions.enforceLocationPermission(
                            identity.getUid(), permissionLevel, PERMISSION_COARSE);
                } else {
                    permissionLevel = PERMISSION_FINE;
                }
            }
        } else {
            LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                    PERMISSION_COARSE);
        }

        // clients in the system process must have an attribution tag set
        Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
@@ -982,8 +1018,19 @@ public class LocationManagerService extends ILocationManager.Stub implements
        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
                identity.getPid());
        if (Flags.enableLocationBypass()) {
            if (permissionLevel == PERMISSION_NONE) {
                if (mContext.checkCallingPermission(LOCATION_BYPASS) != PERMISSION_GRANTED) {
                    LocationPermissions.enforceLocationPermission(
                            identity.getUid(), permissionLevel, PERMISSION_COARSE);
                } else {
                    permissionLevel = PERMISSION_FINE;
                }
            }
        } else {
            LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                    PERMISSION_COARSE);
        }

        // clients in the system process must have an attribution tag set
        Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
+87 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.location.provider;

import static android.Manifest.permission.LOCATION_BYPASS;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.compat.CompatChanges.isChangeEnabled;
@@ -51,6 +52,7 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AlarmManager.OnAlarmListener;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.Context;
@@ -66,6 +68,7 @@ import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
import android.location.altitude.AltitudeConverter;
import android.location.flags.Flags;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -106,6 +109,7 @@ import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.AppForegroundHelper.AppForegroundListener;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
@@ -375,8 +379,13 @@ public class LocationProviderManager extends
        // we cache these values because checking/calculating on the fly is more expensive
        @GuardedBy("mMultiplexerLock")
        private boolean mPermitted;

        @GuardedBy("mMultiplexerLock")
        private boolean mBypassPermitted;

        @GuardedBy("mMultiplexerLock")
        private boolean mForeground;

        @GuardedBy("mMultiplexerLock")
        private LocationRequest mProviderLocationRequest;
        @GuardedBy("mMultiplexerLock")
@@ -421,8 +430,8 @@ public class LocationProviderManager extends
            EVENT_LOG.logProviderClientRegistered(mName, getIdentity(), mBaseRequest);

            // initialization order is important as there are ordering dependencies
            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
                    getIdentity());
            onLocationPermissionsChanged();
            onBypassLocationPermissionsChanged(mEmergencyHelper.isInEmergency(0));
            mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
            mProviderLocationRequest = calculateProviderLocationRequest();
            mIsUsingHighPower = isUsingHighPower();
@@ -491,7 +500,13 @@ public class LocationProviderManager extends

        public final boolean isPermitted() {
            synchronized (mMultiplexerLock) {
                return mPermitted;
                return mPermitted || mBypassPermitted;
            }
        }

        public final boolean isOnlyBypassPermitted() {
            synchronized (mMultiplexerLock) {
                return mBypassPermitted && !mPermitted;
            }
        }

@@ -562,6 +577,33 @@ public class LocationProviderManager extends
            }
        }

        boolean onBypassLocationPermissionsChanged(boolean isInEmergency) {
            synchronized (mMultiplexerLock) {
                boolean bypassPermitted =
                        Flags.enableLocationBypass() && isInEmergency
                                && mContext.checkPermission(
                                LOCATION_BYPASS, mIdentity.getPid(), mIdentity.getUid())
                                == PERMISSION_GRANTED;
                if (mBypassPermitted != bypassPermitted) {
                    if (D) {
                        Log.v(
                                TAG,
                                mName
                                        + " provider package "
                                        + getIdentity().getPackageName()
                                        + " bypass permitted = "
                                        + bypassPermitted);
                    }

                    mBypassPermitted = bypassPermitted;

                    return true;
                }

                return false;
            }
        }

        @GuardedBy("mMultiplexerLock")
        private boolean onLocationPermissionsChanged() {
            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -941,8 +983,11 @@ public class LocationProviderManager extends
            }

            // note app ops
            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
                    getIdentity())) {
            int op =
                    Flags.enableLocationBypass() && isOnlyBypassPermitted()
                            ? AppOpsManager.OP_EMERGENCY_LOCATION
                            : LocationPermissions.asAppOp(getPermissionLevel());
            if (!mAppOpsHelper.noteOpNoThrow(op, getIdentity())) {
                if (D) {
                    Log.w(TAG,
                            mName + " provider registration " + getIdentity() + " noteOp denied");
@@ -1292,13 +1337,18 @@ public class LocationProviderManager extends
            }

            // lastly - note app ops
            if (fineLocationResult != null && !mAppOpsHelper.noteOpNoThrow(
                    LocationPermissions.asAppOp(getPermissionLevel()), getIdentity())) {
            if (fineLocationResult != null) {
                int op =
                        Flags.enableLocationBypass() && isOnlyBypassPermitted()
                                ? AppOpsManager.OP_EMERGENCY_LOCATION
                                : LocationPermissions.asAppOp(getPermissionLevel());
                if (!mAppOpsHelper.noteOpNoThrow(op, getIdentity())) {
                    if (D) {
                        Log.w(TAG, "noteOp denied for " + getIdentity());
                    }
                    fineLocationResult = null;
                }
            }

            if (fineLocationResult != null) {
                fineLocationResult = fineLocationResult.asLastLocationResult();
@@ -1399,6 +1449,7 @@ public class LocationProviderManager extends
    protected final ScreenInteractiveHelper mScreenInteractiveHelper;
    protected final LocationUsageLogger mLocationUsageLogger;
    protected final LocationFudger mLocationFudger;
    protected final EmergencyHelper mEmergencyHelper;
    private final PackageResetHelper mPackageResetHelper;

    private final UserListener mUserChangedListener = this::onUserChanged;
@@ -1434,6 +1485,8 @@ public class LocationProviderManager extends
            this::onLocationPowerSaveModeChanged;
    private final ScreenInteractiveChangedListener mScreenInteractiveChangedListener =
            this::onScreenInteractiveChanged;
    private final EmergencyHelper.EmergencyStateChangedListener mEmergencyStateChangedListener =
            this::onEmergencyStateChanged;
    private final PackageResetHelper.Responder mPackageResetResponder =
            new PackageResetHelper.Responder() {
                @Override
@@ -1507,6 +1560,7 @@ public class LocationProviderManager extends
        mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
        mLocationUsageLogger = injector.getLocationUsageLogger();
        mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
        mEmergencyHelper = injector.getEmergencyHelper();
        mPackageResetHelper = injector.getPackageResetHelper();

        mProvider = new MockableLocationProvider(mMultiplexerLock);
@@ -1757,8 +1811,17 @@ public class LocationProviderManager extends

        if (location != null) {
            // lastly - note app ops
            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
                    identity)) {
            int op =
                    (Flags.enableLocationBypass()
                            && !mLocationPermissionsHelper.hasLocationPermissions(
                                    permissionLevel, identity)
                            && mEmergencyHelper.isInEmergency(0)
                            && mContext.checkPermission(
                                    LOCATION_BYPASS, identity.getPid(), identity.getUid())
                            == PERMISSION_GRANTED)
                            ? AppOpsManager.OP_EMERGENCY_LOCATION
                            : LocationPermissions.asAppOp(permissionLevel);
            if (!mAppOpsHelper.noteOpNoThrow(op, identity)) {
                return null;
            }

@@ -2069,6 +2132,9 @@ public class LocationProviderManager extends
        mAppForegroundHelper.addListener(mAppForegroundChangedListener);
        mLocationPowerSaveModeHelper.addListener(mLocationPowerSaveModeChangedListener);
        mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener);
        if (Flags.enableLocationBypass()) {
            mEmergencyHelper.addOnEmergencyStateChangedListener(mEmergencyStateChangedListener);
        }
        mPackageResetHelper.register(mPackageResetResponder);
    }

@@ -2088,6 +2154,9 @@ public class LocationProviderManager extends
        mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
        mLocationPowerSaveModeHelper.removeListener(mLocationPowerSaveModeChangedListener);
        mScreenInteractiveHelper.removeListener(mScreenInteractiveChangedListener);
        if (Flags.enableLocationBypass()) {
            mEmergencyHelper.removeOnEmergencyStateChangedListener(mEmergencyStateChangedListener);
        }
        mPackageResetHelper.unregister(mPackageResetResponder);
    }

@@ -2466,6 +2535,12 @@ public class LocationProviderManager extends
        }
    }

    private void onEmergencyStateChanged() {
        boolean inEmergency = mEmergencyHelper.isInEmergency(0);
        updateRegistrations(
                registration -> registration.onBypassLocationPermissionsChanged(inEmergency));
    }

    private void onBackgroundThrottlePackageWhitelistChanged() {
        updateRegistrations(Registration::onProviderLocationRequestChanged);
    }
+1 −1
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ public class TestInjector implements Injector {
    }

    @Override
    public EmergencyHelper getEmergencyHelper() {
    public FakeEmergencyHelper getEmergencyHelper() {
        return mEmergencyHelper;
    }

+60 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.location.provider;

import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.LOCATION_BYPASS;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
@@ -1169,6 +1172,63 @@ public class LocationProviderManagerTest {
        assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
    }

    @Test
    public void testProviderRequest_IgnoreLocationSettings_LocationBypass() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LOCATION_BYPASS);

        doReturn(PackageManager.PERMISSION_GRANTED)
                .when(mContext)
                .checkPermission(LOCATION_BYPASS, IDENTITY.getPid(), IDENTITY.getUid());
        mInjector.getLocationPermissionsHelper()
                .revokePermission(IDENTITY.getPackageName(), ACCESS_FINE_LOCATION);
        mInjector.getLocationPermissionsHelper()
                .revokePermission(IDENTITY.getPackageName(), ACCESS_COARSE_LOCATION);
        mInjector
                .getSettingsHelper()
                .setIgnoreSettingsAllowlist(
                        new PackageTagsList.Builder().add(IDENTITY.getPackageName()).build());

        ILocationListener listener = createMockLocationListener();
        LocationRequest request =
                new LocationRequest.Builder(1)
                        .setLocationSettingsIgnored(true)
                        .setWorkSource(WORK_SOURCE)
                        .build();
        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);

        assertThat(mProvider.getRequest().isActive()).isFalse();
    }

    @Test
    public void testProviderRequest_IgnoreLocationSettings_LocationBypass_EmergencyCall() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LOCATION_BYPASS);

        doReturn(PackageManager.PERMISSION_GRANTED)
                .when(mContext)
                .checkPermission(LOCATION_BYPASS, IDENTITY.getPid(), IDENTITY.getUid());
        mInjector.getLocationPermissionsHelper()
                .revokePermission(IDENTITY.getPackageName(), ACCESS_FINE_LOCATION);
        mInjector.getLocationPermissionsHelper()
                .revokePermission(IDENTITY.getPackageName(), ACCESS_COARSE_LOCATION);
        mInjector.getEmergencyHelper().setInEmergency(true);
        mInjector
                .getSettingsHelper()
                .setIgnoreSettingsAllowlist(
                        new PackageTagsList.Builder().add(IDENTITY.getPackageName()).build());

        ILocationListener listener = createMockLocationListener();
        LocationRequest request =
                new LocationRequest.Builder(1)
                        .setLocationSettingsIgnored(true)
                        .setWorkSource(WORK_SOURCE)
                        .build();
        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);

        assertThat(mProvider.getRequest().isActive()).isTrue();
        assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
        assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
    }

    @Test
    public void testProviderRequest_BackgroundThrottle_IgnoreLocationSettings() {
        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(