1 /*
2  * Copyright (C) 2013 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.tradefed.targetprep;
18 
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.config.OptionClass;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.device.DeviceUnresponsiveException;
23 import com.android.tradefed.device.ITestDevice;
24 import com.android.tradefed.device.TestDeviceState;
25 import com.android.tradefed.invoker.TestInformation;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.tradefed.util.RunUtil;
28 
29 /** Performs reboot or format as cleanup action after test, and optionally turns screen off */
30 @OptionClass(alias = "device-cleaner")
31 public class DeviceCleaner extends BaseTargetPreparer {
32 
33     public static enum CleanupAction {
34         /** no cleanup action */
35         NONE,
36         /** reboot the device as post test cleanup */
37         REBOOT,
38         /** format userdata and cache partitions as post test cleanup */
39         FORMAT,
40     }
41 
42     public static enum PostCleanupAction {
43         /** no post cleanup action */
44         NONE,
45         /** turns screen off after the cleanup action */
46         SCREEN_OFF,
47         /** turns off screen and stops runtime after the cleanup action */
48         SCREEN_OFF_AND_STOP,
49     }
50 
51     private static final int MAX_SCREEN_OFF_RETRY = 5;
52     private static final int SCREEN_OFF_RETRY_DELAY_MS = 2 * 1000;
53 
54     @Option(name = "cleanup-action",
55             description = "Type of action to perform as a post test cleanup; options are: "
56             + "NONE, REBOOT or FORMAT; defaults to NONE")
57     private CleanupAction mCleanupAction = CleanupAction.NONE;
58 
59     /**
60      * @deprecated use --post-cleanup SCREEN_OFF option instead.
61      */
62     @Deprecated
63     @Option(name = "screen-off", description = "After cleanup action, "
64             + "if screen should be turned off; defaults to false; "
65             + "[deprecated] use --post-cleanup SCREEN_OFF instead")
66     private boolean mScreenOff = false;
67 
68     @Option(name = "post-cleanup",
69             description = "Type of action to perform after the cleanup action;"
70             + "this will override the deprecated screen-off action if specified")
71     private PostCleanupAction mPostCleanupAction = PostCleanupAction.NONE;
72 
73     @Override
setUp(TestInformation testInfo)74     public void setUp(TestInformation testInfo)
75             throws TargetSetupError, BuildError, DeviceNotAvailableException {
76         // no op since this is a target cleaner
77     }
78 
79     @Override
tearDown(TestInformation testInfo, Throwable e)80     public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
81         if (e instanceof DeviceFailedToBootError) {
82             CLog.w("boot failure: attempting to stop runtime instead of cleanup");
83             try {
84                 testInfo.getDevice().executeShellCommand("stop");
85             } catch (DeviceUnresponsiveException due) {
86                 CLog.w("boot failure: ignored DeviceUnresponsiveException during forced cleanup");
87             }
88         } else {
89             clean(testInfo.getDevice());
90         }
91     }
92 
93     /**
94      * Execute cleanup action followed by post cleanup action
95      */
clean(ITestDevice device)96     protected void clean(ITestDevice device) throws DeviceNotAvailableException {
97         if (TestDeviceState.ONLINE.equals(device.getDeviceState())) {
98             switch (mCleanupAction) {
99                 case NONE:
100                     // do nothing here
101                     break;
102                 case REBOOT:
103                     device.reboot();
104                     break;
105                 case FORMAT:
106                     device.rebootIntoBootloader();
107                     device.executeLongFastbootCommand("-w");
108                     device.executeFastbootCommand("reboot");
109                     device.waitForDeviceAvailable();
110                     break;
111             }
112             if (mScreenOff && mPostCleanupAction == PostCleanupAction.NONE) {
113                 mPostCleanupAction = PostCleanupAction.SCREEN_OFF;
114             }
115             // perform post cleanup action
116             switch (mPostCleanupAction) {
117                 case NONE:
118                     // do nothing here
119                     break;
120                 case SCREEN_OFF:
121                     turnScreenOff(device);
122                     break;
123                 case SCREEN_OFF_AND_STOP:
124                     turnScreenOff(device);
125                     device.executeShellCommand("stop");
126                     break;
127             }
128         }
129     }
130 
turnScreenOff(ITestDevice device)131     private void turnScreenOff(ITestDevice device) throws DeviceNotAvailableException {
132         String output =
133                 device.executeShellCommand("dumpsys power | grep -e mScreenOn -e mInteractive");
134         int retries = 1;
135         // screen on semantics have changed in newest API platform, checking for both signatures
136         // to detect screen on state
137         while (output.contains("mScreenOn=true") || output.contains("mInteractive=true")) {
138             // KEYCODE_POWER = 26
139             device.executeShellCommand("input keyevent 26");
140             // due to framework initialization, device may not actually turn off screen
141             // after boot, recheck screen status with linear backoff
142             RunUtil.getDefault().sleep(SCREEN_OFF_RETRY_DELAY_MS * retries);
143             output =
144                     device.executeShellCommand("dumpsys power | grep -e mScreenOn -e mInteractive");
145             retries++;
146             if (retries > MAX_SCREEN_OFF_RETRY) {
147                 CLog.w(String.format("screen still on after %d retries", retries));
148                 break;
149             }
150         }
151     }
152 }
153