1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.common.util; 18 19 import static org.junit.Assert.assertTrue; 20 21 import android.provider.DeviceConfig; 22 import android.util.ArrayMap; 23 24 import androidx.annotation.GuardedBy; 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 28 import com.android.compatibility.common.util.TestUtils.RunnableWithThrow; 29 30 import java.util.Objects; 31 32 /** 33 * Helper to automatically save multiple existing DeviceConfig values, change them during tests, and 34 * restore the original values after the test. 35 */ 36 public class DeviceConfigStateHelper implements AutoCloseable { 37 private final String mNamespace; 38 @GuardedBy("mOriginalValues") 39 private final ArrayMap<String, String> mOriginalValues = new ArrayMap<>(); 40 41 /** 42 * @param namespace DeviceConfig namespace. 43 */ DeviceConfigStateHelper(@onNull String namespace)44 public DeviceConfigStateHelper(@NonNull String namespace) { 45 mNamespace = Objects.requireNonNull(namespace); 46 } 47 maybeCacheOriginalValueLocked(String key)48 private void maybeCacheOriginalValueLocked(String key) { 49 if (!mOriginalValues.containsKey(key)) { 50 // Only save the current value if we haven't changed it. 51 final String ogValue = SystemUtil.runWithShellPermissionIdentity( 52 () -> DeviceConfig.getProperty(mNamespace, key)); 53 mOriginalValues.put(key, ogValue); 54 } 55 } 56 set(@onNull String key, @Nullable String value)57 public void set(@NonNull String key, @Nullable String value) { 58 synchronized (mOriginalValues) { 59 maybeCacheOriginalValueLocked(key); 60 } 61 SystemUtil.runWithShellPermissionIdentity( 62 () -> assertTrue( 63 DeviceConfig.setProperty(mNamespace, key, value, /* makeDefault */false))); 64 } 65 66 /** 67 * Resets the value of the given key if was ever modified and returns {@code true} on success. 68 */ reset(@onNull String key)69 public boolean reset(@NonNull String key) { 70 final String ogValue; 71 synchronized (mOriginalValues) { 72 ogValue = mOriginalValues.get(key); 73 if (ogValue == null) { 74 return false; 75 } 76 } 77 set(key, ogValue); 78 return true; 79 } 80 81 /** 82 * Run a Runnable, with DeviceConfig.setSyncDisabledMode(SYNC_DISABLED_MODE_NONE), 83 * with all the shell permissions. 84 */ callWithSyncEnabledWithShellPermissions(RunnableWithThrow r)85 public static void callWithSyncEnabledWithShellPermissions(RunnableWithThrow r) { 86 SystemUtil.runWithShellPermissionIdentity(() -> { 87 final String originalSyncMode = ShellUtils.runShellCommand( 88 "device_config get_sync_disabled_for_tests"); 89 try { 90 // TODO: Use DeviceConfig.setSyncDisabledMode, once the SYNC_* constants 91 // are exposed. 92 ShellUtils.runShellCommand("cmd device_config set_sync_disabled_for_tests none"); 93 94 r.run(); 95 } finally { 96 ShellUtils.runShellCommand( 97 "device_config set_sync_disabled_for_tests %s", originalSyncMode); 98 } 99 }); 100 } 101 set(@onNull DeviceConfig.Properties properties)102 public void set(@NonNull DeviceConfig.Properties properties) { 103 synchronized (mOriginalValues) { 104 for (String key : properties.getKeyset()) { 105 maybeCacheOriginalValueLocked(key); 106 } 107 } 108 callWithSyncEnabledWithShellPermissions( 109 () -> assertTrue(DeviceConfig.setProperties(properties))); 110 } 111 restoreOriginalValues()112 public void restoreOriginalValues() { 113 final DeviceConfig.Properties.Builder builder = 114 new DeviceConfig.Properties.Builder(mNamespace); 115 synchronized (mOriginalValues) { 116 for (int i = 0; i < mOriginalValues.size(); ++i) { 117 builder.setString(mOriginalValues.keyAt(i), mOriginalValues.valueAt(i)); 118 } 119 mOriginalValues.clear(); 120 } 121 callWithSyncEnabledWithShellPermissions( 122 () -> assertTrue(DeviceConfig.setProperties(builder.build()))); 123 } 124 125 @Override close()126 public void close() throws Exception { 127 restoreOriginalValues(); 128 } 129 } 130