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