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