• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.tradefed.device.recovery;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.tradefed.command.ICommandScheduler;
20 import com.android.tradefed.command.ICommandScheduler.IScheduledInvocationListener;
21 import com.android.tradefed.config.ConfigurationException;
22 import com.android.tradefed.config.GlobalConfiguration;
23 import com.android.tradefed.config.Option;
24 import com.android.tradefed.device.DeviceAllocationState;
25 import com.android.tradefed.device.DeviceManager.FastbootDevice;
26 import com.android.tradefed.device.FreeDeviceState;
27 import com.android.tradefed.device.IDeviceManager;
28 import com.android.tradefed.device.IManagedTestDevice;
29 import com.android.tradefed.device.IMultiDeviceRecovery;
30 import com.android.tradefed.device.ITestDevice;
31 import com.android.tradefed.device.StubDevice;
32 import com.android.tradefed.invoker.IInvocationContext;
33 import com.android.tradefed.log.LogUtil.CLog;
34 import com.android.tradefed.util.QuotationAwareTokenizer;
35 
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * Generic base {@link IMultiDeviceRecovery} to run a tradefed configuration to do the recovery
43  * step.
44  */
45 public class RunConfigDeviceRecovery implements IMultiDeviceRecovery {
46 
47     @Option(name = "disable", description = "Completely disable the recoverer.")
48     private boolean mDisable = false;
49 
50     @Option(
51         name = "recovery-config-name",
52         description = "The configuration to be used on the device to recover.",
53         mandatory = true
54     )
55     private String mRecoveryConfigName = null;
56 
57     @Option(
58         name = "extra-arg",
59         description = "Extra arguments to be passed to the recovery " + "invocation."
60     )
61     private List<String> mExtraArgs = new ArrayList<>();
62 
63     @Override
recoverDevices(List<IManagedTestDevice> managedDevices)64     public void recoverDevices(List<IManagedTestDevice> managedDevices) {
65         if (mDisable) {
66             return;
67         }
68 
69         for (IManagedTestDevice device : managedDevices) {
70             if (DeviceAllocationState.Allocated.equals(device.getAllocationState())) {
71                 continue;
72             }
73             if (device.getIDevice() instanceof StubDevice
74                     && !(device.getIDevice() instanceof FastbootDevice)) {
75                 continue;
76             }
77             if (shouldSkip(device)) {
78                 continue;
79             }
80 
81             List<String> argList = new ArrayList<>();
82             argList.add(mRecoveryConfigName);
83 
84             List<String> deviceExtraArgs = getExtraArguments(device);
85             if (deviceExtraArgs == null) {
86                 CLog.w("Something went wrong recovery cannot be attempted.");
87                 continue;
88             }
89 
90             argList.addAll(deviceExtraArgs);
91             for (String args : mExtraArgs) {
92                 String[] extraArgs = QuotationAwareTokenizer.tokenizeLine(args);
93                 if (extraArgs.length != 0) {
94                     argList.addAll(Arrays.asList(extraArgs));
95                 }
96             }
97 
98             String serial = device.getSerialNumber();
99             ITestDevice deviceToRecover = getDeviceManager().forceAllocateDevice(serial);
100             if (deviceToRecover == null) {
101                 CLog.e("Fail to force allocate '%s'", serial);
102                 continue;
103             }
104             try {
105                 getCommandScheduler()
106                         .execCommand(
107                                 new FreeDeviceHandler(getDeviceManager()),
108                                 deviceToRecover,
109                                 argList.toArray(new String[0]));
110             } catch (ConfigurationException e) {
111                 CLog.e("Device multi recovery is misconfigured");
112                 CLog.e(e);
113                 // In this case, the device doesn't go through regular de-allocation so we
114                 // explicitly deallocate.
115                 getDeviceManager().freeDevice(device, FreeDeviceState.UNAVAILABLE);
116                 return;
117             }
118         }
119     }
120 
121     /**
122      * Get the list of extra arguments to be passed to the configuration. If null is returned
123      * something went wrong and recovery should be attempted.
124      *
125      * @param device The {@link ITestDevice} to run recovery against
126      * @return The list of extra arguments to be used. Or null if something went wrong.
127      */
getExtraArguments(ITestDevice device)128     public List<String> getExtraArguments(ITestDevice device) {
129         return new ArrayList<>();
130     }
131 
132     /**
133      * Extra chance to skip the recovery on a given device by doing extra checks.
134      *
135      * @param device The {@link IManagedTestDevice} considered for recovery.
136      * @return True if recovery should be skipped.
137      */
shouldSkip(IManagedTestDevice device)138     public boolean shouldSkip(IManagedTestDevice device) {
139         return false;
140     }
141 
142     /** Returns a {@link IDeviceManager} instance. Exposed for testing. */
143     @VisibleForTesting
getDeviceManager()144     protected IDeviceManager getDeviceManager() {
145         return GlobalConfiguration.getInstance().getDeviceManager();
146     }
147 
148     /** Returns a {@link ICommandScheduler} instance. Exposed for testing. */
149     @VisibleForTesting
getCommandScheduler()150     protected ICommandScheduler getCommandScheduler() {
151         return GlobalConfiguration.getInstance().getCommandScheduler();
152     }
153 
154     /** Handler to free up the device once the invocation completes */
155     private class FreeDeviceHandler implements IScheduledInvocationListener {
156 
157         private final IDeviceManager mDeviceManager;
158 
FreeDeviceHandler(IDeviceManager deviceManager)159         FreeDeviceHandler(IDeviceManager deviceManager) {
160             mDeviceManager = deviceManager;
161         }
162 
163         @Override
invocationComplete( IInvocationContext context, Map<ITestDevice, FreeDeviceState> devicesStates)164         public void invocationComplete(
165                 IInvocationContext context, Map<ITestDevice, FreeDeviceState> devicesStates) {
166             for (ITestDevice device : context.getDevices()) {
167                 mDeviceManager.freeDevice(device, devicesStates.get(device));
168                 if (device instanceof IManagedTestDevice) {
169                     // This is quite an important setting so we do make sure it's reset.
170                     ((IManagedTestDevice) device).setFastbootPath(mDeviceManager.getFastbootPath());
171                 }
172             }
173         }
174     }
175 }
176