• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.cts.devicepolicy;
18 
19 import com.android.cts.tradefed.build.CtsBuildHelper;
20 import com.android.cts.util.AbiUtils;
21 import com.android.ddmlib.Log.LogLevel;
22 import com.android.ddmlib.testrunner.InstrumentationResultParser;
23 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
24 import com.android.ddmlib.testrunner.TestIdentifier;
25 import com.android.ddmlib.testrunner.TestResult;
26 import com.android.ddmlib.testrunner.TestResult.TestStatus;
27 import com.android.ddmlib.testrunner.TestRunResult;
28 import com.android.tradefed.build.IBuildInfo;
29 import com.android.tradefed.device.DeviceNotAvailableException;
30 import com.android.tradefed.device.ITestDevice;
31 import com.android.tradefed.log.LogUtil.CLog;
32 import com.android.tradefed.result.CollectingTestListener;
33 import com.android.tradefed.testtype.DeviceTestCase;
34 import com.android.tradefed.testtype.IBuildReceiver;
35 
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 import java.util.ArrayList;
39 import java.util.HashSet;
40 import java.util.Map;
41 
42 import javax.annotation.Nullable;
43 
44 /**
45  * Base class for device policy tests. It offers utility methods to run tests, set device or profile
46  * owner, etc.
47  */
48 public class BaseDevicePolicyTest extends DeviceTestCase implements IBuildReceiver {
49 
50     protected static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
51     protected static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
52     protected static final String ADMIN_RECEIVER_TEST_CLASS =
53             MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
54 
55     private static final String RUNNER = "android.test.InstrumentationTestRunner";
56 
57     private static final String[] REQUIRED_DEVICE_FEATURES = new String[] {
58         "android.software.managed_users",
59         "android.software.device_admin" };
60 
61     private CtsBuildHelper mCtsBuild;
62 
63     protected boolean mHasFeature;
64 
65     @Override
setBuild(IBuildInfo buildInfo)66     public void setBuild(IBuildInfo buildInfo) {
67         mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
68     }
69 
70     @Override
setUp()71     protected void setUp() throws Exception {
72         super.setUp();
73         assertNotNull(mCtsBuild);  // ensure build has been set before test is run.
74         mHasFeature = getDevice().getApiLevel() >= 21 /* Build.VERSION_CODES.L */
75                 && hasDeviceFeatures(REQUIRED_DEVICE_FEATURES);
76     }
77 
installApp(String fileName)78     protected void installApp(String fileName)
79             throws FileNotFoundException, DeviceNotAvailableException {
80         CLog.logAndDisplay(LogLevel.INFO, "Installing app " + fileName);
81         String installResult = getDevice().installPackage(mCtsBuild.getTestApp(fileName), true);
82         assertNull(String.format("Failed to install %s, Reason: %s", fileName, installResult),
83                 installResult);
84     }
85 
installAppAsUser(String appFileName, int userId)86     protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
87             DeviceNotAvailableException {
88         final ITestDevice device = getDevice();
89 
90         final File apk = mCtsBuild.getTestApp(appFileName);
91         final String remotePath = "/data/local/tmp/" + apk.getName();
92         if (!device.pushFile(apk, remotePath)) {
93             throw new IllegalStateException("Failed to push " + apk);
94         }
95 
96         final String result = device.executeShellCommand(
97                 "pm install --user " + userId + " " + remotePath);
98         assertTrue(result, result.contains("\nSuccess"));
99     }
100 
101     /** Initializes the user with the given id. This is required so that apps can run on it. */
startUser(int userId)102     protected void startUser(int userId) throws Exception {
103         String command = "am start-user " + userId;
104         CLog.logAndDisplay(LogLevel.INFO, "Starting command " + command);
105         String commandOutput = getDevice().executeShellCommand(command);
106         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
107         assertTrue(commandOutput + " expected to start with \"Success:\"",
108                 commandOutput.startsWith("Success:"));
109         // Wait 60 seconds for intents generated to be handled.
110         Thread.sleep(60 * 1000);
111     }
112 
getMaxNumberOfUsersSupported()113     protected int getMaxNumberOfUsersSupported() throws DeviceNotAvailableException {
114         // TODO: move this to ITestDevice once it supports users
115         String command = "pm get-max-users";
116         String commandOutput = getDevice().executeShellCommand(command);
117         CLog.i("Output for command " + command + ": " + commandOutput);
118 
119         try {
120             return Integer.parseInt(commandOutput.substring(commandOutput.lastIndexOf(" ")).trim());
121         } catch (NumberFormatException e) {
122             fail("Failed to parse result: " + commandOutput);
123         }
124         return 0;
125     }
126 
listUsers()127     protected ArrayList<Integer> listUsers() throws DeviceNotAvailableException {
128         String command = "pm list users";
129         String commandOutput = getDevice().executeShellCommand(command);
130         CLog.i("Output for command " + command + ": " + commandOutput);
131 
132         // Extract the id of all existing users.
133         String[] lines = commandOutput.split("\\r?\\n");
134         assertTrue(commandOutput + " should contain at least one line", lines.length >= 1);
135         assertEquals(commandOutput, lines[0], "Users:");
136 
137         ArrayList<Integer> users = new ArrayList<Integer>();
138         for (int i = 1; i < lines.length; i++) {
139             // Individual user is printed out like this:
140             // \tUserInfo{$id$:$name$:$Integer.toHexString(flags)$} [running]
141             String[] tokens = lines[i].split("\\{|\\}|:");
142             assertTrue(lines[i] + " doesn't contain 4 or 5 tokens",
143                     tokens.length == 4 || tokens.length == 5);
144             users.add(Integer.parseInt(tokens[1]));
145         }
146         return users;
147     }
148 
removeUser(int userId)149     protected void removeUser(int userId) throws Exception  {
150         String removeUserCommand = "pm remove-user " + userId;
151         CLog.logAndDisplay(LogLevel.INFO, "starting command " + removeUserCommand);
152         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + removeUserCommand + ": "
153                 + getDevice().executeShellCommand(removeUserCommand));
154         // Wait 60 seconds for user to finish being removed.
155         Thread.sleep(60 * 1000);
156     }
157 
158     /** Returns true if the specified tests passed. Tests are run as user owner. */
runDeviceTests(String pkgName, @Nullable String testClassName)159     protected boolean runDeviceTests(String pkgName, @Nullable String testClassName)
160             throws DeviceNotAvailableException {
161         return runDeviceTests(pkgName, testClassName, null /*testMethodName*/, null /*userId*/);
162     }
163 
164     /** Returns true if the specified tests passed. Tests are run as given user. */
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, int userId)165     protected boolean runDeviceTestsAsUser(
166             String pkgName, @Nullable String testClassName, int userId)
167             throws DeviceNotAvailableException {
168         return runDeviceTestsAsUser(pkgName, testClassName, null, userId);
169     }
170 
171     /** Returns true if the specified tests passed. Tests are run as given user. */
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, String testMethodName, int userId)172     protected boolean runDeviceTestsAsUser(
173             String pkgName, @Nullable String testClassName, String testMethodName, int userId)
174             throws DeviceNotAvailableException {
175         if (testClassName.startsWith(".")) {
176             testClassName = pkgName + testClassName;
177         }
178         return runDeviceTests(pkgName, testClassName, testMethodName, userId);
179     }
180 
runDeviceTests(String pkgName, @Nullable String testClassName, @Nullable String testMethodName, @Nullable Integer userId)181     protected boolean runDeviceTests(String pkgName, @Nullable String testClassName,
182             @Nullable String testMethodName, @Nullable Integer userId)
183             throws DeviceNotAvailableException {
184         return runDeviceTests(pkgName, testClassName, testMethodName, userId, /*params*/ null);
185     }
186 
runDeviceTests(String pkgName, @Nullable String testClassName, @Nullable String testMethodName, @Nullable Integer userId, @Nullable String params)187     protected boolean runDeviceTests(String pkgName, @Nullable String testClassName,
188             @Nullable String testMethodName, @Nullable Integer userId, @Nullable String params)
189                    throws DeviceNotAvailableException {
190         TestRunResult runResult = (userId == null && params == null)
191                 ? doRunTests(pkgName, testClassName, testMethodName)
192                 : doRunTestsAsUser(pkgName, testClassName, testMethodName,
193                         userId != null ? userId : 0, params != null ? params : "");
194         printTestResult(runResult);
195         return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
196     }
197 
198     /** Helper method to run tests and return the listener that collected the results. */
doRunTests( String pkgName, String testClassName, String testMethodName)199     private TestRunResult doRunTests(
200             String pkgName, String testClassName,
201             String testMethodName) throws DeviceNotAvailableException {
202         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
203                 pkgName, RUNNER, getDevice().getIDevice());
204         if (testClassName != null && testMethodName != null) {
205             testRunner.setMethodName(testClassName, testMethodName);
206         } else if (testClassName != null) {
207             testRunner.setClassName(testClassName);
208         }
209 
210         CollectingTestListener listener = new CollectingTestListener();
211         assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
212         return listener.getCurrentRunResults();
213     }
214 
doRunTestsAsUser(String pkgName, @Nullable String testClassName, @Nullable String testMethodName, int userId, String params)215     private TestRunResult doRunTestsAsUser(String pkgName, @Nullable String testClassName,
216             @Nullable String testMethodName, int userId, String params)
217             throws DeviceNotAvailableException {
218         // TODO: move this to RemoteAndroidTestRunner once it supports users. Should be straight
219         // forward to add a RemoteAndroidTestRunner.setUser(userId) method. Then we can merge both
220         // doRunTests* methods.
221         StringBuilder testsToRun = new StringBuilder();
222         if (testClassName != null) {
223             testsToRun.append("-e class " + testClassName);
224             if (testMethodName != null) {
225                 testsToRun.append("#" + testMethodName);
226             }
227         }
228         String command = "am instrument --user " + userId + " " + params + " -w -r "
229                 + testsToRun + " " + pkgName + "/" + RUNNER;
230         CLog.i("Running " + command);
231 
232         CollectingTestListener listener = new CollectingTestListener();
233         InstrumentationResultParser parser = new InstrumentationResultParser(pkgName, listener);
234         getDevice().executeShellCommand(command, parser);
235         return listener.getCurrentRunResults();
236     }
237 
printTestResult(TestRunResult runResult)238     private void printTestResult(TestRunResult runResult) {
239         for (Map.Entry<TestIdentifier, TestResult> testEntry :
240                 runResult.getTestResults().entrySet()) {
241             TestResult testResult = testEntry.getValue();
242             CLog.logAndDisplay(LogLevel.INFO,
243                     "Test " + testEntry.getKey() + ": " + testResult.getStatus());
244             if (testResult.getStatus() != TestStatus.PASSED) {
245                 CLog.logAndDisplay(LogLevel.WARN, testResult.getStackTrace());
246             }
247         }
248     }
249 
hasDeviceFeatures(String[] requiredFeatures)250     protected boolean hasDeviceFeatures(String[] requiredFeatures)
251             throws DeviceNotAvailableException {
252         // TODO: Move this logic to ITestDevice.
253         String command = "pm list features";
254         String commandOutput = getDevice().executeShellCommand(command);
255         CLog.i("Output for command " + command + ": " + commandOutput);
256 
257         // Extract the id of the new user.
258         HashSet<String> availableFeatures = new HashSet<String>();
259         for (String feature: commandOutput.split("\\s+")) {
260             // Each line in the output of the command has the format "feature:{FEATURE_VALUE}".
261             String[] tokens = feature.split(":");
262             assertTrue("\"" + feature + "\" expected to have format feature:{FEATURE_VALUE}",
263                     tokens.length > 1);
264             assertEquals(feature, "feature", tokens[0]);
265             availableFeatures.add(tokens[1]);
266         }
267 
268         for (String requiredFeature : requiredFeatures) {
269             if(!availableFeatures.contains(requiredFeature)) {
270                 CLog.logAndDisplay(LogLevel.INFO, "Device doesn't have required feature "
271                         + requiredFeature + ". Tests won't run.");
272                 return false;
273             }
274         }
275         return true;
276     }
277 
createUser()278     protected int createUser() throws Exception {
279         String command ="pm create-user TestUser_"+ System.currentTimeMillis();
280         CLog.logAndDisplay(LogLevel.INFO, "Starting command " + command);
281         String commandOutput = getDevice().executeShellCommand(command);
282         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
283 
284         // Extract the id of the new user.
285         String[] tokens = commandOutput.split("\\s+");
286         assertTrue(tokens.length > 0);
287         assertEquals("Success:", tokens[0]);
288         // Wait 60 seconds for intents generated to be handled.
289         Thread.sleep(60 * 1000);
290         return Integer.parseInt(tokens[tokens.length-1]);
291     }
292 
createManagedProfile()293     protected int createManagedProfile() throws DeviceNotAvailableException {
294         String command =
295                 "pm create-user --profileOf 0 --managed TestProfile_" + System.currentTimeMillis();
296         CLog.logAndDisplay(LogLevel.INFO, "Starting command " + command);
297         String commandOutput = getDevice().executeShellCommand(command);
298         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
299 
300         // Extract the id of the new user.
301         String[] tokens = commandOutput.split("\\s+");
302         assertTrue(commandOutput + " expected to have format \"Success: {USER_ID}\"",
303                 tokens.length > 0);
304         assertEquals(commandOutput, "Success:", tokens[0]);
305         return Integer.parseInt(tokens[tokens.length-1]);
306     }
307 
308 
getUserSerialNumber(int userId)309     protected int getUserSerialNumber(int userId) throws DeviceNotAvailableException{
310         // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0"
311         String commandOutput = getDevice().executeShellCommand("dumpsys user");
312         String[] tokens = commandOutput.split("\\n");
313         for (String token : tokens) {
314             token = token.trim();
315             if (token.contains("UserInfo{" + userId + ":")) {
316                 String[] split = token.split("serialNo=");
317                 assertTrue(split.length == 2);
318                 int serialNumber = Integer.parseInt(split[1]);
319                 CLog.logAndDisplay(LogLevel.INFO, "Serial number of user " + userId + ": "
320                         + serialNumber);
321                 return serialNumber;
322             }
323         }
324         fail("Couldn't find user " + userId);
325         return -1;
326     }
327 
setProfileOwner(String componentName, int userId)328     protected void setProfileOwner(String componentName, int userId)
329             throws DeviceNotAvailableException {
330         String command = "dpm set-profile-owner '" + componentName + "' " + userId;
331         String commandOutput = getDevice().executeShellCommand(command);
332         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
333         assertTrue(commandOutput + " expected to start with \"Success:\"",
334                 commandOutput.startsWith("Success:"));
335     }
336 }
337