• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.ddmlib.AdbCommandRejectedException;
20 import com.android.ddmlib.IDevice;
21 import com.android.ddmlib.IDevice.DeviceState;
22 import com.android.ddmlib.ShellCommandUnresponsiveException;
23 import com.android.ddmlib.TimeoutException;
24 import com.android.tradefed.device.DeviceManager.FastbootDevice;
25 import com.android.tradefed.device.cloud.ManagedRemoteDevice;
26 import com.android.tradefed.device.cloud.NestedDeviceStateMonitor;
27 import com.android.tradefed.device.cloud.NestedRemoteDevice;
28 import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
29 import com.android.tradefed.device.cloud.VmRemoteDevice;
30 import com.android.tradefed.invoker.RemoteInvocationExecution;
31 import com.android.tradefed.log.LogUtil.CLog;
32 import com.android.tradefed.util.IRunUtil;
33 import com.android.tradefed.util.RunUtil;
34 
35 import java.io.IOException;
36 import java.util.concurrent.TimeUnit;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 /**
41  * Factory to create the different kind of devices that can be monitored by Tf
42  */
43 public class ManagedTestDeviceFactory implements IManagedTestDeviceFactory {
44 
45     public static final String IPADDRESS_PATTERN =
46             "((^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
47                     + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
48                     + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
49                     + "([01]?\\d\\d?|2[0-4]\\d|25[0-5]))|(localhost)){1}";
50 
51     protected boolean mFastbootEnabled;
52     protected IDeviceManager mDeviceManager;
53     protected IDeviceMonitor mAllocationMonitor;
54     protected static final String CHECK_PM_CMD = "ls %s";
55     protected static final String EXPECTED_RES = "/system/bin/pm";
56     protected static final String EXPECTED_ERROR = "No such file or directory";
57     protected static final long FRAMEWORK_CHECK_SLEEP_MS = 500;
58     protected static final int FRAMEWORK_CHECK_MAX_RETRY = 3;
59 
ManagedTestDeviceFactory(boolean fastbootEnabled, IDeviceManager deviceManager, IDeviceMonitor allocationMonitor)60     public ManagedTestDeviceFactory(boolean fastbootEnabled, IDeviceManager deviceManager,
61             IDeviceMonitor allocationMonitor) {
62         mFastbootEnabled = fastbootEnabled;
63         mDeviceManager = deviceManager;
64         mAllocationMonitor = allocationMonitor;
65     }
66 
67     /**
68      * {@inheritDoc}
69      */
70     @Override
createDevice(IDevice idevice)71     public IManagedTestDevice createDevice(IDevice idevice) {
72         IManagedTestDevice testDevice = null;
73         if (idevice instanceof VmRemoteDevice) {
74             testDevice =
75                     new ManagedRemoteDevice(
76                             idevice,
77                             new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
78                             mAllocationMonitor);
79             testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
80         } else if (idevice instanceof RemoteAvdIDevice) {
81             testDevice =
82                     new RemoteAndroidVirtualDevice(
83                             idevice,
84                             new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
85                             mAllocationMonitor);
86             testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
87         } else if (idevice instanceof TcpDevice) {
88             // Special device for Tcp device for custom handling.
89             testDevice = new RemoteAndroidDevice(idevice,
90                     new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
91                     mAllocationMonitor);
92             testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
93         } else if (isTcpDeviceSerial(idevice.getSerialNumber())) {
94             if (isRemoteEnvironment()) {
95                 // If we are in a remote environment, treat the device as such
96                 testDevice =
97                         new NestedRemoteDevice(
98                                 idevice,
99                                 new NestedDeviceStateMonitor(
100                                         mDeviceManager, idevice, mFastbootEnabled),
101                                 mAllocationMonitor);
102             } else {
103                 // Handle device connected via 'adb connect'
104                 testDevice =
105                         new RemoteAndroidDevice(
106                                 idevice,
107                                 new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
108                                 mAllocationMonitor);
109                 testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
110             }
111         } else if (!checkFrameworkSupport(idevice)) {
112             // Iot device instance tier 1 (no framework support)
113             testDevice =
114                     new NativeDevice(
115                             idevice,
116                             new NativeDeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
117                             mAllocationMonitor);
118         } else {
119             // Default to-go device is Android full stack device.
120             testDevice = new TestDevice(idevice,
121                     new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
122                     mAllocationMonitor);
123         }
124 
125         if (idevice instanceof FastbootDevice) {
126             testDevice.setDeviceState(TestDeviceState.FASTBOOT);
127         } else if (idevice instanceof StubDevice) {
128             testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE);
129         }
130         testDevice.setFastbootEnabled(mFastbootEnabled);
131         testDevice.setFastbootPath(mDeviceManager.getFastbootPath());
132         return testDevice;
133     }
134 
135     /**
136      * Helper that return true if device has framework support.
137      */
checkFrameworkSupport(IDevice idevice)138     protected boolean checkFrameworkSupport(IDevice idevice) {
139         if (idevice instanceof StubDevice) {
140             // Assume stub device should go to the default full framework support for
141             // backward compatibility
142             return true;
143         }
144         final long timeout = 60 * 1000;
145         try {
146             for (int i = 0; i < FRAMEWORK_CHECK_MAX_RETRY; i++) {
147                 CollectingOutputReceiver receiver = createOutputReceiver();
148                 if (!DeviceState.ONLINE.equals(idevice.getState())) {
149                     // Device will be 'unavailable' and recreated in DeviceManager so no need to
150                     // check.
151                     CLog.w("Device state is not Online, assuming Framework support for now.");
152                     return true;
153                 }
154                 String cmd = String.format(CHECK_PM_CMD, EXPECTED_RES);
155                 idevice.executeShellCommand(cmd, receiver, timeout, TimeUnit.MILLISECONDS);
156                 String output = receiver.getOutput().trim();
157                 // It can only be one of the expected output or an exception if device offline
158                 // otherwise we retry
159                 if (EXPECTED_RES.equals(output)) {
160                     return true;
161                 }
162                 if (output.contains(EXPECTED_ERROR)) {
163                     CLog.i("No support for Framework, creating a native device. "
164                             + "output: %s", receiver.getOutput());
165                     return false;
166                 }
167                 getRunUtil().sleep(FRAMEWORK_CHECK_SLEEP_MS);
168             }
169             CLog.w("Could not determine the framework support for '%s' after retries, assuming "
170                     + "framework support.", idevice.getSerialNumber());
171         } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException
172                 | IOException e) {
173             CLog.w("Exception during checkFrameworkSupport, assuming True: '%s' with device: %s",
174                     e.getMessage(), idevice.getSerialNumber());
175             CLog.e(e);
176         }
177         // We default to support for framework to get same behavior as before.
178         return true;
179     }
180 
181     /** Return the default {@link IRunUtil} instance. */
182     @VisibleForTesting
getRunUtil()183     protected IRunUtil getRunUtil() {
184         return RunUtil.getDefault();
185     }
186 
187     /**
188      * Return true if we are currently running in a remote environment. This will alter the device
189      * behavior.
190      */
191     @VisibleForTesting
isRemoteEnvironment()192     protected boolean isRemoteEnvironment() {
193         if ("1".equals(System.getenv(RemoteInvocationExecution.REMOTE_VM_VARIABLE))) {
194             return true;
195         }
196         return false;
197     }
198 
199     /** Create a {@link CollectingOutputReceiver}. */
200     @VisibleForTesting
createOutputReceiver()201     protected CollectingOutputReceiver createOutputReceiver() {
202         return new CollectingOutputReceiver();
203     }
204 
205     /**
206      * {@inheritDoc}
207      */
208     @Override
setFastbootEnabled(boolean enable)209     public void setFastbootEnabled(boolean enable) {
210         mFastbootEnabled = enable;
211     }
212 
213     /**
214      * Helper to device if it's a serial from a remotely connected device.
215      * serial format of tcp device is <ip or locahost>:<port>
216      */
isTcpDeviceSerial(String serial)217     protected boolean isTcpDeviceSerial(String serial) {
218         final String remotePattern = IPADDRESS_PATTERN + "(:)([0-9]{2,5})(\\b)";
219         Pattern pattern = Pattern.compile(remotePattern);
220         Matcher match = pattern.matcher(serial.trim());
221         if (match.find()) {
222             return true;
223         }
224         return false;
225     }
226 }
227