• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.media.cts;
18 
19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
20 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.log.LogUtil.CLog;
23 import com.android.tradefed.result.CollectingTestListener;
24 import com.android.tradefed.result.TestRunResult;
25 
26 import java.io.FileNotFoundException;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 
31 import javax.annotation.Nonnull;
32 import javax.annotation.Nullable;
33 
34 /**
35  * Base class for host-side tests for multi-user aware media APIs.
36  */
37 public class BaseMultiUserTest extends BaseMediaHostSideTest {
38     private static final String SETTINGS_PACKAGE_VERIFIER_NAMESPACE = "global";
39     private static final String SETTINGS_PACKAGE_VERIFIER_NAME = "package_verifier_enable";
40 
41     /**
42      * User ID for all users.
43      * The value is from the UserHandle class.
44      */
45     protected static final int USER_ALL = -1;
46 
47     /**
48      * User ID for the system user.
49      * The value is from the UserHandle class.
50      */
51     protected static final int USER_SYSTEM = 0;
52 
53     private String mPackageVerifier;
54 
55     private int mInitialUserId;
56     private Set<String> mExistingPackages;
57     private List<Integer> mExistingUsers;
58     private HashSet<String> mAvailableFeatures;
59 
60     @Override
setUp()61     protected void setUp() throws Exception {
62         super.setUp();
63         // Ensure that build has been set before test is run.
64         assertNotNull(mCtsBuild);
65         mExistingPackages = getDevice().getInstalledPackageNames();
66 
67         // Disable the package verifier to avoid the dialog when installing an app
68         mPackageVerifier =
69                 getSettings(
70                         SETTINGS_PACKAGE_VERIFIER_NAMESPACE,
71                         SETTINGS_PACKAGE_VERIFIER_NAME,
72                         USER_ALL);
73         putSettings(
74                 SETTINGS_PACKAGE_VERIFIER_NAMESPACE,
75                 SETTINGS_PACKAGE_VERIFIER_NAME,
76                 "0",
77                 USER_ALL);
78 
79         mInitialUserId = getDevice().getCurrentUser();
80         mExistingUsers = getDevice().listUsers();
81         Integer mainUserId = getDevice().getMainUserId();
82         Integer primaryUserId = getDevice().getPrimaryUserId();
83         if (primaryUserId != null) {
84             getDevice().switchUser(primaryUserId);
85         } else if (mainUserId != null) {
86             getDevice().switchUser(mainUserId);
87         } else {
88             // Neither a primary nor a main user exists. Just use the current one.
89         }
90         executeShellCommand("wm dismiss-keyguard");
91     }
92 
93     @Override
tearDown()94     protected void tearDown() throws Exception {
95         // Reset the package verifier setting to its original value.
96         putSettings(
97                 SETTINGS_PACKAGE_VERIFIER_NAMESPACE,
98                 SETTINGS_PACKAGE_VERIFIER_NAME,
99                 mPackageVerifier,
100                 USER_ALL);
101 
102         // We want to fail if something goes wrong in tearDown, but we want to complete as many
103         // cleanup steps as we can to reduce the chances of leaving the device in an inconsistent
104         // state.
105         Throwable lastTearDownError = null;
106 
107         // Remove users created during the test.
108         for (int userId : getDevice().listUsers()) {
109             if (!mExistingUsers.contains(userId)) {
110                 try {
111                     removeUser(userId);
112                 } catch (Throwable t) {
113                     lastTearDownError = t;
114                 }
115             }
116         }
117         // Remove packages installed during the test.
118         for (String packageName : getDevice().getUninstallablePackageNames()) {
119             if (mExistingPackages.contains(packageName)) {
120                 continue;
121             }
122             CLog.d("Removing leftover package: " + packageName);
123             try {
124                 getDevice().uninstallPackage(packageName);
125             } catch (Throwable t) {
126                 lastTearDownError = t;
127             }
128         }
129         if (getDevice().getCurrentUser() != mInitialUserId) {
130             getDevice().switchUser(mInitialUserId);
131         }
132         super.tearDown();
133         if (lastTearDownError != null) {
134             throw new AssertionError("Something went wrong while cleaning up.", lastTearDownError);
135         }
136     }
137 
138     /**
139      * Installs the app as if the user of the ID {@param userId} has installed the app.
140      *
141      * @param appFileName file name of the app.
142      * @param packageName the app's package name.
143      * @param userId user ID to install the app against.
144      * @param asInstantApp whether to install the app as an instant app.
145      */
installAppAsUser( String appFileName, String packageName, int userId, boolean asInstantApp)146     protected void installAppAsUser(
147             String appFileName, String packageName, int userId, boolean asInstantApp)
148             throws FileNotFoundException, DeviceNotAvailableException {
149         // Installation may fail if the package already exists.
150         getDevice().uninstallPackage(packageName);
151 
152         CLog.d("Installing app " + appFileName + " for user " + userId);
153         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
154         String result = getDevice().installPackageForUser(
155                 buildHelper.getTestFile(appFileName),
156                 true,
157                 true,
158                 userId,
159                 "-t",
160                 asInstantApp ? "--instant" : "");
161         assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result,
162                 result);
163     }
164 
createAndStartUser(String extraParam)165     private int createAndStartUser(String extraParam) throws Exception {
166         String command = "pm create-user" + extraParam + " TestUser_" + System.currentTimeMillis();
167         String commandOutput = executeShellCommand(command);
168 
169         String[] tokens = commandOutput.split("\\s+");
170         assertTrue(tokens.length > 0);
171         assertEquals("Success:", tokens[0]);
172         int userId = Integer.parseInt(tokens[tokens.length-1]);
173 
174         // Start user for MediaSessionService to notice the created user.
175         getDevice().startUser(userId);
176         return userId;
177     }
178 
179     /**
180      * Creates and starts a new user.
181      */
createAndStartUser()182     protected int createAndStartUser() throws Exception {
183         return createAndStartUser("");
184     }
185 
186     /**
187      * Creates and starts a restricted profile for the {@param parentUserId}.
188      *
189      * @param parentUserId parent user id.
190      */
createAndStartRestrictedProfile(int parentUserId)191     protected int createAndStartRestrictedProfile(int parentUserId) throws Exception {
192         return createAndStartUser(" --profileOf " + parentUserId + " --restricted");
193     }
194 
195     /**
196      * Creates and starts a managed profile for the {@param parentUserId}.
197      *
198      * @param parentUserId parent user id.
199      */
createAndStartManagedProfile(int parentUserId)200     protected int createAndStartManagedProfile(int parentUserId) throws Exception {
201         return createAndStartUser(" --profileOf " + parentUserId + " --managed");
202     }
203 
204     /**
205      * Removes the user that is created during the test.
206      * <p>It will be no-op if the user cannot be removed or doesn't exist.
207      *
208      * @param userId user ID to remove.
209      */
removeUser(int userId)210     protected void removeUser(int userId) throws Exception  {
211         if (getDevice().listUsers().contains(userId) && userId != USER_SYSTEM
212                 && !mExistingUsers.contains(userId)) {
213             getDevice().executeShellCommand("am wait-for-broadcast-idle");
214             // Don't log output, as tests sometimes set no debug user restriction, which
215             // causes this to fail, we should still continue and remove the user.
216             String stopUserCommand = "am stop-user -w -f " + userId;
217             CLog.d("Stopping and removing user " + userId);
218             getDevice().executeShellCommand(stopUserCommand);
219             assertTrue("Couldn't remove user", getDevice().removeUser(userId));
220         }
221     }
222 
223     /**
224      * Runs tests on the device as if it's {@param userId}.
225      *
226      * @param pkgName test package file name that contains the {@link AndroidTestCase}
227      * @param testClassName Class name to test within the test package. Can be {@code null} if you
228      *    want to run all test classes in the package.
229      * @param testMethodName Method name to test within the test class. Can be {@code null} if you
230      *    want to run all test methods in the class. Will be ignored if {@param testClassName} is
231      *    {@code null}.
232      * @param userId user ID to run the tests as.
233      */
runDeviceTests( String pkgName, @Nullable String testClassName, @Nullable String testMethodName, int userId)234     protected void runDeviceTests(
235             String pkgName, @Nullable String testClassName,
236             @Nullable String testMethodName, int userId) throws DeviceNotAvailableException {
237         RemoteAndroidTestRunner testRunner = getTestRunner(pkgName, testClassName, testMethodName);
238         CollectingTestListener listener = new CollectingTestListener();
239         assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener));
240 
241         final TestRunResult result = listener.getCurrentRunResults();
242         assertTestsPassed(result);
243     }
244 
245     /**
246      * Checks whether it is possible to create the desired number of users.
247      */
canCreateAdditionalUsers(int numberOfUsers)248     protected boolean canCreateAdditionalUsers(int numberOfUsers)
249             throws DeviceNotAvailableException {
250         return getDevice().listUsers().size() + numberOfUsers <=
251                 getDevice().getMaxNumberOfUsersSupported();
252     }
253 
254     /**
255      * Gets the system setting as a string from the system settings provider for the user.
256      *
257      * @param namespace namespace of the setting.
258      * @param name name of the setting.
259      * @param userId user ID to query the setting. Can be {@link #USER_ALL}.
260      * @return value of the system setting provider with the given namespace and name.
261      *    {@code null}, empty string, or "null" will be returned to the empty string ("") instead.
262      */
getSettings(@onnull String namespace, @Nonnull String name, int userId)263     protected @Nonnull String getSettings(@Nonnull String namespace, @Nonnull String name,
264             int userId) throws Exception {
265         String userFlag = (userId == USER_ALL) ? "" : " --user " + userId;
266         String commandOutput = executeShellCommand(
267                 "settings" + userFlag + " get " + namespace + " " + name);
268         if (commandOutput == null || commandOutput.isEmpty() || commandOutput.equals("null")) {
269             commandOutput = "";
270         }
271         return commandOutput;
272     }
273 
274     /**
275      * Puts the string to the system settings provider for the user.
276      * <p>This deletes the setting for an empty {@param value} as 'settings put' doesn't allow
277      * putting empty value.
278      *
279      * @param namespace namespace of the setting.
280      * @param name name of the setting.
281      * @param value value of the system setting provider with the given namespace and name.
282      * @param userId user ID to set the setting. Can be {@link #USER_ALL}.
283      */
putSettings(@onnull String namespace, @Nonnull String name, @Nullable String value, int userId)284     protected void putSettings(@Nonnull String namespace, @Nonnull String name,
285             @Nullable String value, int userId) throws Exception {
286         if (value == null || value.isEmpty()) {
287             // Delete the setting if the value is null or empty as 'settings put' doesn't accept
288             // them.
289             // Ignore userId here because 'settings delete' doesn't support it.
290             executeShellCommand("settings delete " + namespace + " " + name);
291         } else {
292             String userFlag = (userId == USER_ALL) ? "" : " --user " + userId;
293             executeShellCommand("settings" + userFlag + " put " + namespace + " " + name
294                     + " " + value);
295         }
296     }
297 
hasDeviceFeature(String requiredFeature)298     protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
299         if (mAvailableFeatures == null) {
300             // TODO: Move this logic to ITestDevice.
301             String command = "pm list features";
302             String commandOutput = getDevice().executeShellCommand(command);
303             CLog.i("Output for command " + command + ": " + commandOutput);
304 
305             // Extract the id of the new user.
306             mAvailableFeatures = new HashSet<>();
307             for (String feature : commandOutput.split("\\s+")) {
308                 // Each line in the output of the command has the format "feature:{FEATURE_VALUE}".
309                 String[] tokens = feature.split(":");
310                 assertTrue(
311                         "\"" + feature + "\" expected to have format feature:{FEATURE_VALUE}",
312                         tokens.length > 1);
313                 assertEquals(feature, "feature", tokens[0]);
314                 mAvailableFeatures.add(tokens[1]);
315             }
316         }
317         boolean result = mAvailableFeatures.contains(requiredFeature);
318         return result;
319     }
320 }
321