1 /* 2 * Copyright (C) 2017 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 android.cts.backup.keyvaluerestoreapp; 18 19 import static android.content.Context.MODE_PRIVATE; 20 21 import static androidx.test.InstrumentationRegistry.getTargetContext; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertTrue; 25 26 import android.app.backup.BackupManager; 27 import android.app.backup.BackupObserver; 28 import android.app.backup.FileBackupHelper; 29 import android.app.backup.SharedPreferencesBackupHelper; 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.SharedPreferences; 35 import android.platform.test.annotations.AppModeFull; 36 import android.util.Log; 37 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.compatibility.common.util.SystemUtil; 41 import com.android.compatibility.common.util.TestUtils; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 48 import java.io.File; 49 import java.io.FileNotFoundException; 50 import java.io.PrintWriter; 51 import java.util.Scanner; 52 import java.util.concurrent.CountDownLatch; 53 import java.util.concurrent.TimeUnit; 54 55 /** 56 * Device side routines to be invoked by the host side KeyValueBackupRestoreHostSideTest. These 57 * are not designed to be called in any other way, as they rely on state set up by the host side 58 * test. 59 * 60 * Some tests invoked by KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() are 61 * interacting with SharedPrefsRestoreTestActivity from another package. 62 * 63 */ 64 @RunWith(AndroidJUnit4.class) 65 @AppModeFull 66 public class KeyValueBackupRestoreTest { 67 private static final String KEY_VALUE_RESTORE_APP_PACKAGE = 68 "android.cts.backup.keyvaluerestoreapp"; 69 private static final String TAG = "KeyValueBackupRestore"; 70 71 // Names and values of the test files for 72 // KeyValueBackupRestoreHostSideTest#testKeyValueBackupAndRestore() 73 private static final String TEST_FILE_1 = "test-file-1"; 74 private static final String TEST_FILE_1_DATA = "test-file-1-data"; 75 76 private static final String TEST_FILE_2 = "test-file-2"; 77 private static final String TEST_FILE_2_DATA = "test-file-2-data"; 78 79 private static final String TEST_PREFS_1 = "test-prefs-1"; 80 private static final String INT_PREF = "int-pref"; 81 private static final int INT_PREF_VALUE = 111; 82 private static final String BOOL_PREF = "bool-pref"; 83 private static final boolean BOOL_PREF_VALUE = true; 84 85 private static final String TEST_PREFS_2 = "test-prefs-2"; 86 private static final String FLOAT_PREF = "float-pref"; 87 private static final float FLOAT_PREF_VALUE = 0.12345f; 88 private static final String LONG_PREF = "long-pref"; 89 private static final long LONG_PREF_VALUE = 12345L; 90 private static final String STRING_PREF = "string-pref"; 91 private static final String STRING_PREF_VALUE = "string-pref-value"; 92 93 private static final int DEFAULT_INT_VALUE = 0; 94 private static final boolean DEFAULT_BOOL_VALUE = false; 95 private static final float DEFAULT_FLOAT_VALUE = 0.0f; 96 private static final long DEFAULT_LONG_VALUE = 0L; 97 private static final String DEFAULT_STRING_VALUE = null; 98 private static final String DEFAULT_FILE_CONTENT = ""; 99 100 101 /** Package name of the test app for 102 * KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() */ 103 private static final String SHARED_PREFERENCES_RESTORE_PACKAGE_NAME = 104 "android.cts.backup.sharedprefrestoreapp"; 105 106 /** Test activity for KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() */ 107 private static final String SHARED_PREFERENCES_RESTORE_ACTIVITY_NAME = 108 SHARED_PREFERENCES_RESTORE_PACKAGE_NAME + ".SharedPrefsRestoreTestActivity"; 109 110 // Shared prefs test activity actions 111 private static final String INIT_ACTION = "android.backup.cts.backuprestore.INIT"; 112 private static final String UPDATE_ACTION = "android.backup.cts.backuprestore.UPDATE"; 113 private static final String TEST_ACTION = "android.backup.cts.backuprestore.TEST"; 114 115 // Action for returning the result of shared preference activity's operations 116 private static final String RESULT_ACTION = "android.backup.cts.backuprestore.RESULT"; 117 private static final String EXTRA_SUCCESS = "EXTRA_SUCCESS"; 118 119 private static final int ACTIVITY_TEST_TIMEOUT_MS = 5000; 120 private static final int REQUEST_BACKUP_TIMEOUT_SECS = 60; 121 122 private Context mContext; 123 124 private int mIntValue; 125 private boolean mBoolValue; 126 private float mFloatValue; 127 private long mLongValue; 128 private String mStringValue; 129 private String mFileContent1; 130 private String mFileContent2; 131 132 private boolean mSharedPrefTestSuccess; 133 private CountDownLatch mLatch; 134 private Intent mSharedPrefActivityIntent; 135 private BackupManager mBackupManager; 136 137 private final BroadcastReceiver mSharedPrefencesReceiver = new BroadcastReceiver() { 138 @Override 139 public void onReceive(Context context, Intent intent) { 140 Log.i(TAG, "Received shared preference activity result broadcast"); 141 mSharedPrefTestSuccess = intent.getBooleanExtra(EXTRA_SUCCESS, false); 142 mLatch.countDown(); 143 } 144 }; 145 146 @Before setUp()147 public void setUp() { 148 Log.i(TAG, "set up"); 149 150 mContext = getTargetContext(); 151 mLatch = new CountDownLatch(1); 152 mSharedPrefTestSuccess = false; 153 mSharedPrefActivityIntent = new Intent() 154 .setClassName(SHARED_PREFERENCES_RESTORE_PACKAGE_NAME, 155 SHARED_PREFERENCES_RESTORE_ACTIVITY_NAME) 156 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 157 mContext.registerReceiver(mSharedPrefencesReceiver, new IntentFilter(RESULT_ACTION), 158 Context.RECEIVER_EXPORTED_UNAUDITED); 159 mBackupManager = new BackupManager(mContext); 160 } 161 162 @After tearDown()163 public void tearDown() { 164 mContext.unregisterReceiver(mSharedPrefencesReceiver); 165 } 166 167 @Test saveSharedPreferencesAndNotifyBackupManager()168 public void saveSharedPreferencesAndNotifyBackupManager() throws Exception { 169 saveSharedPreferencesValues(); 170 171 //Let BackupManager know that the data has changed 172 BackupManager backupManager = new BackupManager(mContext); 173 backupManager.dataChanged(); 174 } 175 176 @Test checkSharedPrefIsEmpty()177 public void checkSharedPrefIsEmpty() throws Exception { 178 readSharedPrefValues(); 179 assertEquals(DEFAULT_INT_VALUE, mIntValue); 180 assertEquals(DEFAULT_BOOL_VALUE, mBoolValue); 181 assertEquals(DEFAULT_FLOAT_VALUE, mFloatValue, 0.001f); 182 assertEquals(DEFAULT_LONG_VALUE, mLongValue); 183 assertEquals(DEFAULT_STRING_VALUE, mStringValue); 184 assertEquals(DEFAULT_FILE_CONTENT, mFileContent1); 185 assertEquals(DEFAULT_FILE_CONTENT, mFileContent2); 186 } 187 188 @Test checkSharedPreferencesAreRestored()189 public void checkSharedPreferencesAreRestored() throws Exception { 190 readSharedPrefValues(); 191 assertEquals(INT_PREF_VALUE, mIntValue); 192 assertEquals(BOOL_PREF_VALUE, mBoolValue); 193 assertEquals(FLOAT_PREF_VALUE, mFloatValue, 0.001f); 194 assertEquals(LONG_PREF_VALUE, mLongValue); 195 assertEquals(STRING_PREF_VALUE, mStringValue); 196 assertEquals(TEST_FILE_1_DATA, mFileContent1); 197 assertEquals(TEST_FILE_2_DATA, mFileContent2); 198 } 199 200 @Test launchSharedPrefActivity()201 public void launchSharedPrefActivity() throws Exception { 202 mContext.startActivity(mSharedPrefActivityIntent.setAction(INIT_ACTION)); 203 204 assertTrue("Activity init timed out", 205 mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 206 assertTrue("Saving shared preferences didn't succeed", mSharedPrefTestSuccess); 207 } 208 209 @Test updateSharedPrefActivity()210 public void updateSharedPrefActivity() throws Exception { 211 mContext.startActivity(mSharedPrefActivityIntent.setAction(UPDATE_ACTION)); 212 213 assertTrue("Activity launch timed out", 214 mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 215 assertTrue("Saving shared preferences didn't succeed", mSharedPrefTestSuccess); 216 } 217 218 @Test checkSharedPrefActivity()219 public void checkSharedPrefActivity() throws Exception { 220 mContext.startActivity(mSharedPrefActivityIntent.setAction(TEST_ACTION)); 221 222 assertTrue("Activity test timed out", 223 mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 224 assertTrue("Shared preference value wasn't updated from the restore set", 225 mSharedPrefTestSuccess); 226 } 227 228 /** Saves predefined constant values to shared preferences and files. */ saveSharedPreferencesValues()229 private void saveSharedPreferencesValues() throws FileNotFoundException { 230 SharedPreferences prefs = mContext.getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE); 231 SharedPreferences.Editor editor = prefs.edit(); 232 editor.putInt(INT_PREF, INT_PREF_VALUE); 233 editor.putBoolean(BOOL_PREF, BOOL_PREF_VALUE); 234 assertTrue("Error committing shared preference 1 value", editor.commit()); 235 236 prefs = mContext.getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE); 237 editor = prefs.edit(); 238 editor.putFloat(FLOAT_PREF, FLOAT_PREF_VALUE); 239 editor.putLong(LONG_PREF, LONG_PREF_VALUE); 240 editor.putString(STRING_PREF, STRING_PREF_VALUE); 241 assertTrue("Error committing shared preference 2 value", editor.commit()); 242 243 File file = new File(mContext.getFilesDir(), TEST_FILE_1); 244 PrintWriter writer = new PrintWriter(file); 245 writer.write(TEST_FILE_1_DATA); 246 writer.close(); 247 assertTrue("Error writing file 1 data", file.exists()); 248 249 file = new File(mContext.getFilesDir(), TEST_FILE_2); 250 writer = new PrintWriter(file); 251 writer.write(TEST_FILE_2_DATA); 252 writer.close(); 253 assertTrue("Error writing file 2 data", file.exists()); 254 } 255 readSharedPrefValues()256 private void readSharedPrefValues() throws InterruptedException { 257 SharedPreferences prefs = mContext.getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE); 258 mIntValue = prefs.getInt(INT_PREF, DEFAULT_INT_VALUE); 259 mBoolValue = prefs.getBoolean(BOOL_PREF, DEFAULT_BOOL_VALUE); 260 261 prefs = mContext.getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE); 262 mFloatValue = prefs.getFloat(FLOAT_PREF, DEFAULT_FLOAT_VALUE); 263 mLongValue = prefs.getLong(LONG_PREF, DEFAULT_LONG_VALUE); 264 mStringValue = prefs.getString(STRING_PREF, DEFAULT_STRING_VALUE); 265 266 mFileContent1 = readFileContent(TEST_FILE_1); 267 mFileContent2 = readFileContent(TEST_FILE_2); 268 269 Log.i(TAG, INT_PREF + ":" + mIntValue + "\n" 270 + BOOL_PREF + ":" + mBoolValue + "\n" 271 + FLOAT_PREF + ":" + mFloatValue + "\n" 272 + LONG_PREF + ":" + mLongValue + "\n" 273 + STRING_PREF + ":" + mStringValue + "\n" 274 + TEST_FILE_1 + ":" + mFileContent1 + "\n" 275 + TEST_FILE_2 + ":" + mFileContent2 + "\n"); 276 } 277 readFileContent(String fileName)278 private String readFileContent(String fileName) { 279 StringBuilder contents = new StringBuilder(); 280 Scanner scanner = null; 281 try { 282 scanner = new Scanner(new File(mContext.getFilesDir(), fileName)); 283 while (scanner.hasNext()) { 284 contents.append(scanner.nextLine()); 285 } 286 scanner.close(); 287 } catch (FileNotFoundException e) { 288 Log.i(TAG, "Couldn't find test file but this may be fine..."); 289 } finally { 290 if (scanner != null) { 291 scanner.close(); 292 } 293 } 294 return contents.toString(); 295 } 296 getSharedPreferencesBackupHelper(Context context)297 public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) { 298 return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2); 299 } 300 getFileBackupHelper(Context context)301 public static FileBackupHelper getFileBackupHelper(Context context) { 302 return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2); 303 } 304 305 /** 306 * This is not an actual test, but a device-side routine that's called from the host-side to 307 * requestBackup. 308 */ 309 @Test requestBackup()310 public void requestBackup(){ 311 SystemUtil.runWithShellPermissionIdentity(this::requestBackupInternal); 312 } 313 requestBackupInternal()314 private void requestBackupInternal() throws Exception { 315 RequestBackupObserver observer = new RequestBackupObserver(); 316 int result = mBackupManager.requestBackup( 317 new String[] {KEY_VALUE_RESTORE_APP_PACKAGE}, observer); 318 Log.i(TAG, "requestBackup returned with:" + result); 319 if (result == 0) { 320 Log.i(TAG, "Waiting for requestBackup to finish"); 321 observer.waitForCompletion(); 322 } 323 } 324 325 private static class RequestBackupObserver extends BackupObserver { 326 private volatile boolean mDone = false; 327 328 @Override backupFinished(int status)329 public void backupFinished(int status) { 330 Log.i(TAG, "backupFinished with status=:" + status); 331 mDone = true; 332 } 333 waitForCompletion()334 public void waitForCompletion() throws Exception { 335 TestUtils.waitUntil( 336 "Waiting for requestBackup to complete for " + KEY_VALUE_RESTORE_APP_PACKAGE, 337 REQUEST_BACKUP_TIMEOUT_SECS, 338 () -> mDone); 339 } 340 } 341 } 342