diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 18656521f515b..ce2774d333d19 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -50,6 +50,7 @@ import android.net.ConnectivityManager; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; +import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.DropBoxManager; @@ -1581,6 +1582,24 @@ public final class Settings { // with a partial enable/disable state in multi-threaded situations. private static final Object mLocationSettingsLock = new Object(); + // Used in system server calling uid workaround in call() + private static boolean sInSystemServer = false; + private static final Object sInSystemServerLock = new Object(); + + /** @hide */ + public static void setInSystemServer() { + synchronized (sInSystemServerLock) { + sInSystemServer = true; + } + } + + /** @hide */ + public static boolean isInSystemServer() { + synchronized (sInSystemServerLock) { + return sInSystemServer; + } + } + public static class SettingNotFoundException extends AndroidException { public SettingNotFoundException(String msg) { super(msg); @@ -1789,7 +1808,23 @@ public final class Settings { } } } - Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + Bundle b; + // If we're in system server and in a binder transaction we need to clear the + // calling uid. This works around code in system server that did not call + // clearCallingIdentity, previously this wasn't needed because reading settings + // did not do permission checking but thats no longer the case. + // Long term this should be removed and callers should properly call + // clearCallingIdentity or use a ContentResolver from the caller as needed. + if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + b = cp.call(cr.getPackageName(), mCallGetCommand, name, args); + } if (b != null) { String value = b.getString(Settings.NameValueTable.VALUE); // Don't update our cache for reads of other users' data @@ -1849,7 +1884,19 @@ public final class Settings { try { Bundle queryArgs = ContentResolver.createSqlQueryBundle( NAME_EQ_PLACEHOLDER, new String[]{name}, null); - c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null); + // Same workaround as above. + if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, + null); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, + null); + } if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); return null; @@ -4005,6 +4052,22 @@ public final class Settings { outMap.putAll(CLONE_FROM_PARENT_ON_VALUE); } + /** + * System settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(TEXT_AUTO_REPLACE); + EPHEMERAL_SETTINGS.add(TEXT_AUTO_CAPS); + EPHEMERAL_SETTINGS.add(TEXT_AUTO_PUNCTUATE); + EPHEMERAL_SETTINGS.add(TEXT_SHOW_PASSWORD); + EPHEMERAL_SETTINGS.add(DATE_FORMAT); + EPHEMERAL_SETTINGS.add(FONT_SCALE); + EPHEMERAL_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED); + EPHEMERAL_SETTINGS.add(TIME_12_24); + } + /** * When to use Wi-Fi calling * @@ -6866,6 +6929,20 @@ public final class Settings { outKeySet.addAll(CLONE_TO_MANAGED_PROFILE); } + /** + * Secure settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(ENABLED_ACCESSIBILITY_SERVICES); + EPHEMERAL_SETTINGS.add(ACCESSIBILITY_SPEAK_PASSWORD); + EPHEMERAL_SETTINGS.add(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + + EPHEMERAL_SETTINGS.add(DEFAULT_INPUT_METHOD); + EPHEMERAL_SETTINGS.add(ENABLED_INPUT_METHODS); + } + /** * Helper method for determining if a location provider is enabled. * @@ -9863,6 +9940,20 @@ public final class Settings { * @hide */ public static final String CELL_ON = "cell_on"; + + /** + * Global settings which can be accessed by ephemeral apps. + * @hide + */ + public static final Set EPHEMERAL_SETTINGS = new ArraySet<>(); + static { + EPHEMERAL_SETTINGS.add(WAIT_FOR_DEBUGGER); + EPHEMERAL_SETTINGS.add(DEVICE_PROVISIONED); + EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); + EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RTL); + EPHEMERAL_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES); + } + } /** diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3e62158d591cd..a29a46debba09 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 @@ public class SettingsProvider extends ContentProvider { @Override public boolean onCreate() { + Settings.setInSystemServer(); synchronized (mLock) { mUserManager = UserManager.get(getContext()); mPackageManager = AppGlobals.getPackageManager(); @@ -813,7 +814,8 @@ public class SettingsProvider extends ContentProvider { SettingsState settingsState = mSettingsRegistry.getSettingsLocked( SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); - List names = settingsState.getSettingNamesLocked(); + List names = getSettingsNamesLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM); final int nameCount = names.size(); @@ -836,6 +838,9 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); synchronized (mLock) { - List names = mSettingsRegistry.getSettingsNamesLocked( - SETTINGS_TYPE_SECURE, callingUserId); + List names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId); final int nameCount = names.size(); @@ -974,6 +978,9 @@ public class SettingsProvider extends ContentProvider { // 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 @@ public class SettingsProvider extends ContentProvider { final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); synchronized (mLock) { - List names = mSettingsRegistry.getSettingsNamesLocked( - SETTINGS_TYPE_SYSTEM, callingUserId); + List names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId); final int nameCount = names.size(); @@ -1136,6 +1142,9 @@ public class SettingsProvider extends ContentProvider { // 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 @@ public class SettingsProvider extends ContentProvider { && (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 @@ public class SettingsProvider extends ContentProvider { } } + private Set 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 getSettingsNamesLocked(int settingsType, int userId) { + ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId); + if (ai.isEphemeralApp()) { + return new ArrayList(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 @@ public class SettingsProvider extends ContentProvider { value = value.substring(1); Setting settingValue = getSecureSetting( - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); if (settingValue == null) { return false; }