1 /*
2  * Copyright (C) 2014 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 
17 package android.appsecurity.cts;
18 
19 import com.android.ddmlib.AdbCommandRejectedException;
20 import com.android.ddmlib.CollectingOutputReceiver;
21 import com.android.ddmlib.Log;
22 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
23 import com.android.ddmlib.testrunner.TestResult.TestStatus;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.result.CollectingTestListener;
27 import com.android.tradefed.result.TestDescription;
28 import com.android.tradefed.result.TestResult;
29 import com.android.tradefed.result.TestRunResult;
30 import com.android.tradefed.util.RunUtil;
31 
32 import java.util.Arrays;
33 import java.util.HashMap;
34 import java.util.Map;
35 import java.util.Objects;
36 import java.util.concurrent.TimeUnit;
37 
38 public class Utils {
39     private static final String LOG_TAG = Utils.class.getSimpleName();
40 
41     public static final int USER_SYSTEM = 0;
42 
43     static final String PKG = "com.android.cts.splitapp";
44     static final String CLASS = PKG + ".SplitAppTest";
45 
46     static final String APK = "CtsSplitApp.apk";
47 
48     static final String APK_mdpi = "CtsSplitApp_mdpi-v4.apk";
49     static final String APK_hdpi = "CtsSplitApp_hdpi-v4.apk";
50     static final String APK_xhdpi = "CtsSplitApp_xhdpi-v4.apk";
51     static final String APK_xxhdpi = "CtsSplitApp_xxhdpi-v4.apk";
52 
53     static final String APK_x86 = "CtsSplitApp_x86.apk";
54     static final String APK_x86_64 = "CtsSplitApp_x86_64.apk";
55     static final String APK_armeabi_v7a = "CtsSplitApp_armeabi-v7a.apk";
56     static final String APK_armeabi = "CtsSplitApp_armeabi.apk";
57     static final String APK_arm64_v8a = "CtsSplitApp_arm64-v8a.apk";
58     static final String APK_mips64 = "CtsSplitApp_mips64.apk";
59     static final String APK_mips = "CtsSplitApp_mips.apk";
60 
61     static final HashMap<String, String> ABI_TO_APK = new HashMap<>();
62 
63     static {
64         ABI_TO_APK.put("x86", APK_x86);
65         ABI_TO_APK.put("x86_64", APK_x86_64);
66         ABI_TO_APK.put("armeabi-v7a", APK_armeabi_v7a);
67         ABI_TO_APK.put("armeabi", APK_armeabi);
68         ABI_TO_APK.put("arm64-v8a", APK_arm64_v8a);
69         ABI_TO_APK.put("mips64", APK_mips64);
70         ABI_TO_APK.put("mips", APK_mips);
71     }
72 
runDeviceTestsAsCurrentUser(ITestDevice device, String packageName, String testClassName, String testMethodName)73     public static void runDeviceTestsAsCurrentUser(ITestDevice device, String packageName,
74             String testClassName, String testMethodName) throws DeviceNotAvailableException {
75         runDeviceTests(device, packageName, testClassName, testMethodName, device.getCurrentUser(),
76                 null);
77     }
78 
runDeviceTestsAsCurrentUser(ITestDevice device, String packageName, String testClassName, String testMethodName, Map<String, String> testArgs)79     public static void runDeviceTestsAsCurrentUser(ITestDevice device, String packageName,
80             String testClassName, String testMethodName, Map<String, String> testArgs)
81                     throws DeviceNotAvailableException {
82         runDeviceTests(device, packageName, testClassName, testMethodName, device.getCurrentUser(),
83                 testArgs);
84     }
85 
runDeviceTests(ITestDevice device, String packageName, String testClassName, String testMethodName)86     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
87             String testMethodName) throws DeviceNotAvailableException {
88         runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM, null);
89     }
90 
runDeviceTests(ITestDevice device, String packageName, String testClassName, String testMethodName, Map<String, String> testArgs)91     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
92             String testMethodName, Map<String, String> testArgs)
93                     throws DeviceNotAvailableException {
94         runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM, testArgs);
95     }
96 
runDeviceTests(ITestDevice device, String packageName, String testClassName, String testMethodName, int userId)97     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
98             String testMethodName, int userId) throws DeviceNotAvailableException {
99         runDeviceTests(device, packageName, testClassName, testMethodName, userId, null);
100     }
101 
runDeviceTests(ITestDevice device, String packageName, String testClassName, String testMethodName, int userId, Map<String, String> testArgs)102     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
103             String testMethodName, int userId, Map<String, String> testArgs)
104                     throws DeviceNotAvailableException {
105         // 60 min timeout per test by default
106         runDeviceTests(device, packageName, testClassName, testMethodName, userId, testArgs,
107                 60L, TimeUnit.MINUTES);
108     }
109 
runDeviceTests(ITestDevice device, String packageName, String testClassName, String testMethodName, int userId, Map<String, String> testArgs, long timeout, TimeUnit unit)110     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
111             String testMethodName, int userId, Map<String, String> testArgs, long timeout,
112             TimeUnit unit)
113                     throws DeviceNotAvailableException {
114         if (testClassName != null && testClassName.startsWith(".")) {
115             testClassName = packageName + testClassName;
116         }
117         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
118                 "androidx.test.runner.AndroidJUnitRunner", device.getIDevice());
119         // timeout_msec is the timeout per test for instrumentation
120         long testTimeoutMs = unit.toMillis(timeout);
121         testRunner.addInstrumentationArg("timeout_msec", Long.toString(testTimeoutMs));
122         // Similar logic as InstrumentationTest to ensure on host-side level that no-hanging can happen
123         long maxTimeToOutputMs = testTimeoutMs + testTimeoutMs / 10;
124         testRunner.setMaxTimeToOutputResponse(maxTimeToOutputMs, TimeUnit.MILLISECONDS);
125 
126         if (testClassName != null && testMethodName != null) {
127             testRunner.setMethodName(testClassName, testMethodName);
128         } else if (testClassName != null) {
129             testRunner.setClassName(testClassName);
130         }
131 
132         if (testArgs != null && testArgs.size() > 0) {
133             for (String name : testArgs.keySet()) {
134                 final String value = testArgs.get(name);
135                 testRunner.addInstrumentationArg(name, value);
136             }
137         }
138         final CollectingTestListener listener = new CollectingTestListener();
139         device.runInstrumentationTestsAsUser(testRunner, userId, listener);
140 
141         final TestRunResult result = listener.getCurrentRunResults();
142         if (result.isRunFailure()) {
143             throw new AssertionError("Failed to successfully run device tests for "
144                     + result.getName() + ": " + result.getRunFailureMessage());
145         }
146         if (result.getNumTests() == 0) {
147             throw new AssertionError("No tests were run on the device");
148         }
149         if (result.hasFailedTests()) {
150             // build a meaningful error message
151             StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
152             for (Map.Entry<TestDescription, TestResult> resultEntry :
153                 result.getTestResults().entrySet()) {
154                 if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
155                     errorBuilder.append(resultEntry.getKey().toString());
156                     errorBuilder.append(":\n");
157                     errorBuilder.append(resultEntry.getValue().getStackTrace());
158                 }
159             }
160             throw new AssertionError(errorBuilder.toString());
161         }
162     }
163 
164     /**
165      * Prepare and return a single user relevant for testing.
166      */
prepareSingleUser(ITestDevice device)167     public static int[] prepareSingleUser(ITestDevice device)
168             throws DeviceNotAvailableException {
169         return prepareMultipleUsers(device, 1);
170     }
171 
172     /**
173      * Prepare and return two users relevant for testing.
174      */
prepareMultipleUsers(ITestDevice device)175     public static int[] prepareMultipleUsers(ITestDevice device)
176             throws DeviceNotAvailableException {
177         return prepareMultipleUsers(device, 2);
178     }
179 
180     /**
181      * Prepare and return multiple users relevant for testing.
182      */
prepareMultipleUsers(ITestDevice device, int maxUsers)183     public static int[] prepareMultipleUsers(ITestDevice device, int maxUsers)
184             throws DeviceNotAvailableException {
185         final int[] userIds = getAllUsers(device);
186         int currentUserId = device.getCurrentUser();
187         for (int i = 1; i < userIds.length; i++) {
188             if (i < maxUsers) {
189                 device.startUser(userIds[i], true);
190             } else if (userIds[i] != currentUserId) {
191                 device.stopUser(userIds[i], true, true);
192             }
193         }
194         if (userIds.length > maxUsers) {
195             return Arrays.copyOf(userIds, maxUsers);
196         } else {
197             return userIds;
198         }
199     }
200 
getAllUsers(ITestDevice device)201     public static int[] getAllUsers(ITestDevice device)
202             throws DeviceNotAvailableException {
203         Integer primary = device.getPrimaryUserId();
204         if (device.isHeadlessSystemUserMode()
205                 && primary == USER_SYSTEM
206                 && !device.canSwitchToHeadlessSystemUser()) {
207             primary = device.getMainUserId();
208         }
209         if (primary == null) {
210             primary = USER_SYSTEM;
211         }
212         int[] users = new int[] { primary };
213         for (Integer user : device.listUsers()) {
214             if ((user != USER_SYSTEM) && !Objects.equals(user, primary)) {
215                 users = Arrays.copyOf(users, users.length + 1);
216                 users[users.length - 1] = user;
217             }
218         }
219         return users;
220     }
221 
waitForBootCompleted(ITestDevice device)222     public static void waitForBootCompleted(ITestDevice device) throws Exception {
223         for (int i = 0; i < 45; i++) {
224             if (isBootCompleted(device)) {
225                 Log.d(LOG_TAG, "Yay, system is ready!");
226                 // or is it really ready?
227                 // guard against potential USB mode switch weirdness at boot
228                 RunUtil.getDefault().sleep(10 * 1000);
229                 return;
230             }
231             Log.d(LOG_TAG, "Waiting for system ready...");
232             // For low performance devices
233             RunUtil.getDefault().sleep(10*1000);
234         }
235         throw new AssertionError("System failed to become ready!");
236     }
237 
isBootCompleted(ITestDevice device)238     private static boolean isBootCompleted(ITestDevice device) throws Exception {
239         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
240         try {
241             device.getIDevice().executeShellCommand("getprop sys.boot_completed", receiver);
242         } catch (AdbCommandRejectedException e) {
243             // do nothing: device might be temporarily disconnected
244             Log.d(LOG_TAG, "Ignored AdbCommandRejectedException while `getprop sys.boot_completed`");
245         }
246         String output = receiver.getOutput();
247         if (output != null) {
248             output = output.trim();
249         }
250         return "1".equals(output);
251     }
252 
253 }
254