• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 Cuttlefish snapshot. This is meant to only be used internally to the test
45  * harness. This shouldn't be called during a test.
46  */
47 public class DeviceSnapshotHandler {
48 
49     private final TradefedFeatureClient mClient;
50     private final IInvocationContext mContext;
51 
DeviceSnapshotHandler()52     public DeviceSnapshotHandler() {
53         this(new TradefedFeatureClient(), CurrentInvocation.getInvocationContext());
54     }
55 
56     @VisibleForTesting
DeviceSnapshotHandler(TradefedFeatureClient client, IInvocationContext context)57     DeviceSnapshotHandler(TradefedFeatureClient client, IInvocationContext context) {
58         mClient = client;
59         mContext = context;
60     }
61 
62     /**
63      * Calls delete snapshot of the given device.
64      *
65      * @param device The device to delete a snapshot. Needed to get user.
66      * @param snapshotId Snapshot ID to delete.
67      * @return True if deleting snapshot was successful, false otherwise.
68      * @throws DeviceNotAvailableException
69      */
deleteSnapshot(ITestDevice device, String snapshotId)70     public void deleteSnapshot(ITestDevice device, String snapshotId)
71             throws DeviceNotAvailableException {
72         if (device.getIDevice() instanceof StubDevice) {
73             CLog.d(
74                     "Device '%s' is a stub device. skipping deleting snapshot.",
75                     device.getSerialNumber());
76             return;
77         }
78         FeatureResponse response;
79         try {
80             Map<String, String> args = new HashMap<>();
81             args.put(DeviceSnapshotFeature.SNAPSHOT_ID, snapshotId);
82             args.put(DeviceSnapshotFeature.DEVICE_NAME, mContext.getDeviceName(device));
83             args.put(DeviceSnapshotFeature.DELETE_FLAG, "true");
84             response =
85                     mClient.triggerFeature(
86                             DeviceSnapshotFeature.DEVICE_SNAPSHOT_FEATURE_NAME, args);
87             CLog.d(
88                     "Response from deleting snapshot(%s) request: %s",
89                     snapshotId, response.getResponse());
90         } finally {
91             mClient.close();
92         }
93         if (response.hasErrorInfo()) {
94             String trace = response.getErrorInfo().getErrorTrace();
95             // Handle if it's an exception error.
96             Object o = null;
97             try {
98                 o = SerializationUtil.deserialize(trace);
99             } catch (IOException | RuntimeException e) {
100                 CLog.e("Failed to deserialize delete snapshot error response: %s", e.getMessage());
101             }
102             if (o instanceof DeviceNotAvailableException) {
103                 throw (DeviceNotAvailableException) o;
104             } else if (o instanceof IHarnessException) {
105                 IHarnessException exception = (IHarnessException) o;
106                 throw new HarnessRuntimeException("Exception while deleting snapshot.", exception);
107             } else if (o instanceof Exception) {
108                 throw new HarnessRuntimeException(
109                         "Exception while deleting snapshot.",
110                         (Exception) o,
111                         InfraErrorIdentifier.UNDETERMINED);
112             }
113             throw new HarnessRuntimeException(
114                     "Exception while deleting snapshot. Unserialized error response: " + trace,
115                     InfraErrorIdentifier.UNDETERMINED);
116         }
117     }
118 
119     /**
120      * Calls snapshot of the given device.
121      *
122      * @param device The device to snapshot.
123      * @param snapshotId Snapshot ID for the device to be saved to.
124      * @return True if snapshot was successful, false otherwise.
125      * @throws DeviceNotAvailableException
126      */
snapshotDevice(ITestDevice device, String snapshotId)127     public void snapshotDevice(ITestDevice device, String snapshotId)
128             throws DeviceNotAvailableException {
129         if (device.getIDevice() instanceof StubDevice) {
130             CLog.d("Device '%s' is a stub device. skipping snapshot.", device.getSerialNumber());
131             return;
132         }
133         FeatureResponse response;
134         try {
135             Map<String, String> args = new HashMap<>();
136             args.put(DeviceSnapshotFeature.DEVICE_NAME, mContext.getDeviceName(device));
137             args.put(DeviceSnapshotFeature.SNAPSHOT_ID, snapshotId);
138             response =
139                     mClient.triggerFeature(
140                             DeviceSnapshotFeature.DEVICE_SNAPSHOT_FEATURE_NAME, args);
141             CLog.d("Response from snapshot request: %s", response.getResponse());
142         } finally {
143             mClient.close();
144         }
145         if (response.hasErrorInfo()) {
146             String trace = response.getErrorInfo().getErrorTrace();
147             // Handle if it's an exception error.
148             Object o = null;
149             try {
150                 o = SerializationUtil.deserialize(trace);
151             } catch (IOException | RuntimeException e) {
152                 CLog.e("Failed to deserialize snapshot error response: %s", e.getMessage());
153             }
154             if (o instanceof DeviceNotAvailableException) {
155                 throw (DeviceNotAvailableException) o;
156             } else if (o instanceof IHarnessException) {
157                 IHarnessException exception = (IHarnessException) o;
158                 throw new HarnessRuntimeException(
159                         "Exception while snapshotting the device.", exception);
160             } else if (o instanceof Exception) {
161                 throw new HarnessRuntimeException(
162                         "Exception while snapshotting the device.",
163                         (Exception) o,
164                         InfraErrorIdentifier.UNDETERMINED);
165             }
166             throw new HarnessRuntimeException(
167                     "Exception while snapshotting the device. Unserialized error response: "
168                             + trace,
169                     InfraErrorIdentifier.UNDETERMINED);
170         }
171 
172         // Save snapshot performance data
173         Pattern durationPattern = Pattern.compile("Snapshot\\sfinished\\sin (\\d+)\\sms");
174         Matcher matcher;
175         matcher = durationPattern.matcher(response.getResponse());
176         if (matcher.find()) {
177             InvocationMetricLogger.addInvocationMetrics(
178                     InvocationMetricKey.DEVICE_SNAPSHOT_SUCCESS_COUNT, 1);
179             InvocationMetricLogger.addInvocationMetrics(
180                     InvocationMetricKey.DEVICE_SNAPSHOT_DURATIONS, matcher.group(1));
181         } else {
182             InvocationMetricLogger.addInvocationMetrics(
183                     InvocationMetricKey.DEVICE_SNAPSHOT_FAILURE_COUNT, 1);
184         }
185     }
186 
187     /**
188      * Calls restore snapshot of the given device.
189      *
190      * @param device The device to restore.
191      * @param snapshotId Snapshot ID for the device to be restored to.
192      * @return True if restore was successful, false otherwise.
193      * @throws DeviceNotAvailableException
194      */
restoreSnapshotDevice(ITestDevice device, String snapshotId)195     public void restoreSnapshotDevice(ITestDevice device, String snapshotId)
196             throws DeviceNotAvailableException {
197         if (device.getIDevice() instanceof StubDevice) {
198             CLog.d(
199                     "Device '%s' is a stub device. skipping restoring snapshot.",
200                     device.getSerialNumber());
201             return;
202         }
203         FeatureResponse response;
204         try {
205             Map<String, String> args = new HashMap<>();
206             args.put(DeviceSnapshotFeature.SNAPSHOT_ID, snapshotId);
207             args.put(DeviceSnapshotFeature.RESTORE_FLAG, "true");
208             args.put(DeviceSnapshotFeature.DEVICE_NAME, mContext.getDeviceName(device));
209             response =
210                     mClient.triggerFeature(
211                             DeviceSnapshotFeature.DEVICE_SNAPSHOT_FEATURE_NAME, args);
212             CLog.d(
213                     "Response from restoring snapshot(%s) request: %s",
214                     snapshotId, response.getResponse());
215         } finally {
216             mClient.close();
217         }
218         if (response.hasErrorInfo()) {
219             String trace = response.getErrorInfo().getErrorTrace();
220             // Handle if it's an exception error.
221             Object o = null;
222             try {
223                 o = SerializationUtil.deserialize(trace);
224             } catch (IOException | RuntimeException e) {
225                 CLog.e("Failed to deserialize snapshot error response: %s", e.getMessage());
226             }
227             if (o instanceof DeviceNotAvailableException) {
228                 throw (DeviceNotAvailableException) o;
229             } else if (o instanceof IHarnessException) {
230                 IHarnessException exception = (IHarnessException) o;
231                 throw new HarnessRuntimeException(
232                         "Exception while restoring snapshot of the device.", exception);
233             } else if (o instanceof Exception) {
234                 throw new HarnessRuntimeException(
235                         "Exception while restoring snapshot of the device.",
236                         (Exception) o,
237                         InfraErrorIdentifier.UNDETERMINED);
238             }
239             throw new HarnessRuntimeException(
240                     "Exception while restoring snapshot of the device. Unserialized error response:"
241                             + " "
242                             + trace,
243                     InfraErrorIdentifier.UNDETERMINED);
244         }
245         if (device instanceof NativeDevice) {
246             ((NativeDevice) device).resetContentProviderSetup();
247         }
248         CurrentInvocation.setModuleIsolation(IsolationGrade.FULLY_ISOLATED);
249         CurrentInvocation.setRunIsolation(IsolationGrade.FULLY_ISOLATED);
250 
251         // Save snapshot performance data
252         Pattern durationPattern = Pattern.compile("Restoring snapshot\\sfinished\\sin (\\d+)\\sms");
253         Matcher matcher;
254         matcher = durationPattern.matcher(response.getResponse());
255         if (matcher.find()) {
256             InvocationMetricLogger.addInvocationMetrics(
257                     InvocationMetricKey.DEVICE_SNAPSHOT_RESTORE_SUCCESS_COUNT, 1);
258             InvocationMetricLogger.addInvocationMetrics(
259                     InvocationMetricKey.DEVICE_SNAPSHOT_RESTORE_DURATIONS, matcher.group(1));
260         } else {
261             InvocationMetricLogger.addInvocationMetrics(
262                     InvocationMetricKey.DEVICE_SNAPSHOT_RESTORE_FAILURE_COUNT, 1);
263         }
264     }
265 }
266