• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static org.junit.Assert.fail;
20 
21 import com.android.compatibility.common.util.BackupHostSideUtils;
22 import com.android.compatibility.common.util.BackupUtils;
23 import com.android.tradefed.build.IBuildInfo;
24 import com.android.tradefed.config.Option;
25 import com.android.tradefed.config.OptionClass;
26 import com.android.tradefed.device.CollectingOutputReceiver;
27 import com.android.tradefed.device.DeviceNotAvailableException;
28 import com.android.tradefed.device.ITestDevice;
29 import com.android.tradefed.log.LogUtil.CLog;
30 import com.android.tradefed.targetprep.BaseTargetPreparer;
31 import com.android.tradefed.targetprep.BuildError;
32 import com.android.tradefed.targetprep.TargetSetupError;
33 import com.android.tradefed.util.RunUtil;
34 
35 import java.io.IOException;
36 import java.util.concurrent.Callable;
37 import java.util.concurrent.CompletionException;
38 import java.util.concurrent.TimeUnit;
39 import java.util.function.Function;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42 
43 /**
44  * Tradedfed target preparer for the backup tests. Enables backup before all the tests and selects
45  * local transport. Reverts to the original state after all the tests are executed.
46  */
47 @OptionClass(alias = "backup-preparer")
48 public class BackupPreparer extends BaseTargetPreparer {
49     private static final long TRANSPORT_AVAILABLE_TIMEOUT_SECONDS = TimeUnit.MINUTES.toSeconds(5);
50     @Option(name="enable-backup-if-needed", description=
51             "Enable backup before all the tests and return to the original state after.")
52     private boolean mEnableBackup = true;
53 
54     @Option(name="select-local-transport", description=
55             "Select local transport before all the tests and return to the original transport "
56                     + "after.")
57     private boolean mSelectLocalTransport = true;
58 
59     /** Value of PackageManager.FEATURE_BACKUP */
60     private static final String FEATURE_BACKUP = "android.software.backup";
61 
62     private static final String LOCAL_TRANSPORT =
63             "com.android.localtransport/.LocalTransport";
64     private final int USER_SYSTEM = 0;
65 
66     private boolean mIsBackupSupported;
67     private boolean mWasBackupEnabled;
68     private String mOldTransport;
69     private ITestDevice mDevice;
70     private BackupUtils mBackupUtils;
71 
72     @Override
setUp(ITestDevice device, IBuildInfo buildInfo)73     public void setUp(ITestDevice device, IBuildInfo buildInfo)
74             throws TargetSetupError, BuildError, DeviceNotAvailableException {
75         mDevice = device;
76         mBackupUtils = BackupHostSideUtils.createBackupUtils(mDevice);
77         mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
78 
79         // In case the device was just rebooted, wait for the broadcast queue to get idle to avoid
80         // any interference from services doing backup clean up on reboot.
81         waitForBroadcastIdle();
82 
83         if (mIsBackupSupported) {
84             BackupHostSideUtils.checkSetupComplete(mDevice);
85             if (!isBackupActiveForSysytemUser()) {
86                 throw new TargetSetupError("Cannot run test as backup is not active for system "
87                         + "user", device.getDeviceDescriptor());
88             }
89 
90             // Enable backup and select local backup transport
91             waitForTransport(LOCAL_TRANSPORT);
92 
93             if (mEnableBackup) {
94                 CLog.i("Enabling backup on %s", mDevice.getSerialNumber());
95                 mWasBackupEnabled = enableBackup(true);
96                 CLog.d("Backup was enabled? : %s", mWasBackupEnabled);
97                 if (mSelectLocalTransport) {
98                     CLog.i("Selecting local transport on %s", mDevice.getSerialNumber());
99                     mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
100                     CLog.d("Old transport : %s", mOldTransport);
101                 }
102                 try {
103                     mBackupUtils.waitForBackupInitialization();
104                 } catch (IOException e) {
105                     throw new TargetSetupError("Backup not initialized", e);
106                 }
107             }
108         }
109     }
110 
111     @Override
tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)112     public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
113             throws DeviceNotAvailableException {
114         mDevice = device;
115 
116         if (mIsBackupSupported) {
117             if (mEnableBackup) {
118                 CLog.i("Returning backup to it's previous state on %s",
119                         mDevice.getSerialNumber());
120                 enableBackup(mWasBackupEnabled);
121                 if (mSelectLocalTransport) {
122                     CLog.i("Returning selected transport to it's previous value on %s",
123                             mDevice.getSerialNumber());
124                     setBackupTransport(mOldTransport);
125                 }
126             }
127         }
128     }
129 
waitForTransport(String transport)130     private void waitForTransport(String transport) throws TargetSetupError {
131         try {
132             waitUntilWithLastTry(
133                     "Local transport didn't become available",
134                     TRANSPORT_AVAILABLE_TIMEOUT_SECONDS,
135                     lastTry -> uncheck(() -> hasBackupTransport(transport, lastTry)));
136         } catch (InterruptedException e) {
137             throw new TargetSetupError(
138                     "Device should have LocalTransport available", mDevice.getDeviceDescriptor());
139         }
140     }
141 
hasBackupTransport(String transport, boolean logIfFail)142     private boolean hasBackupTransport(String transport, boolean logIfFail)
143             throws DeviceNotAvailableException, TargetSetupError {
144         String output = mDevice.executeShellCommand("bmgr list transports");
145         for (String t : output.split(" ")) {
146             if (transport.equals(t.trim())) {
147                 return true;
148             }
149         }
150         if (logIfFail) {
151             throw new TargetSetupError(
152                     transport + " not available. bmgr list transports: " + output,
153                     mDevice.getDeviceDescriptor());
154         }
155         return false;
156     }
157 
158     /**
159      * Calls {@code predicate} with {@code false} until time-out {@code timeoutSeconds} is reached,
160      * if {@code predicate} returns true, method returns. If time-out is reached before that, we
161      * call {@code predicate} with {@code true} one last time, if that last call returns false we
162      * fail with {@code message}.
163      *
164      * TODO: Move to CommonTestUtils
165      */
waitUntilWithLastTry( String message, long timeoutSeconds, Function<Boolean, Boolean> predicate)166     private static void waitUntilWithLastTry(
167             String message, long timeoutSeconds, Function<Boolean, Boolean> predicate)
168             throws InterruptedException {
169         int sleep = 125;
170         final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000;
171         while (System.currentTimeMillis() < timeout) {
172             if (predicate.apply(false)) {
173                 return;
174             }
175             RunUtil.getDefault().sleep(sleep);
176         }
177         if (!predicate.apply(true)) {
178             fail(message);
179         }
180     }
181 
182     // Copied over from BackupQuotaTest
enableBackup(boolean enable)183     private boolean enableBackup(boolean enable) throws DeviceNotAvailableException {
184         boolean previouslyEnabled;
185         String output = mDevice.executeShellCommand("bmgr enabled");
186         Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
187         Matcher matcher = pattern.matcher(output.trim());
188         if (matcher.find()) {
189             previouslyEnabled = "enabled".equals(matcher.group(1));
190         } else {
191             throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
192         }
193 
194         mDevice.executeShellCommand("bmgr enable " + enable);
195         return previouslyEnabled;
196     }
197 
198     // Copied over from BackupQuotaTest
setBackupTransport(String transport)199     private String setBackupTransport(String transport) throws DeviceNotAvailableException {
200         String output = mDevice.executeShellCommand("bmgr transport " + transport);
201         Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$");
202         Matcher matcher = pattern.matcher(output);
203         if (matcher.find()) {
204             return matcher.group(1);
205         } else {
206             throw new RuntimeException("non-parsable output setting bmgr transport: " + output);
207         }
208     }
209 
210     // Copied over from BaseDevicePolicyTest
waitForBroadcastIdle()211     private void waitForBroadcastIdle() throws DeviceNotAvailableException, TargetSetupError {
212         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
213         try {
214             // we allow 20 min for the command to complete and 10 min for the command to start to
215             // output something
216             mDevice.executeShellCommand(
217                     "am wait-for-broadcast-idle", receiver, 20, 10, TimeUnit.MINUTES, 0);
218         } finally {
219             String output = receiver.getOutput();
220             CLog.d("Output from 'am wait-for-broadcast-idle': %s", output);
221             if (!output.contains("All broadcast queues are idle!")) {
222                 // the call most likely failed we should fail the test
223                 throw new TargetSetupError("'am wait-for-broadcase-idle' did not complete.",
224                         mDevice.getDeviceDescriptor());
225                 // TODO: consider adding a reboot or recovery before failing if necessary
226             }
227         }
228     }
229 
isBackupActiveForSysytemUser()230     private boolean isBackupActiveForSysytemUser() {
231         try {
232             return mBackupUtils.isBackupActivatedForUser(USER_SYSTEM);
233         } catch (IOException e) {
234             throw new RuntimeException("Failed to check backup activation status");
235         }
236     }
237 
uncheck(Callable<T> callable)238     private static <T> T uncheck(Callable<T> callable) {
239         try {
240             return callable.call();
241         } catch (RuntimeException e) {
242             throw e;
243         } catch (Exception e) {
244             throw new CompletionException(e);
245         }
246     }
247 }
248