1 /*
2  * Copyright (C) 2021 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.internal;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.tradefed.device.DeviceNotAvailableException;
20 import com.android.tradefed.device.ITestDevice;
21 import com.android.tradefed.device.NativeDevice;
22 import com.android.tradefed.device.StubDevice;
23 import com.android.tradefed.error.HarnessRuntimeException;
24 import com.android.tradefed.error.IHarnessException;
25 import com.android.tradefed.invoker.IInvocationContext;
26 import com.android.tradefed.invoker.logger.CurrentInvocation;
27 import com.android.tradefed.invoker.logger.CurrentInvocation.IsolationGrade;
28 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
29 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
30 import com.android.tradefed.log.LogUtil.CLog;
31 import com.android.tradefed.result.error.InfraErrorIdentifier;
32 import com.android.tradefed.service.TradefedFeatureClient;
33 import com.android.tradefed.util.SerializationUtil;
34 
35 import com.proto.tradefed.feature.FeatureResponse;
36 
37 import java.io.IOException;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42 
43 /**
44  * Utility handling generically device resetting. This is meant to only be used internally to the
45  * test harness. This shouldn't be called during a test.
46  */
47 public class DeviceResetHandler {
48 
49     private final TradefedFeatureClient mClient;
50     private final IInvocationContext mContext;
51 
DeviceResetHandler()52     public DeviceResetHandler() {
53         this(new TradefedFeatureClient(), CurrentInvocation.getInvocationContext());
54     }
55 
DeviceResetHandler(IInvocationContext context)56     public DeviceResetHandler(IInvocationContext context) {
57         this(new TradefedFeatureClient(), context);
58     }
59 
60     @VisibleForTesting
DeviceResetHandler(TradefedFeatureClient client, IInvocationContext context)61     DeviceResetHandler(TradefedFeatureClient client, IInvocationContext context) {
62         mClient = client;
63         mContext = context;
64     }
65 
66     /**
67      * Calls reset of the given device.
68      *
69      * @param device The device to reset.
70      * @return True if reset was successful, false otherwise.
71      * @throws DeviceNotAvailableException
72      */
resetDevice(ITestDevice device)73     public boolean resetDevice(ITestDevice device) throws DeviceNotAvailableException {
74         if (device.getIDevice() instanceof StubDevice) {
75             CLog.d("Device '%s' is a stub device. skipping reset.", device.getSerialNumber());
76             return true;
77         }
78         FeatureResponse response;
79         try {
80             Map<String, String> args = new HashMap<>();
81             args.put(DeviceResetFeature.DEVICE_NAME, mContext.getDeviceName(device));
82             response = mClient.triggerFeature(DeviceResetFeature.DEVICE_RESET_FEATURE_NAME, args);
83             CLog.d("Response from reset request: %s", response.getResponse());
84         } finally {
85             mClient.close();
86         }
87         if (response.hasErrorInfo()) {
88             String trace = response.getErrorInfo().getErrorTrace();
89             // Handle if it's an exception error.
90             Object o = null;
91             try {
92                 o = SerializationUtil.deserialize(trace);
93                 CLog.e("Reset failed: %s", o);
94             } catch (IOException | RuntimeException e) {
95                 CLog.e(e);
96             }
97             if (o instanceof DeviceNotAvailableException) {
98                 throw (DeviceNotAvailableException) o;
99             } else if (o instanceof IHarnessException) {
100                 IHarnessException exception = (IHarnessException) o;
101                 throw new HarnessRuntimeException(
102                         "Exception while resetting the device.", exception);
103             } else if (o instanceof Exception) {
104                 throw new HarnessRuntimeException(
105                         "Exception while resetting the device.",
106                         (Exception) o, InfraErrorIdentifier.UNDETERMINED);
107             }
108             return false;
109         }
110         if (device instanceof NativeDevice) {
111             ((NativeDevice) device).resetContentProviderSetup();
112         }
113         CurrentInvocation.setModuleIsolation(IsolationGrade.FULLY_ISOLATED);
114         CurrentInvocation.setRunIsolation(IsolationGrade.FULLY_ISOLATED);
115 
116         // Save powerwash performance data
117         Pattern durationPattern = Pattern.compile("Powerwash\\sfinished\\sin (\\d+)\\sms");
118         Matcher matcher;
119         matcher = durationPattern.matcher(response.getResponse());
120         if (matcher.find()) {
121             InvocationMetricLogger.addInvocationMetrics(InvocationMetricKey.DEVICE_RESET_COUNT, 1);
122             InvocationMetricLogger.addInvocationMetrics(
123                     InvocationMetricKey.DEVICE_POWERWASH_DURATIONS, matcher.group(1));
124         }
125         return true;
126     }
127 }
128