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

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

Refactor background/system user behavior

This is a breaking change so that providers are no longer disabled for
non-current (ie background users). Instead, location requests from
background users are now considered inactive. This brings provider
behavior more in line with other APIs (such as GNSS and geofencing),
and allows us to special case requests from the system UID.

Without this change, the system UID would see incorrect provider
enabled/disabled notifications. Impact on clients is expected to be
minimal, as by far the vast majority of android applications run on
single user devices. For multiuser devices, applications will no longer
see enabled/disabled notifications in response to current user changes.

Bug: 157682495
Test: manual + presubmit
Change-Id: I0edb15f0792bf1426855baefcda8477ba3a32b28
parent f20b5f03
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -150,6 +150,11 @@ public final class CallerIdentity {
        return mListenerId;
    }

    /** Returns true if this represents a system identity. */
    public boolean isSystem() {
        return mUid == Process.SYSTEM_UID;
    }

    /**
     * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a
     * new worksource representing this identity.
+47 −42
Original line number Diff line number Diff line
@@ -1004,7 +1004,7 @@ class LocationProviderManager extends

                // if the provider is currently disabled fail immediately
                int userId = getIdentity().getUserId();
                if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
                if (!isEnabled(userId)) {
                    deliverLocation(null);
                }
            }
@@ -1165,7 +1165,7 @@ class LocationProviderManager extends

    protected final LocationManagerInternal mLocationManagerInternal;
    protected final SettingsHelper mSettingsHelper;
    protected final UserInfoHelper mUserInfoHelper;
    protected final UserInfoHelper mUserHelper;
    protected final AlarmHelper mAlarmHelper;
    protected final AppOpsHelper mAppOpsHelper;
    protected final LocationPermissionsHelper mLocationPermissionsHelper;
@@ -1227,7 +1227,7 @@ class LocationProviderManager extends
        mLocationManagerInternal = Objects.requireNonNull(
                LocalServices.getService(LocationManagerInternal.class));
        mSettingsHelper = injector.getSettingsHelper();
        mUserInfoHelper = injector.getUserInfoHelper();
        mUserHelper = injector.getUserInfoHelper();
        mAlarmHelper = injector.getAlarmHelper();
        mAppOpsHelper = injector.getAppOpsHelper();
        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
@@ -1252,7 +1252,7 @@ class LocationProviderManager extends
        synchronized (mLock) {
            mStarted = true;

            mUserInfoHelper.addListener(mUserChangedListener);
            mUserHelper.addListener(mUserChangedListener);
            mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);

            long identity = Binder.clearCallingIdentity();
@@ -1267,20 +1267,19 @@ class LocationProviderManager extends

    public void stopManager() {
        synchronized (mLock) {
            mUserInfoHelper.removeListener(mUserChangedListener);
            mUserHelper.removeListener(mUserChangedListener);
            mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);

            // notify and remove all listeners
            mStarted = false;

            long identity = Binder.clearCallingIdentity();
            try {
                onUserStopped(UserHandle.USER_ALL);
                onEnabledChanged(UserHandle.USER_ALL);
                removeRegistrationIf(key -> true);
                mEnabledListeners.clear();
            } finally {
                Binder.restoreCallingIdentity(identity);
            }

            mEnabledListeners.clear();
            mStarted = false;
        }
    }

@@ -1430,12 +1429,14 @@ class LocationProviderManager extends
                identity.getPackageName())) {
            return null;
        }
        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
        if (!ignoreLocationSettings) {
            if (!isEnabled(identity.getUserId())) {
                return null;
            }
        if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) {
            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
                return null;
            }
        }

        Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel,
                ignoreLocationSettings, Long.MAX_VALUE);
@@ -1462,7 +1463,7 @@ class LocationProviderManager extends
        if (userId == UserHandle.USER_ALL) {
            // find the most recent location across all users
            Location lastLocation = null;
            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
            final int[] runningUserIds = mUserHelper.getRunningUserIds();
            for (int i = 0; i < runningUserIds.length; i++) {
                Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
                        ignoreLocationSettings, maximumAgeMs);
@@ -1507,7 +1508,7 @@ class LocationProviderManager extends

    private void setLastLocation(Location location, int userId) {
        if (userId == UserHandle.USER_ALL) {
            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
            final int[] runningUserIds = mUserHelper.getRunningUserIds();
            for (int i = 0; i < runningUserIds.length; i++) {
                setLastLocation(location, runningUserIds[i]);
            }
@@ -1533,7 +1534,7 @@ class LocationProviderManager extends

    @Nullable
    public ICancellationSignal getCurrentLocation(LocationRequest request,
            CallerIdentity callerIdentity, int permissionLevel, ILocationCallback callback) {
            CallerIdentity identity, int permissionLevel, ILocationCallback callback) {
        if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
            request = new LocationRequest.Builder(request)
                    .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
@@ -1543,27 +1544,31 @@ class LocationProviderManager extends
        GetCurrentLocationListenerRegistration registration =
                new GetCurrentLocationListenerRegistration(
                        request,
                        callerIdentity,
                        identity,
                        new GetCurrentLocationTransport(callback),
                        permissionLevel);

        synchronized (mLock) {
            if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(),
                    callerIdentity.getPackageName())) {
            // shortcut various failure conditions so that we can return immediately rather than
            // waiting for location to timeout
            if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                    identity.getPackageName())) {
                registration.deliverLocation(null);
                return null;
            }
            if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) {
            if (!request.isLocationSettingsIgnored()) {
                if (!isEnabled(identity.getUserId())) {
                    registration.deliverLocation(null);
                    return null;
                }
            if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) {
                if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
                    registration.deliverLocation(null);
                    return null;
                }
            }

            Location lastLocation = getLastLocationUnsafe(
                    callerIdentity.getUserId(),
                    identity.getUserId(),
                    permissionLevel,
                    request.isLocationSettingsIgnored(),
                    MAX_CURRENT_LOCATION_AGE_MS);
@@ -1573,11 +1578,11 @@ class LocationProviderManager extends
            }

            // if last location isn't good enough then we add a location request
            long identity = Binder.clearCallingIdentity();
            long ident = Binder.clearCallingIdentity();
            try {
                addRegistration(callback.asBinder(), registration);
            } finally {
                Binder.restoreCallingIdentity(identity);
                Binder.restoreCallingIdentity(ident);
            }
        }

@@ -1601,20 +1606,20 @@ class LocationProviderManager extends
        }
    }

    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
            @PermissionLevel int permissionLevel, ILocationListener listener) {
        synchronized (mLock) {
            long identity = Binder.clearCallingIdentity();
            long ident = Binder.clearCallingIdentity();
            try {
                addRegistration(
                        listener.asBinder(),
                        new LocationListenerRegistration(
                                request,
                                callerIdentity,
                                identity,
                                new LocationListenerTransport(listener),
                                permissionLevel));
            } finally {
                Binder.restoreCallingIdentity(identity);
                Binder.restoreCallingIdentity(ident);
            }
        }
    }
@@ -1845,6 +1850,9 @@ class LocationProviderManager extends
            if (!isEnabled(identity.getUserId())) {
                return false;
            }
            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
                return false;
            }

            switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
                case LOCATION_MODE_FOREGROUND_ONLY:
@@ -1973,7 +1981,8 @@ class LocationProviderManager extends
        synchronized (mLock) {
            switch (change) {
                case UserListener.CURRENT_USER_CHANGED:
                    onEnabledChanged(userId);
                    updateRegistrations(
                            registration -> registration.getIdentity().getUserId() == userId);
                    break;
                case UserListener.USER_STARTED:
                    onUserStarted(userId);
@@ -2149,13 +2158,10 @@ class LocationProviderManager extends
        }

        if (userId == UserHandle.USER_ALL) {
            onEnabledChanged(UserHandle.USER_ALL);
            mEnabled.clear();
            mLastLocations.clear();
        } else {
            Preconditions.checkArgument(userId >= 0);

            onEnabledChanged(userId);
            mEnabled.delete(userId);
            mLastLocations.remove(userId);
        }
@@ -2172,7 +2178,7 @@ class LocationProviderManager extends
            // settings for instance) do not support the null user
            return;
        } else if (userId == UserHandle.USER_ALL) {
            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
            final int[] runningUserIds = mUserHelper.getRunningUserIds();
            for (int i = 0; i < runningUserIds.length; i++) {
                onEnabledChanged(runningUserIds[i]);
            }
@@ -2183,7 +2189,6 @@ class LocationProviderManager extends

        boolean enabled = mStarted
                && mProvider.getState().allowed
                && mUserInfoHelper.isCurrentUserId(userId)
                && mSettingsHelper.isLocationEnabled(userId);

        int index = mEnabled.indexOfKey(userId);
@@ -2247,7 +2252,7 @@ class LocationProviderManager extends

            super.dump(fd, ipw, args);

            int[] userIds = mUserInfoHelper.getRunningUserIds();
            int[] userIds = mUserHelper.getRunningUserIds();
            for (int userId : userIds) {
                if (userIds.length != 1) {
                    ipw.print("user ");
+1 −1
Original line number Diff line number Diff line
@@ -327,7 +327,7 @@ public class GeofenceManager extends
    protected boolean isActive(GeofenceRegistration registration) {
        CallerIdentity identity = registration.getIdentity();
        return registration.isPermitted()
                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                && mSettingsHelper.isLocationEnabled(identity.getUserId())
                && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                identity.getPackageName());
+1 −1
Original line number Diff line number Diff line
@@ -260,7 +260,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
        CallerIdentity identity = registration.getIdentity();
        return registration.isPermitted()
                && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
                identity.getUserId())
                && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+6 −26
Original line number Diff line number Diff line
@@ -202,22 +202,20 @@ public class LocationProviderManagerTest {
    @Test
    public void testIsEnabled() {
        assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();

        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();

        mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
        mProvider.setAllowed(false);
        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();

        mProvider.setAllowed(true);
        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();

        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
        assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
    }

    @Test
@@ -237,23 +235,15 @@ public class LocationProviderManagerTest {
        mProvider.setAllowed(false);
        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                false);
        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
                false);

        mProvider.setAllowed(true);
        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                true);

        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
                false);
        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
                true);

        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
                true);
        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
                false);

        mManager.removeEnabledListener(listener);
        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
        verifyNoMoreInteractions(listener);
@@ -397,16 +387,6 @@ public class LocationProviderManagerTest {
        mProvider.setAllowed(true);
        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true);

        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false);
        loc = createLocation(NAME, mRandom);
        mProvider.setProviderLocation(loc);
        verify(listener, times(1)).onLocationChanged(any(Location.class),
                nullable(IRemoteCallback.class));

        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true);

        loc = createLocation(NAME, mRandom);
        mProvider.setProviderLocation(loc);
        verify(listener, times(2)).onLocationChanged(locationCaptor.capture(),