Add ephemeral whitelist for SettingsProvider

Currently the list is small, only whats required to launch a basic
ephemeral app. It will expand in followup CLs.

Note that the goal of this is not to completely shut down all ways that
an ephemeral app could learn the value (or part of) of a setting not in
the set. The goal is to limit the raw access to settings to a small set that
includes settings that ephemeral apps should have access to directly
System APIs that are exposed to ephemeral apps may allow for
ephemeral apps to learn the value of settings not in the directly
exposed set and that is OK and _not_ a security issue.

This contains a hack to support code in system system server that in
the process of a binder transaction reads a setting using a
ContentReceiver with a system package name. This was previously not an
issue but causes an exception to be thrown from getCallingPackage which
reading a setting now calls.

Bug: 33349998
Test: Boots, functions as normal for regular apps.
Test: cts-tradefed run cts -m CtsProviderTestCases -t
android.provider.cts.SettingsTest

Change-Id: Icc839b0d98c725d23cdd395e8cb76a7b293f8767
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3e62158..a29a46d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -262,6 +262,7 @@
 
     @Override
     public boolean onCreate() {
+        Settings.setInSystemServer();
         synchronized (mLock) {
             mUserManager = UserManager.get(getContext());
             mPackageManager = AppGlobals.getPackageManager();
@@ -813,7 +814,8 @@
             SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
                     SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
 
-            List<String> names = settingsState.getSettingNamesLocked();
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_GLOBAL,
+                    UserHandle.USER_SYSTEM);
 
             final int nameCount = names.size();
 
@@ -836,6 +838,9 @@
             Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")");
         }
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_GLOBAL, UserHandle.getCallingUserId());
+
         // Get the value.
         synchronized (mLock) {
             return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,
@@ -938,8 +943,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
         synchronized (mLock) {
-            List<String> names = mSettingsRegistry.getSettingsNamesLocked(
-                    SETTINGS_TYPE_SECURE, callingUserId);
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId);
 
             final int nameCount = names.size();
 
@@ -974,6 +978,9 @@
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_SECURE, callingUserId);
+
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
 
@@ -1104,8 +1111,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
         synchronized (mLock) {
-            List<String> names = mSettingsRegistry.getSettingsNamesLocked(
-                    SETTINGS_TYPE_SYSTEM, callingUserId);
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId);
 
             final int nameCount = names.size();
 
@@ -1136,6 +1142,9 @@
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, callingUserId);
+
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
 
@@ -1354,9 +1363,15 @@
                 && (parentId = getGroupParentLocked(userId)) != userId) {
             // The setting has a dependency and the profile has a parent
             String dependency = sSystemCloneFromParentOnDependency.get(setting);
-            Setting settingObj = getSecureSetting(dependency, userId);
-            if (settingObj != null && settingObj.getValue().equals("1")) {
-                return parentId;
+            // Lookup the dependency setting as ourselves, some callers may not have access to it.
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Setting settingObj = getSecureSetting(dependency, userId);
+                if (settingObj != null && settingObj.getValue().equals("1")) {
+                    return parentId;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
         return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting);
@@ -1424,6 +1439,55 @@
         }
     }
 
+    private Set<String> getEphemeralAccessibleSettings(int settingsType) {
+        switch (settingsType) {
+            case SETTINGS_TYPE_GLOBAL:
+                return Settings.Global.EPHEMERAL_SETTINGS;
+            case SETTINGS_TYPE_SECURE:
+                return Settings.Secure.EPHEMERAL_SETTINGS;
+            case SETTINGS_TYPE_SYSTEM:
+                return Settings.System.EPHEMERAL_SETTINGS;
+            default:
+                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+        }
+    }
+
+    private List<String> getSettingsNamesLocked(int settingsType, int userId) {
+        ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
+        if (ai.isEphemeralApp()) {
+            return new ArrayList<String>(getEphemeralAccessibleSettings(settingsType));
+        } else {
+            return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
+        }
+    }
+
+    private void enforceSettingReadable(String settingName, int settingsType, int userId) {
+        if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
+            return;
+        }
+        ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
+        if (!ai.isEphemeralApp()) {
+            return;
+        }
+        if (!getEphemeralAccessibleSettings(settingsType).contains(settingName)) {
+            throw new SecurityException("Setting " + settingName + " is not accessible from"
+                    + " ephemeral package " + getCallingPackage());
+        }
+    }
+
+    private ApplicationInfo getCallingApplicationInfoOrThrow(int userId) {
+        ApplicationInfo ai = null;
+        try {
+            ai = mPackageManager.getApplicationInfo(getCallingPackage(), 0 , userId);
+        } catch (RemoteException ignored) {
+        }
+        if (ai == null) {
+            throw new IllegalStateException("Failed to lookup info for package "
+                    + getCallingPackage());
+        }
+        return ai;
+    }
+
     private PackageInfo getCallingPackageInfoOrThrow(int userId) {
         try {
             PackageInfo packageInfo = mPackageManager.getPackageInfo(
@@ -1493,7 +1557,7 @@
         value = value.substring(1);
 
         Setting settingValue = getSecureSetting(
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
         if (settingValue == null) {
             return false;
         }