1 /* 2 * Copyright (C) 2013 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.cts.usb; 17 18 import android.cts.statsdatom.lib.AtomTestUtils; 19 import android.platform.test.annotations.AppModeFull; 20 import android.platform.test.annotations.AppModeInstant; 21 22 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 23 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 24 import com.android.ddmlib.testrunner.TestResult.TestStatus; 25 import com.android.tradefed.build.IBuildInfo; 26 import com.android.tradefed.device.DeviceNotAvailableException; 27 import com.android.tradefed.device.ITestDevice; 28 import com.android.tradefed.log.LogUtil.CLog; 29 import com.android.tradefed.result.CollectingTestListener; 30 import com.android.tradefed.result.TestResult; 31 import com.android.tradefed.result.TestRunResult; 32 import com.android.tradefed.testtype.DeviceTestCase; 33 import com.android.tradefed.testtype.IAbi; 34 import com.android.tradefed.testtype.IAbiReceiver; 35 import com.android.tradefed.testtype.IBuildReceiver; 36 import com.android.tradefed.util.AbiUtils; 37 import com.android.tradefed.util.CommandResult; 38 import com.android.tradefed.util.CommandStatus; 39 import com.android.tradefed.util.RunInterruptedException; 40 import com.android.tradefed.util.RunUtil; 41 42 import java.io.File; 43 import java.io.FileNotFoundException; 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Locale; 47 import java.util.regex.Matcher; 48 import java.util.regex.Pattern; 49 50 /** 51 * Functional tests for usb connection 52 */ 53 public class TestUsbTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver { 54 55 private static final String CTS_RUNNER = "androidx.test.runner.AndroidJUnitRunner"; 56 private static final String PACKAGE_NAME = "com.android.cts.usb.serialtest"; 57 private static final String TEST_CLASS_NAME = PACKAGE_NAME + ".UsbSerialTest"; 58 private static final String APK_NAME = "CtsUsbSerialTestApp.apk"; 59 private static final String DUMMY_ACTIVITY = PACKAGE_NAME + ".DummyActivity"; 60 private static final long CONN_TIMEOUT_MS = 15000; 61 private static final long SLEEP_MS = 300; 62 private static final String FEATURE_MIDI = "android.software.midi"; 63 private static final String MIDI_DEVICE_NAME = "Android USB Peripheral Port"; 64 65 private ITestDevice mDevice; 66 private IAbi mAbi; 67 private IBuildInfo mBuild; 68 private boolean mReconnected = false; 69 70 @Override setAbi(IAbi abi)71 public void setAbi(IAbi abi) { 72 mAbi = abi; 73 } 74 75 @Override setBuild(IBuildInfo buildInfo)76 public void setBuild(IBuildInfo buildInfo) { 77 mBuild = buildInfo; 78 } 79 80 @Override setUp()81 protected void setUp() throws Exception { 82 super.setUp(); 83 mDevice = getDevice(); 84 mDevice.uninstallPackage(PACKAGE_NAME); 85 mDevice.executeShellCommand("svc usb setFunctions none"); 86 mDevice.waitForDeviceAvailable(CONN_TIMEOUT_MS); 87 } 88 installApp(boolean installAsInstantApp)89 private void installApp(boolean installAsInstantApp) 90 throws FileNotFoundException, DeviceNotAvailableException { 91 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild); 92 File app = buildHelper.getTestFile(APK_NAME); 93 String[] options; 94 95 if (installAsInstantApp) { 96 options = new String[]{AbiUtils.createAbiFlag(mAbi.getName()), "--instant"}; 97 } else { 98 options = new String[]{AbiUtils.createAbiFlag(mAbi.getName())}; 99 } 100 mDevice.installPackage(app, false, true, options); 101 } 102 103 @Override tearDown()104 protected void tearDown() throws Exception { 105 super.tearDown(); 106 mDevice.uninstallPackage(PACKAGE_NAME); 107 mDevice.executeShellCommand("svc usb setFunctions none"); 108 mDevice.waitForDeviceAvailable(CONN_TIMEOUT_MS); 109 } 110 runTestOnDevice(String testMethod)111 private void runTestOnDevice(String testMethod) throws DeviceNotAvailableException { 112 CollectingTestListener listener = new CollectingTestListener(); 113 RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(PACKAGE_NAME, CTS_RUNNER, 114 mDevice.getIDevice()); 115 testRunner.setMethodName(TEST_CLASS_NAME, testMethod); 116 mDevice.runInstrumentationTests(testRunner, listener); 117 118 while (!listener.getCurrentRunResults().isRunComplete()) { 119 // wait 120 } 121 122 TestRunResult runResult = listener.getCurrentRunResults(); 123 if (runResult.isRunFailure()) { 124 fail(runResult.getRunFailureMessage()); 125 } 126 127 for (TestResult result : runResult.getTestResults().values()) { 128 if (!result.getStatus().equals(TestStatus.PASSED)) { 129 fail(result.getStackTrace()); 130 } 131 } 132 } 133 134 /** 135 * Check if adb serial number, USB serial number, ro.serialno, and android.os.Build.SERIAL 136 * all matches and meets the format requirement [a-zA-Z0-9\\._\\-,]+ 137 */ 138 @AppModeInstant(reason = "only instant apps fail when reading serial") testInstantAppsCannotReadSerial()139 public void testInstantAppsCannotReadSerial() throws Exception { 140 installApp(true); 141 142 runTestOnDevice("verifySerialCannotBeRead"); 143 } 144 145 /** 146 * Check if adb serial number, USB serial number, ro.serialno, and android.os.Build.SERIAL 147 * all matches and meets the format requirement [a-zA-Z0-9\\._\\-,]+ 148 */ 149 @AppModeFull(reason = "serial can not be read by instant apps") testUsbSerialReadOnDeviceMatches()150 public void testUsbSerialReadOnDeviceMatches() throws Exception { 151 installApp(false); 152 153 String adbSerial = mDevice.getSerialNumber().toLowerCase().trim(); 154 if (adbSerial.startsWith("emulator-")) { 155 return; 156 } 157 if (mDevice.isAdbTcp()) { // adb over WiFi, no point checking it 158 return; 159 } 160 161 String roSerial = mDevice.executeShellCommand("getprop ro.serialno").toLowerCase(). 162 trim(); 163 assertEquals("adb serial != ro.serialno" , adbSerial, roSerial); 164 165 CommandResult result = RunUtil.getDefault().runTimedCmdRetry( 166 /* timeout= */ 30000, 167 /* retryInterval= */ 1000, 168 /* attempts= */ 3, 169 "lsusb", 170 "-v" 171 ); 172 assertEquals("lsusb -v failed", result.getStatus(), CommandStatus.SUCCESS); 173 String lsusbOutput = result.getStdout(); 174 Pattern pattern = Pattern.compile("^\\s+iSerial\\s+\\d+\\s+([a-zA-Z0-9\\._\\-,]+)", 175 Pattern.MULTILINE); 176 Matcher matcher = pattern.matcher(lsusbOutput); 177 String usbSerial = ""; 178 while (matcher.find()) { 179 String currentSerial = matcher.group(1).toLowerCase(); 180 if (adbSerial.compareTo(currentSerial) == 0) { 181 usbSerial = currentSerial; 182 break; 183 } 184 } 185 assertEquals("usb serial != adb serial" , usbSerial, adbSerial); 186 187 // now check Build.SERIAL 188 clearLogCat(); 189 runTestOnDevice("logSerial"); 190 RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT); 191 String logs = mDevice.executeAdbCommand( 192 "logcat", "-v", "brief", "-d", "CtsUsbSerialTest:W", "*:S"); 193 pattern = Pattern.compile("^.*CtsUsbSerialTest\\(.*\\):\\s+([a-zA-Z0-9\\._\\-,]+)", 194 Pattern.MULTILINE); 195 matcher = pattern.matcher(logs); 196 String buildSerial = ""; 197 while (matcher.find()) { 198 String currentSerial = matcher.group(1).toLowerCase(); 199 if (usbSerial.compareTo(currentSerial) == 0) { 200 buildSerial = currentSerial; 201 break; 202 } 203 } 204 assertEquals("usb serial != Build.SERIAL" , usbSerial, buildSerial); 205 } 206 207 @AppModeFull testUsbStateIntent()208 public void testUsbStateIntent() throws Exception { 209 String adbSerial = mDevice.getSerialNumber().toLowerCase(Locale.ENGLISH).trim(); 210 if (adbSerial.startsWith("emulator-") || mDevice.isAdbTcp()) { 211 return; // Skip emulators and adb over WiFi 212 } 213 214 // Start DummyActivity to launch the APP so that CtsUsbStateBroadcastReceiver can 215 // start capturing usb state intent 216 installApp(false); 217 mDevice.executeShellCommand("am start -W -n " + PACKAGE_NAME + "/" + DUMMY_ACTIVITY); 218 219 new Thread(new Runnable() { 220 public void run() { 221 try { 222 mDevice.waitForDeviceNotAvailable(CONN_TIMEOUT_MS); 223 CLog.i("Device disconnected"); 224 RunUtil.getDefault().sleep(SLEEP_MS); 225 mDevice.waitForDeviceAvailable(CONN_TIMEOUT_MS); 226 CLog.i("Device reconnected"); 227 mReconnected = true; 228 } catch (DeviceNotAvailableException dnae) { 229 CLog.e("Device is not available"); 230 } catch (RunInterruptedException ie) { 231 CLog.w("Sleep interrupted"); 232 } 233 } 234 }).start(); 235 236 clearLogCat(); 237 mDevice.executeShellCommand("svc usb setFunctions mtp"); 238 long startTime = System.currentTimeMillis(); 239 while (!mReconnected && System.currentTimeMillis() - startTime < CONN_TIMEOUT_MS) { 240 RunUtil.getDefault().sleep(SLEEP_MS); 241 } 242 assertTrue("Device failed to reconnect", mReconnected); 243 244 245 String logs = mDevice.executeAdbCommand( 246 "logcat", "-v", "brief", "-d", "CtsUsbStateBroadcastReceiver:I", "*:S"); 247 List<String> stateList = new ArrayList<>(); 248 Pattern pattern = Pattern.compile("^.*CtsUsbStateBroadcastReceiver\\(.*\\):\\s+([A-Z]+)", 249 Pattern.MULTILINE); 250 Matcher matcher = pattern.matcher(logs); 251 while (matcher.find()) { 252 CLog.i(matcher.group(1)); 253 stateList.add(matcher.group(1)); 254 } 255 256 // Focus on confirming the total count of USB state transitions. The precise order of events 257 // can vary due to timing factors and debounce mechanisms in the kernel and framework. 258 assertTrue("No usb state transition", stateList.size() > 1); 259 // Last state has to be CONFIGURED. 260 assertEquals("Last state != CONFIGURED", "CONFIGURED", stateList.get(stateList.size() - 1)); 261 } 262 testUsbMidiGadget()263 public void testUsbMidiGadget() throws Exception { 264 String adbSerial = mDevice.getSerialNumber().toLowerCase(Locale.ENGLISH).trim(); 265 if (adbSerial.startsWith("emulator-") || mDevice.isAdbTcp()) { 266 return; // Skip emulators and adb over WiFi 267 } 268 269 if (!mDevice.executeShellCommand("pm list features").contains(FEATURE_MIDI)) { 270 return; // Skip if midi isn't supported on the device 271 } 272 273 mDevice.executeShellCommand("svc usb setFunctions midi"); 274 RunUtil.getDefault().sleep(SLEEP_MS); 275 mDevice.waitForDeviceAvailable(CONN_TIMEOUT_MS); 276 CLog.i("Device reconnected"); 277 278 String midiDevices = mDevice.executeShellCommand("dumpsys midi"); 279 CLog.i(midiDevices); 280 assertTrue("Midi device not found", midiDevices.contains(MIDI_DEVICE_NAME)); 281 } 282 clearLogCat()283 private void clearLogCat() throws DeviceNotAvailableException { 284 mDevice.executeAdbCommand("logcat", "-c"); 285 } 286 } 287