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

Commit ac5f03c0 authored by Sarah Chin's avatar Sarah Chin
Browse files

Update ServiceState broadcast for location permissions

Add extra checks for master location switch and location bypass packages
and send broadcasts accordingly.

Test: Verify location is sanitized using testapp
Test: Verify privacy dashboard with MLS on/off
Test: Verify location is sanitized/unsanitized with bypass list
Bug: 230919427
Bug: 210118427
Change-Id: I9784527d6d79235830f562c1944562f5a6ac1fb3
Merged-In: I9784527d6d79235830f562c1944562f5a6ac1fb3
parent 0fe4a203
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -5308,4 +5308,12 @@
    </string>
    </string>


    <integer name="config_chooser_max_targets_per_row">4</integer>
    <integer name="config_chooser_max_targets_per_row">4</integer>

    <!-- List of system components which are allowed to receive ServiceState entries in an
         un-sanitized form, even if the location toggle is off. This is intended ONLY for system
         components, such as the telephony stack, which require access to the full ServiceState for
         tasks such as network registration. -->
    <string-array name="config_serviceStateLocationAllowedPackages">
        <item>"com.android.phone"</item>
    </string-array>
</resources>
</resources>
+1 −1
Original line number Original line Diff line number Diff line
@@ -4492,6 +4492,6 @@
  <java-symbol type="array" name="config_roundedCornerBottomRadiusAdjustmentArray" />
  <java-symbol type="array" name="config_roundedCornerBottomRadiusAdjustmentArray" />
  <java-symbol type="bool" name="config_secondaryBuiltInDisplayIsRound" />
  <java-symbol type="bool" name="config_secondaryBuiltInDisplayIsRound" />
  <java-symbol type="array" name="config_builtInDisplayIsRoundArray" />
  <java-symbol type="array" name="config_builtInDisplayIsRoundArray" />

  <java-symbol type="array" name="config_serviceStateLocationAllowedPackages" />
  <java-symbol type="dimen" name="status_bar_height_default" />
  <java-symbol type="dimen" name="status_bar_height_default" />
</resources>
</resources>
+74 −28
Original line number Original line Diff line number Diff line
@@ -2891,42 +2891,88 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
            Binder.restoreCallingIdentity(ident);
            Binder.restoreCallingIdentity(ident);
        }
        }


        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
        // Send the broadcast exactly once to all possible disjoint sets of apps.
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        // If the location master switch is on, broadcast the ServiceState 4 times:
        Bundle data = new Bundle();
        // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and READ_PHONE_STATE
        state.fillInNotifierBundle(data);
        // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and
        intent.putExtras(data);
        //   READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE
        // Pass the subscription along with the intent.
        // - Sanitized ServiceState sent to apps with READ_PHONE_STATE but not ACCESS_FINE_LOCATION
        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
        // - Sanitized ServiceState sent to apps with READ_PRIVILEGED_PHONE_STATE but neither
        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
        //   READ_PHONE_STATE nor ACCESS_FINE_LOCATION
        intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
        // If the location master switch is off, broadcast the ServiceState multiple times:
        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
        // - Full ServiceState sent to all apps permitted to bypass the location master switch if

        //   they have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE
        // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
        // - Sanitized ServiceState sent to all other apps with READ_PHONE_STATE
        // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE.
        // - Sanitized ServiceState sent to all other apps with READ_PRIVILEGED_PHONE_STATE but not
        // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with
        //   READ_PHONE_STATE
        // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION.
        if (Binder.withCleanCallingIdentity(() ->
        // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE
                LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId()))) {
        // get this broadcast exactly once, and we are not exposing location without permission.
            Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false);
        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                    fullIntent,
                    new String[]{Manifest.permission.READ_PHONE_STATE,
                    new String[]{Manifest.permission.READ_PHONE_STATE,
                            Manifest.permission.ACCESS_FINE_LOCATION});
                            Manifest.permission.ACCESS_FINE_LOCATION});
        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                    fullIntent,
                    new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
                    new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
                            Manifest.permission.ACCESS_FINE_LOCATION},
                            Manifest.permission.ACCESS_FINE_LOCATION},
                    new String[]{Manifest.permission.READ_PHONE_STATE});
                    new String[]{Manifest.permission.READ_PHONE_STATE});


        // Replace bundle with location-sanitized ServiceState
            Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true);
        data = new Bundle();
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
        state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data);
                    sanitizedIntent,
        intent.putExtras(data);
        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
                    new String[]{Manifest.permission.READ_PHONE_STATE},
                    new String[]{Manifest.permission.READ_PHONE_STATE},
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION});
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION});
        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                    sanitizedIntent,
                    new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
                    new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
                    new String[]{Manifest.permission.READ_PHONE_STATE,
                    new String[]{Manifest.permission.READ_PHONE_STATE,
                            Manifest.permission.ACCESS_FINE_LOCATION});
                            Manifest.permission.ACCESS_FINE_LOCATION});
        } else {
            String[] locationBypassPackages = Binder.withCleanCallingIdentity(() ->
                    LocationAccessPolicy.getLocationBypassPackages(mContext));
            for (String locationBypassPackage : locationBypassPackages) {
                Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false);
                fullIntent.setPackage(locationBypassPackage);
                mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                        fullIntent,
                        new String[]{Manifest.permission.READ_PHONE_STATE});
                mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                        fullIntent,
                        new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
                        new String[]{Manifest.permission.READ_PHONE_STATE});
            }

            Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true);
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                    sanitizedIntent,
                    new String[]{Manifest.permission.READ_PHONE_STATE},
                    new String[]{/* no excluded permissions */},
                    locationBypassPackages);
            mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                    sanitizedIntent,
                    new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
                    new String[]{Manifest.permission.READ_PHONE_STATE},
                    locationBypassPackages);
        }
    }

    private Intent createServiceStateIntent(ServiceState state, int subId, int phoneId,
            boolean sanitizeLocation) {
        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        Bundle data = new Bundle();
        if (sanitizeLocation) {
            state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data);
        } else {
            state.fillInNotifierBundle(data);
        }
        intent.putExtras(data);
        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
        intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
        return intent;
    }
    }


    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
+12 −1
Original line number Original line Diff line number Diff line
@@ -361,7 +361,10 @@ public final class LocationAccessPolicy {
        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid);
        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid);
    }
    }


    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
    /**
     * @return Whether location is enabled for the given user.
     */
    public static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
        LocationManager locationManager = context.getSystemService(LocationManager.class);
        LocationManager locationManager = context.getSystemService(LocationManager.class);
        if (locationManager == null) {
        if (locationManager == null) {
            Log.w(TAG, "Couldn't get location manager, denying location access");
            Log.w(TAG, "Couldn't get location manager, denying location access");
@@ -370,6 +373,14 @@ public final class LocationAccessPolicy {
        return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
        return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
    }
    }


    /**
     * @return An array of packages that are always allowed to access location.
     */
    public static @NonNull String[] getLocationBypassPackages(@NonNull Context context) {
        return context.getResources().getStringArray(
                com.android.internal.R.array.config_serviceStateLocationAllowedPackages);
    }

    private static boolean checkInteractAcrossUsersFull(
    private static boolean checkInteractAcrossUsersFull(
            @NonNull Context context, int pid, int uid) {
            @NonNull Context context, int pid, int uid) {
        return checkManifestPermission(context, pid, uid,
        return checkManifestPermission(context, pid, uid,