xref: /aosp_15_r20/cts/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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