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; 18 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.config.OptionClass; 22 import com.android.tradefed.device.CollectingOutputReceiver; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.log.LogUtil.CLog; 26 import com.android.tradefed.targetprep.BuildError; 27 import com.android.tradefed.targetprep.ITargetCleaner; 28 import com.android.tradefed.targetprep.TargetSetupError; 29 30 import java.util.concurrent.TimeUnit; 31 import java.util.regex.Matcher; 32 import java.util.regex.Pattern; 33 34 /** 35 * Tradedfed target preparer for the backup tests. 36 * Enables backup before all the tests and selects local transport. 37 * Reverts to the original state after all the tests are executed. 38 */ 39 @OptionClass(alias = "backup-preparer") 40 public class BackupPreparer implements ITargetCleaner { 41 @Option(name="enable-backup-if-needed", description= 42 "Enable backup before all the tests and return to the original state after.") 43 private boolean mEnableBackup = true; 44 45 @Option(name="select-local-transport", description= 46 "Select local transport before all the tests and return to the original transport " 47 + "after.") 48 private boolean mSelectLocalTransport = true; 49 50 /** Value of PackageManager.FEATURE_BACKUP */ 51 private static final String FEATURE_BACKUP = "android.software.backup"; 52 53 private static final String LOCAL_TRANSPORT = 54 "com.android.localtransport/.LocalTransport"; 55 56 private static final int BACKUP_PROVISIONING_TIMEOUT_SECONDS = 30; 57 private static final int BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS = 1; 58 59 private boolean mIsBackupSupported; 60 private boolean mWasBackupEnabled; 61 private String mOldTransport; 62 private ITestDevice mDevice; 63 64 @Override setUp(ITestDevice device, IBuildInfo buildInfo)65 public void setUp(ITestDevice device, IBuildInfo buildInfo) 66 throws TargetSetupError, BuildError, DeviceNotAvailableException { 67 mDevice = device; 68 69 mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP); 70 71 // In case the device was just rebooted, wait for the broadcast queue to get idle to avoid 72 // any interference from services doing backup clean up on reboot. 73 waitForBroadcastIdle(); 74 75 if (mIsBackupSupported) { 76 // Enable backup and select local backup transport 77 if (!hasBackupTransport(LOCAL_TRANSPORT)) { 78 throw new TargetSetupError("Device should have LocalTransport available", 79 device.getDeviceDescriptor()); 80 } 81 if (mEnableBackup) { 82 CLog.i("Enabling backup on %s", mDevice.getSerialNumber()); 83 mWasBackupEnabled = enableBackup(true); 84 CLog.d("Backup was enabled? : %s", mWasBackupEnabled); 85 if (mSelectLocalTransport) { 86 CLog.i("Selecting local transport on %s", mDevice.getSerialNumber()); 87 mOldTransport = setBackupTransport(LOCAL_TRANSPORT); 88 CLog.d("Old transport : %s", mOldTransport); 89 } 90 waitForBackupInitialization(); 91 } 92 } 93 } 94 95 @Override tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)96 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 97 throws DeviceNotAvailableException { 98 mDevice = device; 99 100 if (mIsBackupSupported) { 101 if (mEnableBackup) { 102 CLog.i("Returning backup to it's previous state on %s", mDevice.getSerialNumber()); 103 enableBackup(mWasBackupEnabled); 104 if (mSelectLocalTransport) { 105 CLog.i("Returning selected transport to it's previous value on %s", 106 mDevice.getSerialNumber()); 107 setBackupTransport(mOldTransport); 108 } 109 } 110 } 111 } 112 113 // Copied over from BackupQuotaTest hasBackupTransport(String transport)114 private boolean hasBackupTransport(String transport) throws DeviceNotAvailableException { 115 String output = mDevice.executeShellCommand("bmgr list transports"); 116 for (String t : output.split(" ")) { 117 if (transport.equals(t.trim())) { 118 return true; 119 } 120 } 121 return false; 122 } 123 124 // Copied over from BackupQuotaTest enableBackup(boolean enable)125 private boolean enableBackup(boolean enable) throws DeviceNotAvailableException { 126 boolean previouslyEnabled; 127 String output = mDevice.executeShellCommand("bmgr enabled"); 128 Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$"); 129 Matcher matcher = pattern.matcher(output.trim()); 130 if (matcher.find()) { 131 previouslyEnabled = "enabled".equals(matcher.group(1)); 132 } else { 133 throw new RuntimeException("non-parsable output setting bmgr enabled: " + output); 134 } 135 136 mDevice.executeShellCommand("bmgr enable " + enable); 137 return previouslyEnabled; 138 } 139 140 // Copied over from BackupQuotaTest setBackupTransport(String transport)141 private String setBackupTransport(String transport) throws DeviceNotAvailableException { 142 String output = mDevice.executeShellCommand("bmgr transport " + transport); 143 Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$"); 144 Matcher matcher = pattern.matcher(output); 145 if (matcher.find()) { 146 return matcher.group(1); 147 } else { 148 throw new RuntimeException("non-parsable output setting bmgr transport: " + output); 149 } 150 } 151 waitForBackupInitialization()152 private void waitForBackupInitialization() 153 throws TargetSetupError, DeviceNotAvailableException { 154 long tryUntilNanos = System.nanoTime() 155 + TimeUnit.SECONDS.toNanos(BACKUP_PROVISIONING_TIMEOUT_SECONDS); 156 while (System.nanoTime() < tryUntilNanos) { 157 String output = mDevice.executeShellCommand("dumpsys backup"); 158 if (output.matches("(?s)" // DOTALL 159 + "^Backup Manager is .* not pending init.*")) { 160 return; 161 } 162 try { 163 Thread.sleep(TimeUnit.SECONDS.toMillis(BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS)); 164 } catch (InterruptedException e) { 165 Thread.currentThread().interrupt(); 166 break; 167 } 168 } 169 throw new TargetSetupError("Timed out waiting for backup initialization", 170 mDevice.getDeviceDescriptor()); 171 } 172 173 // Copied over from BaseDevicePolicyTest waitForBroadcastIdle()174 private void waitForBroadcastIdle() throws DeviceNotAvailableException, TargetSetupError { 175 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 176 try { 177 // we allow 20 min for the command to complete and 10 min for the command to start to 178 // output something 179 mDevice.executeShellCommand( 180 "am wait-for-broadcast-idle", receiver, 20, 10, TimeUnit.MINUTES, 0); 181 } finally { 182 String output = receiver.getOutput(); 183 CLog.d("Output from 'am wait-for-broadcast-idle': %s", output); 184 if (!output.contains("All broadcast queues are idle!")) { 185 // the call most likely failed we should fail the test 186 throw new TargetSetupError("'am wait-for-broadcase-idle' did not complete.", 187 mDevice.getDeviceDescriptor()); 188 // TODO: consider adding a reboot or recovery before failing if necessary 189 } 190 } 191 } 192 193 } 194