Add ability to exclude settings from being restored.
Bug: 143129051 Test: atest SettingsProviderTest Change-Id: Id5d367b0fb8386dbae4eb3e29c8c1e9ba3c547d6
This commit is contained in:
24
packages/SettingsProvider/res/values/blocked_settings.xml
Normal file
24
packages/SettingsProvider/res/values/blocked_settings.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- These are arrays of settings which should not be restored to this device -->
|
||||
<resources>
|
||||
<string-array name="restore_blocked_device_specific_settings" />
|
||||
<string-array name="restore_blocked_global_settings" />
|
||||
<string-array name="restore_blocked_secure_settings" />
|
||||
<string-array name="restore_blocked_system_settings" />
|
||||
</resources>
|
||||
25
packages/SettingsProvider/res/values/overlayable.xml
Normal file
25
packages/SettingsProvider/res/values/overlayable.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<overlayable name="SettingsToNotRestore">
|
||||
<policy type="product|system|vendor">
|
||||
<item type="array" name="restore_blocked_device_specific_settings" />
|
||||
<item type="array" name="restore_blocked_global_settings" />
|
||||
<item type="array" name="restore_blocked_secure_settings" />
|
||||
<item type="array" name="restore_blocked_system_settings" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
</resources>
|
||||
@@ -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<String> 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<String> movedToGlobal = new HashSet<String>();
|
||||
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<String> movedToGlobal, Set<String> movedToSecure) {
|
||||
private void restoreSettings(
|
||||
BackupDataInput data,
|
||||
Uri contentUri,
|
||||
HashSet<String> movedToGlobal,
|
||||
Set<String> movedToSecure,
|
||||
int blockedSettingsArrayId,
|
||||
Set<String> 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<String> movedToGlobal, Set<String> movedToSecure) {
|
||||
restoreSettings(settings, 0, bytes, contentUri, movedToGlobal, movedToSecure);
|
||||
private void restoreSettings(
|
||||
byte[] settings,
|
||||
int bytes,
|
||||
Uri contentUri,
|
||||
HashSet<String> movedToGlobal,
|
||||
Set<String> movedToSecure,
|
||||
int blockedSettingsArrayId,
|
||||
Set<String> dynamicBlockList) {
|
||||
restoreSettings(
|
||||
settings,
|
||||
0,
|
||||
bytes,
|
||||
contentUri,
|
||||
movedToGlobal,
|
||||
movedToSecure,
|
||||
blockedSettingsArrayId,
|
||||
dynamicBlockList);
|
||||
}
|
||||
|
||||
private void restoreSettings(byte[] settings, int pos, int bytes, Uri contentUri,
|
||||
HashSet<String> movedToGlobal, Set<String> movedToSecure) {
|
||||
private void restoreSettings(
|
||||
byte[] settings,
|
||||
int pos,
|
||||
int bytes,
|
||||
Uri contentUri,
|
||||
HashSet<String> movedToGlobal,
|
||||
Set<String> movedToSecure,
|
||||
int blockedSettingsArrayId,
|
||||
Set<String> 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<String> 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<String> 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<String> getBlockedSettings(int blockedSettingsArrayId) {
|
||||
String[] blockedSettings = getResources().getStringArray(blockedSettingsArrayId);
|
||||
return new HashSet<>(Arrays.asList(blockedSettings));
|
||||
}
|
||||
|
||||
private boolean isValidSettingValue(String key, String value,
|
||||
Map<String, Validator> 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<String> 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);
|
||||
|
||||
|
||||
@@ -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<String> 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)
|
||||
|
||||
Reference in New Issue
Block a user