1 /*
2  * Copyright (C) 2019 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.server.pm;
18 
19 import static android.content.pm.UserInfo.FLAG_DEMO;
20 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
21 import static android.content.pm.UserInfo.FLAG_FULL;
22 import static android.content.pm.UserInfo.FLAG_GUEST;
23 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
24 import static android.content.pm.UserInfo.FLAG_PROFILE;
25 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
26 import static android.content.pm.UserInfo.FLAG_SYSTEM;
27 
28 import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assert.assertNotNull;
33 import static org.junit.Assert.assertNotSame;
34 import static org.junit.Assert.assertTrue;
35 import static org.testng.Assert.assertThrows;
36 
37 import android.content.pm.UserInfo;
38 import android.content.res.Resources;
39 import android.content.res.XmlResourceParser;
40 import android.os.Bundle;
41 import android.os.UserManager;
42 import android.util.ArrayMap;
43 
44 import androidx.test.InstrumentationRegistry;
45 import androidx.test.filters.MediumTest;
46 import androidx.test.runner.AndroidJUnit4;
47 
48 import com.android.frameworks.servicestests.R;
49 
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 /**
55  * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
56  *
57  * <p>Run with: atest UserManagerServiceUserTypeTest
58  */
59 @RunWith(AndroidJUnit4.class)
60 @MediumTest
61 public class UserManagerServiceUserTypeTest {
62 
63     private Resources mResources;
64 
65     @Before
setup()66     public void setup() {
67         mResources = InstrumentationRegistry.getTargetContext().getResources();
68     }
69 
70     @Test
testUserTypeBuilder_createUserType()71     public void testUserTypeBuilder_createUserType() {
72         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
73         final UserTypeDetails type = new UserTypeDetails.Builder()
74                 .setName("a.name")
75                 .setEnabled(true)
76                 .setMaxAllowed(21)
77                 .setBaseType(FLAG_FULL)
78                 .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
79                 .setBadgeLabels(23, 24, 25)
80                 .setBadgeColors(26, 27)
81                 .setIconBadge(28)
82                 .setBadgePlain(29)
83                 .setBadgeNoBackground(30)
84                 .setLabel(31)
85                 .setMaxAllowedPerParent(32)
86                 .setDefaultRestrictions(restrictions)
87                 .createUserTypeDetails();
88 
89         assertEquals("a.name", type.getName());
90         assertTrue(type.isEnabled());
91         assertEquals(21, type.getMaxAllowed());
92         assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
93         assertEquals(28, type.getIconBadge());
94         assertEquals(29, type.getBadgePlain());
95         assertEquals(30, type.getBadgeNoBackground());
96         assertEquals(31, type.getLabel());
97         assertEquals(32, type.getMaxAllowedPerParent());
98         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
99         assertNotSame(restrictions, type.getDefaultRestrictions());
100 
101 
102         assertEquals(23, type.getBadgeLabel(0));
103         assertEquals(24, type.getBadgeLabel(1));
104         assertEquals(25, type.getBadgeLabel(2));
105         assertEquals(25, type.getBadgeLabel(3));
106         assertEquals(25, type.getBadgeLabel(4));
107         assertEquals(Resources.ID_NULL, type.getBadgeLabel(-1));
108 
109         assertEquals(26, type.getBadgeColor(0));
110         assertEquals(27, type.getBadgeColor(1));
111         assertEquals(27, type.getBadgeColor(2));
112         assertEquals(27, type.getBadgeColor(3));
113         assertEquals(Resources.ID_NULL, type.getBadgeColor(-100));
114 
115         assertTrue(type.hasBadge());
116     }
117 
118     @Test
testUserTypeBuilder_defaults()119     public void testUserTypeBuilder_defaults() {
120         UserTypeDetails type = new UserTypeDetails.Builder()
121                 .setName("name") // Required (no default allowed)
122                 .setBaseType(FLAG_FULL) // Required (no default allowed)
123                 .createUserTypeDetails();
124 
125         assertTrue(type.isEnabled());
126         assertEquals(UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowed());
127         assertEquals(UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowedPerParent());
128         assertEquals(FLAG_FULL, type.getDefaultUserInfoFlags());
129         assertEquals(Resources.ID_NULL, type.getIconBadge());
130         assertEquals(Resources.ID_NULL, type.getBadgePlain());
131         assertEquals(Resources.ID_NULL, type.getBadgeNoBackground());
132         assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
133         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
134         assertEquals(Resources.ID_NULL, type.getLabel());
135         assertTrue(type.getDefaultRestrictions().isEmpty());
136 
137         assertFalse(type.hasBadge());
138     }
139 
140     @Test
testUserTypeBuilder_nameIsRequired()141     public void testUserTypeBuilder_nameIsRequired() {
142         assertThrows(IllegalArgumentException.class,
143                 () -> new UserTypeDetails.Builder()
144                         .setMaxAllowed(21)
145                         .setBaseType(FLAG_FULL)
146                         .createUserTypeDetails());
147     }
148 
149     @Test
testUserTypeBuilder_baseTypeIsRequired()150     public void testUserTypeBuilder_baseTypeIsRequired() {
151         assertThrows(IllegalArgumentException.class,
152                 () -> new UserTypeDetails.Builder()
153                         .setName("name")
154                         .createUserTypeDetails());
155     }
156 
157     @Test
testUserTypeBuilder_colorIsRequiredIfBadged()158     public void testUserTypeBuilder_colorIsRequiredIfBadged() {
159         assertThrows(IllegalArgumentException.class,
160                 () -> getMinimalBuilder()
161                         .setIconBadge(1)
162                         .setBadgeLabels(2)
163                         .createUserTypeDetails());
164     }
165 
166     @Test
testUserTypeBuilder_badgeLabelIsRequiredIfBadged()167     public void testUserTypeBuilder_badgeLabelIsRequiredIfBadged() {
168         assertThrows(IllegalArgumentException.class,
169                 () -> getMinimalBuilder()
170                         .setIconBadge(1)
171                         .setBadgeColors(2)
172                         .createUserTypeDetails());
173     }
174 
175     @Test
testCheckUserTypeConsistency()176     public void testCheckUserTypeConsistency() {
177         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST));
178         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST | FLAG_EPHEMERAL));
179         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE));
180 
181         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_DEMO | FLAG_RESTRICTED));
182         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_SYSTEM));
183         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_FULL));
184     }
185 
186     @Test
testGetDefaultUserType()187     public void testGetDefaultUserType() {
188         // Simple example.
189         assertEquals(UserManager.USER_TYPE_FULL_RESTRICTED,
190                 UserInfo.getDefaultUserType(FLAG_RESTRICTED));
191 
192         // Type plus a non-type flag.
193         assertEquals(UserManager.USER_TYPE_FULL_GUEST,
194                 UserInfo.getDefaultUserType(FLAG_GUEST | FLAG_EPHEMERAL));
195 
196         // Two types, which is illegal.
197         assertThrows(IllegalArgumentException.class,
198                 () -> UserInfo.getDefaultUserType(FLAG_MANAGED_PROFILE | FLAG_GUEST));
199 
200         // No type, which defaults to {@link UserManager#USER_TYPE_FULL_SECONDARY}.
201         assertEquals(UserManager.USER_TYPE_FULL_SECONDARY,
202                 UserInfo.getDefaultUserType(FLAG_EPHEMERAL));
203     }
204 
205     /** Tests {@link UserTypeFactory#customizeBuilders} for a reasonable xml file. */
206     @Test
testUserTypeFactoryCustomize_profile()207     public void testUserTypeFactoryCustomize_profile() throws Exception {
208         final String userTypeAosp1 = "android.test.1"; // Profile user that is not customized
209         final String userTypeAosp2 = "android.test.2"; // Profile user that is customized
210         final String userTypeOem1 = "custom.test.1"; // Custom-defined profile
211 
212         // Mock some "AOSP defaults".
213         final Bundle restrictions = makeRestrictionsBundle("no_config_vpn", "no_config_tethering");
214         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
215         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
216                 .setName(userTypeAosp1)
217                 .setBaseType(FLAG_PROFILE)
218                 .setMaxAllowedPerParent(31)
219                 .setDefaultRestrictions(restrictions));
220         builders.put(userTypeAosp2, new UserTypeDetails.Builder()
221                 .setName(userTypeAosp1)
222                 .setBaseType(FLAG_PROFILE)
223                 .setMaxAllowedPerParent(32)
224                 .setIconBadge(401)
225                 .setBadgeColors(402, 403, 404)
226                 .setDefaultRestrictions(restrictions));
227 
228         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
229         UserTypeFactory.customizeBuilders(builders, parser);
230 
231         // userTypeAosp1 should not be modified.
232         UserTypeDetails aospType = builders.get(userTypeAosp1).createUserTypeDetails();
233         assertEquals(31, aospType.getMaxAllowedPerParent());
234         assertEquals(Resources.ID_NULL, aospType.getIconBadge());
235         assertTrue(UserRestrictionsUtils.areEqual(restrictions, aospType.getDefaultRestrictions()));
236 
237         // userTypeAosp2 should be modified.
238         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
239         assertEquals(12, aospType.getMaxAllowedPerParent());
240         assertEquals(com.android.internal.R.drawable.ic_corp_icon_badge_case,
241                 aospType.getIconBadge());
242         assertEquals(Resources.ID_NULL, aospType.getBadgePlain()); // No resId for 'garbage'
243         assertEquals(com.android.internal.R.drawable.ic_corp_badge_no_background,
244                 aospType.getBadgeNoBackground());
245         assertEquals(com.android.internal.R.string.managed_profile_label_badge,
246                 aospType.getBadgeLabel(0));
247         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
248                 aospType.getBadgeLabel(1));
249         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
250                 aospType.getBadgeLabel(2));
251         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
252                 aospType.getBadgeLabel(3));
253         assertEquals(com.android.internal.R.color.profile_badge_1,
254                 aospType.getBadgeColor(0));
255         assertEquals(com.android.internal.R.color.profile_badge_2,
256                 aospType.getBadgeColor(1));
257         assertEquals(com.android.internal.R.color.profile_badge_2,
258                 aospType.getBadgeColor(2));
259         assertEquals(com.android.internal.R.color.profile_badge_2,
260                 aospType.getBadgeColor(3));
261         assertTrue(UserRestrictionsUtils.areEqual(
262                 makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
263                 aospType.getDefaultRestrictions()));
264 
265         // userTypeOem1 should be created.
266         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
267         assertNotNull(customType);
268         assertEquals(14, customType.createUserTypeDetails().getMaxAllowedPerParent());
269     }
270 
271     /** Tests {@link UserTypeFactory#customizeBuilders} for customizing a FULL user. */
272     @Test
testUserTypeFactoryCustomize_full()273     public void testUserTypeFactoryCustomize_full() throws Exception {
274         final String userTypeFull = "android.test.1";
275 
276         // Mock "AOSP default".
277         final Bundle restrictions = makeRestrictionsBundle("no_config_vpn", "no_config_tethering");
278         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
279         builders.put(userTypeFull, new UserTypeDetails.Builder()
280                 .setName(userTypeFull)
281                 .setBaseType(FLAG_FULL)
282                 .setDefaultRestrictions(restrictions));
283 
284         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
285         UserTypeFactory.customizeBuilders(builders, parser);
286 
287         UserTypeDetails details = builders.get(userTypeFull).createUserTypeDetails();
288         assertEquals(UNLIMITED_NUMBER_OF_USERS, details.getMaxAllowedPerParent());
289         assertTrue(UserRestrictionsUtils.areEqual(
290                 makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
291                 details.getDefaultRestrictions()));
292         assertEquals(Resources.ID_NULL, details.getBadgeColor(0));
293     }
294 
295     /**
296      * Tests {@link UserTypeFactory#customizeBuilders} when custom user type deletes the
297      * badge-colors and restrictions.
298      */
299     @Test
testUserTypeFactoryCustomize_eraseArray()300     public void testUserTypeFactoryCustomize_eraseArray() throws Exception {
301         final String typeName = "android.test";
302 
303         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
304         builders.put(typeName, new UserTypeDetails.Builder()
305                 .setName(typeName)
306                 .setBaseType(FLAG_PROFILE)
307                 .setMaxAllowedPerParent(1)
308                 .setBadgeColors(501, 502)
309                 .setDefaultRestrictions(makeRestrictionsBundle("r1")));
310 
311         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray);
312         UserTypeFactory.customizeBuilders(builders, parser);
313 
314         UserTypeDetails typeDetails =  builders.get(typeName).createUserTypeDetails();
315         assertEquals(2, typeDetails.getMaxAllowedPerParent());
316         assertEquals(Resources.ID_NULL, typeDetails.getBadgeColor(0));
317         assertEquals(Resources.ID_NULL, typeDetails.getBadgeColor(1));
318         assertTrue(typeDetails.getDefaultRestrictions().isEmpty());
319     }
320 
321     /** Tests {@link UserTypeFactory#customizeBuilders} when custom user type has illegal name. */
322     @Test
testUserTypeFactoryCustomize_illegalOemName()323     public void testUserTypeFactoryCustomize_illegalOemName() throws Exception {
324         final String userTypeAosp = "android.aosp.legal";
325         final String userTypeOem = "android.oem.illegal.name"; // Custom-defined profile
326 
327         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
328         builders.put(userTypeAosp, new UserTypeDetails.Builder()
329                 .setName(userTypeAosp)
330                 .setBaseType(FLAG_PROFILE)
331                 .setMaxAllowedPerParent(21));
332 
333         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_illegalOemName);
334 
335         // parser is illegal because non-AOSP user types cannot be prefixed with "android.".
336         assertThrows(IllegalArgumentException.class,
337                 () -> UserTypeFactory.customizeBuilders(builders, parser));
338     }
339 
340     /**
341      * Tests {@link UserTypeFactory#customizeBuilders} when illegally customizing a non-profile as
342      * a profile.
343      */
344     @Test
testUserTypeFactoryCustomize_illegalUserBaseType()345     public void testUserTypeFactoryCustomize_illegalUserBaseType() throws Exception {
346         final String userTypeFull = "android.test";
347 
348         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
349         builders.put(userTypeFull, new UserTypeDetails.Builder()
350                 .setName(userTypeFull)
351                 .setBaseType(FLAG_FULL)
352                 .setMaxAllowedPerParent(21));
353 
354         XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_illegalUserBaseType);
355 
356         // parser is illegal because userTypeFull is FULL but the tag is for profile-type.
357         assertThrows(IllegalArgumentException.class,
358                 () -> UserTypeFactory.customizeBuilders(builders, parser));
359     }
360 
361     /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */
getMinimalBuilder()362     private UserTypeDetails.Builder getMinimalBuilder() {
363         return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL);
364     }
365 
366     /** Creates a Bundle of the given String restrictions, each set to true. */
makeRestrictionsBundle(String .... restrictions)367     private Bundle makeRestrictionsBundle(String ... restrictions) {
368         final Bundle bundle = new Bundle();
369         for (String restriction : restrictions) {
370             bundle.putBoolean(restriction, true);
371         }
372         return bundle;
373     }
374 }
375