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 import java.util.List;
55 
56 /**
57  * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
58  *
59  * <p>Run with: atest UserManagerServiceUserTypeTest
60  */
61 @RunWith(AndroidJUnit4.class)
62 @MediumTest
63 public class UserManagerServiceUserTypeTest {
64 
65     private Resources mResources;
66 
67     @Before
setup()68     public void setup() {
69         mResources = InstrumentationRegistry.getTargetContext().getResources();
70     }
71 
72     @Test
testUserTypeBuilder_createUserType()73     public void testUserTypeBuilder_createUserType() {
74         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
75         final Bundle systemSettings = makeSettingsBundle("s1", "s2");
76         final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
77         final List<DefaultCrossProfileIntentFilter> filters = List.of(
78                 new DefaultCrossProfileIntentFilter.Builder(
79                 DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
80                 /* flags= */0,
81                 /* letsPersonalDataIntoProfile= */false).build());
82         final UserTypeDetails type = new UserTypeDetails.Builder()
83                 .setName("a.name")
84                 .setEnabled(true)
85                 .setMaxAllowed(21)
86                 .setBaseType(FLAG_PROFILE)
87                 .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
88                 .setBadgeLabels(23, 24, 25)
89                 .setBadgeColors(26, 27)
90                 .setIconBadge(28)
91                 .setBadgePlain(29)
92                 .setBadgeNoBackground(30)
93                 .setLabel(31)
94                 .setMaxAllowedPerParent(32)
95                 .setDefaultRestrictions(restrictions)
96                 .setDefaultSystemSettings(systemSettings)
97                 .setDefaultSecureSettings(secureSettings)
98                 .setDefaultCrossProfileIntentFilters(filters)
99                 .createUserTypeDetails();
100 
101         assertEquals("a.name", type.getName());
102         assertTrue(type.isEnabled());
103         assertEquals(21, type.getMaxAllowed());
104         assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
105         assertEquals(28, type.getIconBadge());
106         assertEquals(29, type.getBadgePlain());
107         assertEquals(30, type.getBadgeNoBackground());
108         assertEquals(31, type.getLabel());
109         assertEquals(32, type.getMaxAllowedPerParent());
110 
111         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
112         assertNotSame(restrictions, type.getDefaultRestrictions());
113 
114         assertNotSame(systemSettings, type.getDefaultSystemSettings());
115         assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
116         for (String key : systemSettings.keySet()) {
117             assertEquals(
118                     systemSettings.getString(key),
119                     type.getDefaultSystemSettings().getString(key));
120         }
121 
122         assertNotSame(secureSettings, type.getDefaultSecureSettings());
123         assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
124         for (String key : secureSettings.keySet()) {
125             assertEquals(
126                     secureSettings.getString(key),
127                     type.getDefaultSecureSettings().getString(key));
128         }
129 
130         assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
131         assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
132         for (int i = 0; i < filters.size(); i++) {
133             assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
134         }
135 
136         assertEquals(23, type.getBadgeLabel(0));
137         assertEquals(24, type.getBadgeLabel(1));
138         assertEquals(25, type.getBadgeLabel(2));
139         assertEquals(25, type.getBadgeLabel(3));
140         assertEquals(25, type.getBadgeLabel(4));
141         assertEquals(Resources.ID_NULL, type.getBadgeLabel(-1));
142 
143         assertEquals(26, type.getBadgeColor(0));
144         assertEquals(27, type.getBadgeColor(1));
145         assertEquals(27, type.getBadgeColor(2));
146         assertEquals(27, type.getBadgeColor(3));
147         assertEquals(Resources.ID_NULL, type.getBadgeColor(-100));
148 
149         assertTrue(type.hasBadge());
150     }
151 
152     @Test
testUserTypeBuilder_defaults()153     public void testUserTypeBuilder_defaults() {
154         UserTypeDetails type = new UserTypeDetails.Builder()
155                 .setName("name") // Required (no default allowed)
156                 .setBaseType(FLAG_FULL) // Required (no default allowed)
157                 .createUserTypeDetails();
158 
159         assertTrue(type.isEnabled());
160         assertEquals(UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowed());
161         assertEquals(UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowedPerParent());
162         assertEquals(FLAG_FULL, type.getDefaultUserInfoFlags());
163         assertEquals(Resources.ID_NULL, type.getIconBadge());
164         assertEquals(Resources.ID_NULL, type.getBadgePlain());
165         assertEquals(Resources.ID_NULL, type.getBadgeNoBackground());
166         assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
167         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
168         assertEquals(Resources.ID_NULL, type.getLabel());
169         assertTrue(type.getDefaultRestrictions().isEmpty());
170         assertTrue(type.getDefaultSystemSettings().isEmpty());
171         assertTrue(type.getDefaultSecureSettings().isEmpty());
172         assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
173 
174         assertFalse(type.hasBadge());
175     }
176 
177     @Test
testUserTypeBuilder_nameIsRequired()178     public void testUserTypeBuilder_nameIsRequired() {
179         assertThrows(IllegalArgumentException.class,
180                 () -> new UserTypeDetails.Builder()
181                         .setMaxAllowed(21)
182                         .setBaseType(FLAG_FULL)
183                         .createUserTypeDetails());
184     }
185 
186     @Test
testUserTypeBuilder_baseTypeIsRequired()187     public void testUserTypeBuilder_baseTypeIsRequired() {
188         assertThrows(IllegalArgumentException.class,
189                 () -> new UserTypeDetails.Builder()
190                         .setName("name")
191                         .createUserTypeDetails());
192     }
193 
194     @Test
testUserTypeBuilder_colorIsRequiredIfBadged()195     public void testUserTypeBuilder_colorIsRequiredIfBadged() {
196         assertThrows(IllegalArgumentException.class,
197                 () -> getMinimalBuilder()
198                         .setIconBadge(1)
199                         .setBadgeLabels(2)
200                         .createUserTypeDetails());
201     }
202 
203     @Test
testUserTypeBuilder_badgeLabelIsRequiredIfBadged()204     public void testUserTypeBuilder_badgeLabelIsRequiredIfBadged() {
205         assertThrows(IllegalArgumentException.class,
206                 () -> getMinimalBuilder()
207                         .setIconBadge(1)
208                         .setBadgeColors(2)
209                         .createUserTypeDetails());
210     }
211 
212     @Test
testCheckUserTypeConsistency()213     public void testCheckUserTypeConsistency() {
214         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST));
215         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST | FLAG_EPHEMERAL));
216         assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE));
217 
218         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_DEMO | FLAG_RESTRICTED));
219         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_SYSTEM));
220         assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_FULL));
221     }
222 
223     @Test
testGetDefaultUserType()224     public void testGetDefaultUserType() {
225         // Simple example.
226         assertEquals(UserManager.USER_TYPE_FULL_RESTRICTED,
227                 UserInfo.getDefaultUserType(FLAG_RESTRICTED));
228 
229         // Type plus a non-type flag.
230         assertEquals(UserManager.USER_TYPE_FULL_GUEST,
231                 UserInfo.getDefaultUserType(FLAG_GUEST | FLAG_EPHEMERAL));
232 
233         // Two types, which is illegal.
234         assertThrows(IllegalArgumentException.class,
235                 () -> UserInfo.getDefaultUserType(FLAG_MANAGED_PROFILE | FLAG_GUEST));
236 
237         // No type, which defaults to {@link UserManager#USER_TYPE_FULL_SECONDARY}.
238         assertEquals(UserManager.USER_TYPE_FULL_SECONDARY,
239                 UserInfo.getDefaultUserType(FLAG_EPHEMERAL));
240     }
241 
242     /** Tests {@link UserTypeFactory#customizeBuilders} for a reasonable xml file. */
243     @Test
testUserTypeFactoryCustomize_profile()244     public void testUserTypeFactoryCustomize_profile() throws Exception {
245         final String userTypeAosp1 = "android.test.1"; // Profile user that is not customized
246         final String userTypeAosp2 = "android.test.2"; // Profile user that is customized
247         final String userTypeOem1 = "custom.test.1"; // Custom-defined profile
248 
249         // Mock some "AOSP defaults".
250         final Bundle restrictions = makeRestrictionsBundle("no_config_vpn", "no_config_tethering");
251         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
252         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
253                 .setName(userTypeAosp1)
254                 .setBaseType(FLAG_PROFILE)
255                 .setMaxAllowedPerParent(31)
256                 .setDefaultRestrictions(restrictions));
257         builders.put(userTypeAosp2, new UserTypeDetails.Builder()
258                 .setName(userTypeAosp1)
259                 .setBaseType(FLAG_PROFILE)
260                 .setMaxAllowedPerParent(32)
261                 .setIconBadge(401)
262                 .setBadgeColors(402, 403, 404)
263                 .setDefaultRestrictions(restrictions));
264 
265         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
266         UserTypeFactory.customizeBuilders(builders, parser);
267 
268         // userTypeAosp1 should not be modified.
269         UserTypeDetails aospType = builders.get(userTypeAosp1).createUserTypeDetails();
270         assertEquals(31, aospType.getMaxAllowedPerParent());
271         assertEquals(Resources.ID_NULL, aospType.getIconBadge());
272         assertTrue(UserRestrictionsUtils.areEqual(restrictions, aospType.getDefaultRestrictions()));
273 
274         // userTypeAosp2 should be modified.
275         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
276         assertEquals(12, aospType.getMaxAllowedPerParent());
277         assertEquals(com.android.internal.R.drawable.ic_corp_icon_badge_case,
278                 aospType.getIconBadge());
279         assertEquals(Resources.ID_NULL, aospType.getBadgePlain()); // No resId for 'garbage'
280         assertEquals(com.android.internal.R.drawable.ic_corp_badge_no_background,
281                 aospType.getBadgeNoBackground());
282         assertEquals(com.android.internal.R.string.managed_profile_label_badge,
283                 aospType.getBadgeLabel(0));
284         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
285                 aospType.getBadgeLabel(1));
286         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
287                 aospType.getBadgeLabel(2));
288         assertEquals(com.android.internal.R.string.managed_profile_label_badge_2,
289                 aospType.getBadgeLabel(3));
290         assertEquals(com.android.internal.R.color.profile_badge_1,
291                 aospType.getBadgeColor(0));
292         assertEquals(com.android.internal.R.color.profile_badge_2,
293                 aospType.getBadgeColor(1));
294         assertEquals(com.android.internal.R.color.profile_badge_2,
295                 aospType.getBadgeColor(2));
296         assertEquals(com.android.internal.R.color.profile_badge_2,
297                 aospType.getBadgeColor(3));
298         assertTrue(UserRestrictionsUtils.areEqual(
299                 makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
300                 aospType.getDefaultRestrictions()));
301 
302         // userTypeOem1 should be created.
303         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
304         assertNotNull(customType);
305         assertEquals(14, customType.createUserTypeDetails().getMaxAllowedPerParent());
306     }
307 
308     /** Tests {@link UserTypeFactory#customizeBuilders} for customizing a FULL user. */
309     @Test
testUserTypeFactoryCustomize_full()310     public void testUserTypeFactoryCustomize_full() throws Exception {
311         final String userTypeFull = "android.test.1";
312 
313         // Mock "AOSP default".
314         final Bundle restrictions = makeRestrictionsBundle("no_config_vpn", "no_config_tethering");
315         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
316         builders.put(userTypeFull, new UserTypeDetails.Builder()
317                 .setName(userTypeFull)
318                 .setBaseType(FLAG_FULL)
319                 .setDefaultRestrictions(restrictions));
320 
321         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
322         UserTypeFactory.customizeBuilders(builders, parser);
323 
324         UserTypeDetails details = builders.get(userTypeFull).createUserTypeDetails();
325         assertEquals(UNLIMITED_NUMBER_OF_USERS, details.getMaxAllowedPerParent());
326         assertTrue(UserRestrictionsUtils.areEqual(
327                 makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
328                 details.getDefaultRestrictions()));
329         assertEquals(Resources.ID_NULL, details.getBadgeColor(0));
330     }
331 
332     /**
333      * Tests {@link UserTypeFactory#customizeBuilders} when custom user type deletes the
334      * badge-colors and restrictions.
335      */
336     @Test
testUserTypeFactoryCustomize_eraseArray()337     public void testUserTypeFactoryCustomize_eraseArray() throws Exception {
338         final String typeName = "android.test";
339 
340         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
341         builders.put(typeName, new UserTypeDetails.Builder()
342                 .setName(typeName)
343                 .setBaseType(FLAG_PROFILE)
344                 .setMaxAllowedPerParent(1)
345                 .setBadgeColors(501, 502)
346                 .setDefaultRestrictions(makeRestrictionsBundle("r1")));
347 
348         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray);
349         UserTypeFactory.customizeBuilders(builders, parser);
350 
351         UserTypeDetails typeDetails =  builders.get(typeName).createUserTypeDetails();
352         assertEquals(2, typeDetails.getMaxAllowedPerParent());
353         assertEquals(Resources.ID_NULL, typeDetails.getBadgeColor(0));
354         assertEquals(Resources.ID_NULL, typeDetails.getBadgeColor(1));
355         assertTrue(typeDetails.getDefaultRestrictions().isEmpty());
356     }
357 
358     /** Tests {@link UserTypeFactory#customizeBuilders} when custom user type has illegal name. */
359     @Test
testUserTypeFactoryCustomize_illegalOemName()360     public void testUserTypeFactoryCustomize_illegalOemName() throws Exception {
361         final String userTypeAosp = "android.aosp.legal";
362         final String userTypeOem = "android.oem.illegal.name"; // Custom-defined profile
363 
364         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
365         builders.put(userTypeAosp, new UserTypeDetails.Builder()
366                 .setName(userTypeAosp)
367                 .setBaseType(FLAG_PROFILE)
368                 .setMaxAllowedPerParent(21));
369 
370         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_illegalOemName);
371 
372         // parser is illegal because non-AOSP user types cannot be prefixed with "android.".
373         assertThrows(IllegalArgumentException.class,
374                 () -> UserTypeFactory.customizeBuilders(builders, parser));
375     }
376 
377     /**
378      * Tests {@link UserTypeFactory#customizeBuilders} when illegally customizing a non-profile as
379      * a profile.
380      */
381     @Test
testUserTypeFactoryCustomize_illegalUserBaseType()382     public void testUserTypeFactoryCustomize_illegalUserBaseType() throws Exception {
383         final String userTypeFull = "android.test";
384 
385         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
386         builders.put(userTypeFull, new UserTypeDetails.Builder()
387                 .setName(userTypeFull)
388                 .setBaseType(FLAG_FULL)
389                 .setMaxAllowedPerParent(21));
390 
391         XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_illegalUserBaseType);
392 
393         // parser is illegal because userTypeFull is FULL but the tag is for profile-type.
394         assertThrows(IllegalArgumentException.class,
395                 () -> UserTypeFactory.customizeBuilders(builders, parser));
396     }
397 
398     @Test
testUserTypeFactoryVersion_versionMissing()399     public void testUserTypeFactoryVersion_versionMissing() {
400         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray);
401         assertEquals(0, UserTypeFactory.getUserTypeVersion(parser));
402     }
403 
404     @Test
testUserTypeFactoryVersion_versionPresent()405     public void testUserTypeFactoryVersion_versionPresent() {
406         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
407         assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser));
408     }
409 
410     @Test
testUserTypeFactoryUpgrades_validUpgrades()411     public void testUserTypeFactoryUpgrades_validUpgrades() {
412         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
413         builders.put("name", getMinimalBuilder());
414 
415         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
416         List<UserTypeFactory.UserTypeUpgrade> upgrades = UserTypeFactory.parseUserUpgrades(builders,
417                 parser);
418 
419         assertFalse(upgrades.isEmpty());
420         UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0);
421         assertEquals("android.test.1", upgrade.getFromType());
422         assertEquals("android.test.2", upgrade.getToType());
423         assertEquals(1233, upgrade.getUpToVersion());
424     }
425 
426     @Test
testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade()427     public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() {
428         final String userTypeFull = "android.test.1";
429         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
430         builders.put(userTypeFull, new UserTypeDetails.Builder()
431                 .setName(userTypeFull)
432                 .setBaseType(FLAG_FULL));
433 
434         final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
435 
436         // parser is illegal because the "to" upgrade type is not a profile, but a full user
437         assertThrows(IllegalArgumentException.class,
438                 () -> UserTypeFactory.parseUserUpgrades(builders, parser));
439     }
440 
441     /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */
getMinimalBuilder()442     private UserTypeDetails.Builder getMinimalBuilder() {
443         return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL);
444     }
445 
446     /** Creates a Bundle of the given String restrictions, each set to true. */
makeRestrictionsBundle(String .... restrictions)447     public static Bundle makeRestrictionsBundle(String ... restrictions) {
448         final Bundle bundle = new Bundle();
449         for (String restriction : restrictions) {
450             bundle.putBoolean(restriction, true);
451         }
452         return bundle;
453     }
454 
455     /** Creates a Bundle of the given settings keys and puts true for the value. */
makeSettingsBundle(String .... settings)456     private static Bundle makeSettingsBundle(String ... settings) {
457         final Bundle bundle = new Bundle();
458         for (String setting : settings) {
459             bundle.putBoolean(setting, true);
460         }
461         return bundle;
462     }
463 }
464