1 /* 2 * Copyright (C) 2018 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; 18 19 import static org.junit.Assert.assertNull; 20 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 24 25 import org.junit.After; 26 import org.junit.Before; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 34 /** 35 * Tests for checking that an observer app is notified by a broadcast Intent whenever a backup 36 * succeeds. 37 * 38 * NB: The tests use "bmgr backupnow" for backup, which works on N+ devices. 39 */ 40 @RunWith(DeviceJUnit4ClassRunner.class) 41 public class SuccessNotificationHostSideTest extends BaseBackupHostSideTest { 42 43 /** The name of the package that a key/value backup will be run for */ 44 private static final String KEY_VALUE_BACKUP_APP_PACKAGE = 45 "android.cts.backup.keyvaluerestoreapp"; 46 47 /** The name of the package that a full backup will be run for */ 48 private static final String FULL_BACKUP_APP_PACKAGE = "android.cts.backup.fullbackuponlyapp"; 49 50 /** The name of the package that observes backup results. */ 51 private static final String SUCCESS_NOTIFICATION_APP_PACKAGE = 52 "android.cts.backup.successnotificationapp"; 53 54 /** The name of the device side test class in the APK that a key/value backup will be run for */ 55 private static final String KEY_VALUE_BACKUP_DEVICE_TEST_NAME = 56 KEY_VALUE_BACKUP_APP_PACKAGE + ".KeyValueBackupRestoreTest"; 57 58 /** The name of the device side test class in the APK that a full backup will be run for */ 59 private static final String FULL_BACKUP_DEVICE_TEST_CLASS_NAME = 60 FULL_BACKUP_APP_PACKAGE + ".FullBackupOnlyTest"; 61 62 /** The name of the device side test class in the APK that observes backup results */ 63 private static final String SUCCESS_NOTIFICATION_DEVICE_TEST_NAME = 64 SUCCESS_NOTIFICATION_APP_PACKAGE + ".SuccessNotificationTest"; 65 66 /** The name of the APK that a key/value backup will be run for */ 67 private static final String KEY_VALUE_BACKUP_APP_APK = "CtsKeyValueBackupRestoreApp.apk"; 68 69 /** The name of the APK that a full backup will be run for */ 70 private static final String FULL_BACKUP_APP_APK = "FullBackupOnlyFalseWithAgentApp.apk"; 71 72 /** The name of the APK that observes backup results */ 73 private static final String SUCCESS_NOTIFICATION_APP_APK = 74 "CtsBackupSuccessNotificationApp.apk"; 75 76 /** Secure setting that holds the backup manager configuration as key-value pairs */ 77 private static final String BACKUP_MANAGER_CONSTANTS_PREF = "backup_manager_constants"; 78 79 /** Key for specifying the apps that the backup manager should notify of successful backups */ 80 private static final String BACKUP_FINISHED_NOTIFICATION_RECEIVERS = 81 "backup_finished_notification_receivers"; 82 83 /** The original backup manager configuration */ 84 private String mOriginalBackupManagerConstants = null; 85 86 @Before 87 @Override 88 public void setUp() throws Exception { 89 super.setUp(); 90 91 if (!mIsBackupSupported) { 92 CLog.i("android.software.backup feature is not supported on this device"); 93 return; 94 } 95 96 installPackage(KEY_VALUE_BACKUP_APP_APK); 97 installPackage(FULL_BACKUP_APP_APK); 98 99 installPackage(SUCCESS_NOTIFICATION_APP_APK); 100 checkDeviceTest("clearBackupSuccessNotificationsReceived"); 101 addBackupFinishedNotificationReceiver(); 102 } 103 104 @After 105 @Override 106 public void tearDown() throws Exception { 107 super.tearDown(); 108 109 if (!mIsBackupSupported) { 110 return; 111 } 112 113 restoreBackupFinishedNotificationReceivers(); 114 assertNull(uninstallPackage(SUCCESS_NOTIFICATION_APP_PACKAGE)); 115 116 // Clear backup data and uninstall the package (in that order!) 117 clearBackupDataInLocalTransport(KEY_VALUE_BACKUP_APP_PACKAGE); 118 assertNull(uninstallPackage(KEY_VALUE_BACKUP_APP_PACKAGE)); 119 120 clearBackupDataInLocalTransport(FULL_BACKUP_APP_PACKAGE); 121 assertNull(uninstallPackage(FULL_BACKUP_APP_PACKAGE)); 122 } 123 124 /** 125 * Test that the observer app is notified when a key/value backup succeeds. 126 * 127 * Test logic: 128 * 1. Change a test app's data, trigger a key/value backup and wait for it to complete. 129 * 2. Verify that the observer app was informed about the backup. 130 */ 131 @Test 132 public void testSuccessNotificationForKeyValueBackup() throws Exception { 133 if (!mIsBackupSupported) { 134 return; 135 } 136 137 checkDeviceTest(KEY_VALUE_BACKUP_APP_PACKAGE, KEY_VALUE_BACKUP_DEVICE_TEST_NAME, 138 "saveSharedPreferencesAndNotifyBackupManager"); 139 backupNowAndAssertSuccess(KEY_VALUE_BACKUP_APP_PACKAGE); 140 141 checkDeviceTest("verifyBackupSuccessNotificationReceivedForKeyValueApp"); 142 } 143 144 /** 145 * Test that the observer app is notified when a full backup succeeds. 146 * 147 * Test logic: 148 * 1. Change a test app's data, trigger a full backup and wait for it to complete. 149 * 2. Verify that the observer app was informed about the backup. 150 */ 151 @Test 152 public void testSuccessNotificationForFullBackup() throws Exception { 153 if (!mIsBackupSupported) { 154 return; 155 } 156 157 checkDeviceTest(FULL_BACKUP_APP_PACKAGE, FULL_BACKUP_DEVICE_TEST_CLASS_NAME, "createFiles"); 158 backupNowAndAssertSuccess(FULL_BACKUP_APP_PACKAGE); 159 160 checkDeviceTest("verifyBackupSuccessNotificationReceivedForFullBackupApp"); 161 } 162 163 /** 164 * Instructs the backup manager to notify the observer app whenever a backup succeeds. The old 165 * backup manager configuration is stored in a member variable and can be restored by calling 166 * {@link restoreBackupFinishedNotificationReceivers}. 167 */ 168 private void addBackupFinishedNotificationReceiver() 169 throws DeviceNotAvailableException { 170 mOriginalBackupManagerConstants = getDevice().executeShellCommand(String.format( 171 "settings get secure %s", BACKUP_MANAGER_CONSTANTS_PREF)).trim(); 172 if ("null".equals(mOriginalBackupManagerConstants)) { 173 mOriginalBackupManagerConstants = null; 174 } 175 String backupManagerConstants = null; 176 177 if (mOriginalBackupManagerConstants == null || mOriginalBackupManagerConstants.isEmpty()) { 178 backupManagerConstants = 179 BACKUP_FINISHED_NOTIFICATION_RECEIVERS + "=" + SUCCESS_NOTIFICATION_APP_PACKAGE; 180 } else { 181 final List<String> keyValuePairs = 182 new ArrayList<>(Arrays.asList(mOriginalBackupManagerConstants.split(","))); 183 boolean present = false; 184 for (int i = 0; i < keyValuePairs.size(); ++i) { 185 final String keyValuePair = keyValuePairs.get(i); 186 final String[] fields = keyValuePair.split("="); 187 final String key = fields[0].trim(); 188 if (BACKUP_FINISHED_NOTIFICATION_RECEIVERS.equals(key)) { 189 if (fields.length == 1 || fields[1].trim().isEmpty()) { 190 keyValuePairs.set(i, key + "=" + SUCCESS_NOTIFICATION_APP_PACKAGE); 191 } else { 192 final String[] values = fields[1].split(":"); 193 for (int j = 0; j < values.length; ++j) { 194 if (SUCCESS_NOTIFICATION_APP_PACKAGE.equals(values[j].trim())) { 195 present = true; 196 break; 197 } 198 } 199 if (!present) { 200 keyValuePairs.set(i, 201 keyValuePair + ":" + SUCCESS_NOTIFICATION_APP_PACKAGE); 202 } 203 } 204 present = true; 205 break; 206 } 207 } 208 if (!present) { 209 keyValuePairs.add(BACKUP_FINISHED_NOTIFICATION_RECEIVERS + "=" + 210 SUCCESS_NOTIFICATION_APP_PACKAGE); 211 } 212 backupManagerConstants = String.join(",", keyValuePairs); 213 } 214 setBackupManagerConstants(backupManagerConstants); 215 } 216 217 /** 218 * Restores the backup manager configuration stored by a previous call to 219 * {@link addBackupFinishedNotificationReceiver}. 220 */ 221 private void restoreBackupFinishedNotificationReceivers() throws DeviceNotAvailableException { 222 setBackupManagerConstants(mOriginalBackupManagerConstants); 223 } 224 225 private void setBackupManagerConstants(String backupManagerConstants) 226 throws DeviceNotAvailableException { 227 getDevice().executeShellCommand(String.format("settings put secure %s %s", 228 BACKUP_MANAGER_CONSTANTS_PREF, backupManagerConstants)); 229 } 230 231 private void checkDeviceTest(String methodName) throws DeviceNotAvailableException { 232 checkDeviceTest(SUCCESS_NOTIFICATION_APP_PACKAGE, SUCCESS_NOTIFICATION_DEVICE_TEST_NAME, 233 methodName); 234 } 235 } 236