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

Commit 2af6e521 authored by Songchun Fan's avatar Songchun Fan
Browse files

[SettingsProvider] @Readable annotation to restrict access to hidden keys

Adding a new annotation that can be fetched at runtime.

The settings keys annotated with the new annotation can be accessed via
the get* methods, even if they are marked as @hide. Access to a value
that is not annotated with @Readable will end up with an
IllegalArgumentException.

Notice that if a caller tries to call get* method with a value that is
not defined in the Settings.Secure, Settings.System or Settings.Global
classes, this access is still regarded as valid. We only reject accesses
where the value is defined in one of the three classes, but is marked as
@hide and does not have @Readable annotation.

This CL adds @Readable to all existing hidden keys and public keys.

A follow up CL will remove the @Readable annotation for the hidden keys
added in S. The end result would be that all the keys (hidden or public)
can be accessed if they were added before S. From S and beyond, only
public keys can be accessed.

+ also fixed some formatting issues

BUG: 175024829
Test: atest android.appsecurity.cts.ReadableSettingsFieldsTest
Change-Id: I8a2733580ff9c9d01cbdb0f14ad56c2062a2f956
parent 3b2c510a
Loading
Loading
Loading
Loading
+1332 −277

File changed.

Preview size limit exceeded, changes collapsed.

+54 −0
Original line number Diff line number Diff line
@@ -297,6 +297,24 @@ public class SettingsProvider extends ContentProvider {
        Settings.System.getCloneFromParentOnValueSettings(sSystemCloneFromParentOnDependency);
    }

    private static final Set<String> sAllSecureSettings = new ArraySet<>();
    private static final Set<String> sReadableSecureSettings = new ArraySet<>();
    static {
        Settings.Secure.getPublicSettings(sAllSecureSettings, sReadableSecureSettings);
    }

    private static final Set<String> sAllSystemSettings = new ArraySet<>();
    private static final Set<String> sReadableSystemSettings = new ArraySet<>();
    static {
        Settings.System.getPublicSettings(sAllSystemSettings, sReadableSystemSettings);
    }

    private static final Set<String> sAllGlobalSettings = new ArraySet<>();
    private static final Set<String> sReadableGlobalSettings = new ArraySet<>();
    static {
        Settings.Global.getPublicSettings(sAllGlobalSettings, sReadableGlobalSettings);
    }

    private final Object mLock = new Object();

    @GuardedBy("mLock")
@@ -1919,6 +1937,7 @@ public class SettingsProvider extends ContentProvider {
        if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
            return;
        }
        checkReadableAnnotation(settingsType, settingName);
        ApplicationInfo ai = getCallingApplicationInfoOrThrow();
        if (!ai.isInstantApp()) {
            return;
@@ -1932,6 +1951,41 @@ public class SettingsProvider extends ContentProvider {
        }
    }

    /**
     * Check if the target settings key is readable. Reject if the caller app is trying to access a
     * settings key defined in the Settings.Secure, Settings.System or Settings.Global and is not
     * annotated as @Readable.
     * Notice that a key string that is not defined in any of the Settings.* classes will still be
     * regarded as readable.
     */
    private void checkReadableAnnotation(int settingsType, String settingName) {
        final Set<String> allFields;
        final Set<String> readableFields;
        switch (settingsType) {
            case SETTINGS_TYPE_GLOBAL:
                allFields = sAllGlobalSettings;
                readableFields = sReadableGlobalSettings;
                break;
            case SETTINGS_TYPE_SYSTEM:
                allFields = sAllSystemSettings;
                readableFields = sReadableSystemSettings;
                break;
            case SETTINGS_TYPE_SECURE:
                allFields = sAllSecureSettings;
                readableFields = sReadableSecureSettings;
                break;
            default:
                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
        }

        if (allFields.contains(settingName) && !readableFields.contains(settingName)) {
            throw new SecurityException(
                    "Settings key: <" + settingName + "> is not readable. From S+, new public "
                            + "settings keys need to be annotated with @Readable unless they are "
                            + "annotated with @hide.");
        }
    }

    private ApplicationInfo getCallingApplicationInfoOrThrow() {
        // We always use the callingUid for this lookup. This means that if hypothetically an
        // app was installed in user A with cross user and in user B as an Instant App