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