diff --git a/packages/SettingsProvider/res/values/blocked_settings.xml b/packages/SettingsProvider/res/values/blocked_settings.xml new file mode 100644 index 0000000000000..b54b74ee1d3ee --- /dev/null +++ b/packages/SettingsProvider/res/values/blocked_settings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SettingsProvider/res/values/overlayable.xml b/packages/SettingsProvider/res/values/overlayable.xml new file mode 100644 index 0000000000000..dc41a77d0e2d6 --- /dev/null +++ b/packages/SettingsProvider/res/values/overlayable.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index c19a340498e71..fb558abe38658 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -66,6 +66,7 @@ import java.io.IOException; import java.io.OutputStream; import java.time.DateTimeException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -250,7 +251,13 @@ public class SettingsBackupAgent extends BackupAgentHelper { @Override public void onRestore(BackupDataInput data, int appVersionCode, - ParcelFileDescriptor newState) throws IOException { + ParcelFileDescriptor newState) { + throw new RuntimeException("SettingsBackupAgent has been migrated to use key exclusion"); + } + + @Override + public void onRestore(BackupDataInput data, long appVersionCode, + ParcelFileDescriptor newState, Set dynamicBlockList) throws IOException { if (DEBUG) { Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode @@ -266,7 +273,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // versionCode of com.android.providers.settings corresponds to SDK_INT - mRestoredFromSdkInt = appVersionCode; + mRestoredFromSdkInt = (int) appVersionCode; HashSet movedToGlobal = new HashSet(); Settings.System.getMovedToGlobalSettings(movedToGlobal); @@ -292,16 +299,29 @@ public class SettingsBackupAgent extends BackupAgentHelper { switch (key) { case KEY_SYSTEM : restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal, - movedToSecure); + movedToSecure, R.array.restore_blocked_system_settings, + dynamicBlockList); mSettingsHelper.applyAudioSettings(); break; case KEY_SECURE : - restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal, null); + restoreSettings( + data, + Settings.Secure.CONTENT_URI, + movedToGlobal, + null, + R.array.restore_blocked_secure_settings, + dynamicBlockList); break; case KEY_GLOBAL : - restoreSettings(data, Settings.Global.CONTENT_URI, null, movedToSecure); + restoreSettings( + data, + Settings.Global.CONTENT_URI, + null, + movedToSecure, + R.array.restore_blocked_global_settings, + dynamicBlockList); break; case KEY_WIFI_SUPPLICANT : @@ -345,7 +365,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { case KEY_DEVICE_SPECIFIC_CONFIG: byte[] restoredDeviceSpecificConfig = new byte[size]; data.readEntityData(restoredDeviceSpecificConfig, 0, size); - restoreDeviceSpecificConfig(restoredDeviceSpecificConfig); + restoreDeviceSpecificConfig( + restoredDeviceSpecificConfig, + R.array.restore_blocked_device_specific_settings, + dynamicBlockList); break; default : @@ -394,14 +417,22 @@ public class SettingsBackupAgent extends BackupAgentHelper { byte[] buffer = new byte[nBytes]; in.readFully(buffer, 0, nBytes); restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal, - movedToSecure); + movedToSecure, R.array.restore_blocked_system_settings, + Collections.emptySet()); // secure settings nBytes = in.readInt(); if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data"); if (nBytes > buffer.length) buffer = new byte[nBytes]; in.readFully(buffer, 0, nBytes); - restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal, null); + restoreSettings( + buffer, + nBytes, + Settings.Secure.CONTENT_URI, + movedToGlobal, + null, + R.array.restore_blocked_secure_settings, + Collections.emptySet()); // Global only if sufficiently new if (version >= FULL_BACKUP_ADDED_GLOBAL) { @@ -411,7 +442,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { in.readFully(buffer, 0, nBytes); movedToGlobal.clear(); // no redirection; this *is* the global namespace restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal, - movedToSecure); + movedToSecure, R.array.restore_blocked_global_settings, + Collections.emptySet()); } // locale @@ -612,8 +644,13 @@ public class SettingsBackupAgent extends BackupAgentHelper { return baos.toByteArray(); } - private void restoreSettings(BackupDataInput data, Uri contentUri, - HashSet movedToGlobal, Set movedToSecure) { + private void restoreSettings( + BackupDataInput data, + Uri contentUri, + HashSet movedToGlobal, + Set movedToSecure, + int blockedSettingsArrayId, + Set dynamicBlockList) { byte[] settings = new byte[data.getDataSize()]; try { data.readEntityData(settings, 0, settings.length); @@ -621,16 +658,44 @@ public class SettingsBackupAgent extends BackupAgentHelper { Log.e(TAG, "Couldn't read entity data"); return; } - restoreSettings(settings, settings.length, contentUri, movedToGlobal, movedToSecure); + restoreSettings( + settings, + settings.length, + contentUri, + movedToGlobal, + movedToSecure, + blockedSettingsArrayId, + dynamicBlockList); } - private void restoreSettings(byte[] settings, int bytes, Uri contentUri, - HashSet movedToGlobal, Set movedToSecure) { - restoreSettings(settings, 0, bytes, contentUri, movedToGlobal, movedToSecure); + private void restoreSettings( + byte[] settings, + int bytes, + Uri contentUri, + HashSet movedToGlobal, + Set movedToSecure, + int blockedSettingsArrayId, + Set dynamicBlockList) { + restoreSettings( + settings, + 0, + bytes, + contentUri, + movedToGlobal, + movedToSecure, + blockedSettingsArrayId, + dynamicBlockList); } - private void restoreSettings(byte[] settings, int pos, int bytes, Uri contentUri, - HashSet movedToGlobal, Set movedToSecure) { + private void restoreSettings( + byte[] settings, + int pos, + int bytes, + Uri contentUri, + HashSet movedToGlobal, + Set movedToSecure, + int blockedSettingsArrayId, + Set dynamicBlockList) { if (DEBUG) { Log.i(TAG, "restoreSettings: " + contentUri); } @@ -662,9 +727,20 @@ public class SettingsBackupAgent extends BackupAgentHelper { SettingsHelper settingsHelper = mSettingsHelper; ContentResolver cr = getContentResolver(); - final int whiteListSize = whitelist.length; - for (int i = 0; i < whiteListSize; i++) { - String key = whitelist[i]; + Set blockedSettings = getBlockedSettings(blockedSettingsArrayId); + + for (String key : whitelist) { + boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key); + if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) { + Log.i( + TAG, + "Key " + + key + + " removed from restore by " + + (isBlockedBySystem ? "system" : "dynamic") + + " block list"); + continue; + } String value = null; boolean hasValueToRestore = false; @@ -722,6 +798,19 @@ public class SettingsBackupAgent extends BackupAgentHelper { } } + private boolean isBlockedByDynamicList(Set dynamicBlockList, Uri areaUri, String key) { + String contentKey = Uri.withAppendedPath(areaUri, key).toString(); + return dynamicBlockList.contains(contentKey); + } + + // There may be other sources of blocked settings, so I'm separating out this + // code to make it easy to modify in the future. + @VisibleForTesting + protected Set getBlockedSettings(int blockedSettingsArrayId) { + String[] blockedSettings = getResources().getStringArray(blockedSettingsArrayId); + return new HashSet<>(Arrays.asList(blockedSettings)); + } + private boolean isValidSettingValue(String key, String value, Map validators) { if (key == null || validators == null) { @@ -998,10 +1087,13 @@ public class SettingsBackupAgent extends BackupAgentHelper { * Restore the device specific settings. * * @param data The byte array holding a backed up version of another devices settings. + * @param blockedSettingsArrayId The string array resource holding the settings not to restore. + * @param dynamicBlocklist The dynamic list of settings not to restore fed into this agent. * @return true if the restore succeeded, false if it was stopped. */ @VisibleForTesting - boolean restoreDeviceSpecificConfig(byte[] data) { + boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId, + Set dynamicBlocklist) { // We're using an AtomicInteger to wrap the position int and allow called methods to // modify it. AtomicInteger pos = new AtomicInteger(0); @@ -1013,7 +1105,14 @@ public class SettingsBackupAgent extends BackupAgentHelper { int dataStart = pos.get(); restoreSettings( - data, dataStart, data.length, Settings.Secure.CONTENT_URI, null, null); + data, + dataStart, + data.length, + Settings.Secure.CONTENT_URI, + null, + null, + blockedSettingsArrayId, + dynamicBlocklist); updateWindowManagerIfNeeded(originalDensity); diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java index 57e22db6a55cd..e6508823c7e3c 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java @@ -37,14 +37,14 @@ import android.test.mock.MockContentResolver; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.annotations.VisibleForTesting; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -85,11 +85,32 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries); - mAgentUnderTest.restoreDeviceSpecificConfig(settingsBackup); + mAgentUnderTest.restoreDeviceSpecificConfig( + settingsBackup, + R.array.restore_blocked_device_specific_settings, + Collections.emptySet()); assertEquals("Not all values were restored.", TEST_VALUES, helper.mWrittenValues); } + @Test + public void testRoundTripDeviceSpecificSettingsWithBlock() throws IOException { + TestSettingsHelper helper = new TestSettingsHelper(mContext); + mAgentUnderTest.mSettingsHelper = helper; + + byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration(); + + assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries); + mAgentUnderTest.setBlockedSettings(TEST_VALUES.keySet().toArray(new String[0])); + + mAgentUnderTest.restoreDeviceSpecificConfig( + settingsBackup, + R.array.restore_blocked_device_specific_settings, + Collections.emptySet()); + + assertTrue("Not all values were blocked.", helper.mWrittenValues.isEmpty()); + } + @Test public void testGeneratedHeaderMatchesCurrentDevice() throws IOException { mAgentUnderTest.mSettingsHelper = new TestSettingsHelper(mContext); @@ -148,7 +169,10 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { assertFalse( "Blocking isSourceAcceptable did not stop restore", - mAgentUnderTest.restoreDeviceSpecificConfig(data)); + mAgentUnderTest.restoreDeviceSpecificConfig( + data, + R.array.restore_blocked_device_specific_settings, + Collections.emptySet())); } private byte[] generateUncorruptedHeader() throws IOException { @@ -184,14 +208,34 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { } } + private byte[] generateSingleKeyTestBackupData(String key, String value) throws IOException { + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + os.write(SettingsBackupAgent.toByteArray(key)); + os.write(SettingsBackupAgent.toByteArray(value)); + return os.toByteArray(); + } + } + private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent { private Boolean mForcedDeviceInfoRestoreAcceptability = null; + private String[] mBlockedSettings = null; void setForcedDeviceInfoRestoreAcceptability(boolean value) { mForcedDeviceInfoRestoreAcceptability = value; } - @VisibleForTesting + void setBlockedSettings(String... blockedSettings) { + mBlockedSettings = blockedSettings; + } + + @Override + protected Set getBlockedSettings(int blockedSettingsArrayId) { + return mBlockedSettings == null + ? super.getBlockedSettings(blockedSettingsArrayId) + : new HashSet<>(Arrays.asList(mBlockedSettings)); + } + + @Override boolean isSourceAcceptable(byte[] data, AtomicInteger pos) { return mForcedDeviceInfoRestoreAcceptability == null ? super.isSourceAcceptable(data, pos)