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.log.LogUtil.CLog; 31 import com.android.tradefed.util.IRunUtil; 32 import com.android.tradefed.util.RunUtil; 33 import com.android.tradefed.util.SystemUtil; 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 StubLocalAndroidVirtualDevice) { 88 // Virtual device to be launched by TradeFed locally. 89 testDevice = 90 new LocalAndroidVirtualDevice( 91 idevice, 92 new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled), 93 mAllocationMonitor); 94 } else if (idevice instanceof TcpDevice) { 95 // Special device for Tcp device for custom handling. 96 testDevice = new RemoteAndroidDevice(idevice, 97 new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled), 98 mAllocationMonitor); 99 testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE); 100 } else if (isTcpDeviceSerial(idevice.getSerialNumber())) { 101 if (isRemoteEnvironment()) { 102 // If we are in a remote environment, treat the device as such 103 testDevice = 104 new NestedRemoteDevice( 105 idevice, 106 new NestedDeviceStateMonitor( 107 mDeviceManager, idevice, mFastbootEnabled), 108 mAllocationMonitor); 109 } else { 110 // Handle device connected via 'adb connect' 111 testDevice = 112 new RemoteAndroidDevice( 113 idevice, 114 new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled), 115 mAllocationMonitor); 116 testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE); 117 } 118 } else if (!checkFrameworkSupport(idevice)) { 119 // Iot device instance tier 1 (no framework support) 120 testDevice = 121 new NativeDevice( 122 idevice, 123 new NativeDeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled), 124 mAllocationMonitor); 125 } else { 126 // Default to-go device is Android full stack device. 127 testDevice = new TestDevice(idevice, 128 new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled), 129 mAllocationMonitor); 130 } 131 132 if (idevice instanceof FastbootDevice) { 133 if (((FastbootDevice) idevice).isFastbootD()) { 134 testDevice.setDeviceState(TestDeviceState.FASTBOOTD); 135 } else { 136 testDevice.setDeviceState(TestDeviceState.FASTBOOT); 137 } 138 } else if (idevice instanceof StubDevice) { 139 testDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE); 140 } 141 testDevice.setFastbootEnabled(mFastbootEnabled); 142 testDevice.setFastbootPath(mDeviceManager.getFastbootPath()); 143 return testDevice; 144 } 145 146 /** 147 * Helper that return true if device has framework support. 148 */ checkFrameworkSupport(IDevice idevice)149 protected boolean checkFrameworkSupport(IDevice idevice) { 150 if (idevice instanceof StubDevice) { 151 // Assume stub device should go to the default full framework support for 152 // backward compatibility 153 return true; 154 } 155 final long timeout = 60 * 1000; 156 try { 157 for (int i = 0; i < FRAMEWORK_CHECK_MAX_RETRY; i++) { 158 CollectingOutputReceiver receiver = createOutputReceiver(); 159 if (!DeviceState.ONLINE.equals(idevice.getState())) { 160 // Device will be 'unavailable' and recreated in DeviceManager so no need to 161 // check. 162 CLog.w("Device state is not Online, assuming Framework support for now."); 163 return true; 164 } 165 String cmd = String.format(CHECK_PM_CMD, EXPECTED_RES); 166 idevice.executeShellCommand(cmd, receiver, timeout, TimeUnit.MILLISECONDS); 167 String output = receiver.getOutput().trim(); 168 // It can only be one of the expected output or an exception if device offline 169 // otherwise we retry 170 if (EXPECTED_RES.equals(output)) { 171 return true; 172 } 173 if (output.contains(EXPECTED_ERROR)) { 174 CLog.i("No support for Framework, creating a native device. " 175 + "output: %s", receiver.getOutput()); 176 return false; 177 } 178 getRunUtil().sleep(FRAMEWORK_CHECK_SLEEP_MS); 179 } 180 CLog.w("Could not determine the framework support for '%s' after retries, assuming " 181 + "framework support.", idevice.getSerialNumber()); 182 } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException 183 | IOException e) { 184 CLog.w("Exception during checkFrameworkSupport, assuming True: '%s' with device: %s", 185 e.getMessage(), idevice.getSerialNumber()); 186 CLog.e(e); 187 } 188 // We default to support for framework to get same behavior as before. 189 return true; 190 } 191 192 /** Return the default {@link IRunUtil} instance. */ 193 @VisibleForTesting getRunUtil()194 protected IRunUtil getRunUtil() { 195 return RunUtil.getDefault(); 196 } 197 198 /** 199 * Return true if we are currently running in a remote environment. This will alter the device 200 * behavior. 201 */ 202 @VisibleForTesting isRemoteEnvironment()203 protected boolean isRemoteEnvironment() { 204 return SystemUtil.isRemoteEnvironment(); 205 } 206 207 /** Create a {@link CollectingOutputReceiver}. */ 208 @VisibleForTesting createOutputReceiver()209 protected CollectingOutputReceiver createOutputReceiver() { 210 return new CollectingOutputReceiver(); 211 } 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override setFastbootEnabled(boolean enable)217 public void setFastbootEnabled(boolean enable) { 218 mFastbootEnabled = enable; 219 } 220 221 /** 222 * Helper to device if it's a serial from a remotely connected device. 223 * serial format of tcp device is <ip or locahost>:<port> 224 */ isTcpDeviceSerial(String serial)225 protected boolean isTcpDeviceSerial(String serial) { 226 final String remotePattern = IPADDRESS_PATTERN + "(:)([0-9]{2,5})(\\b)"; 227 Pattern pattern = Pattern.compile(remotePattern); 228 Matcher match = pattern.matcher(serial.trim()); 229 if (match.find()) { 230 return true; 231 } 232 return false; 233 } 234 } 235