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_ADMIN;
20 import static android.content.pm.UserInfo.FLAG_DEMO;
21 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
22 import static android.content.pm.UserInfo.FLAG_FULL;
23 import static android.content.pm.UserInfo.FLAG_GUEST;
24 import static android.content.pm.UserInfo.FLAG_MAIN;
25 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
26 import static android.content.pm.UserInfo.FLAG_PRIMARY;
27 import static android.content.pm.UserInfo.FLAG_PROFILE;
28 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
29 import static android.content.pm.UserInfo.FLAG_SYSTEM;
30 import static android.os.UserManager.USER_TYPE_FULL_DEMO;
31 import static android.os.UserManager.USER_TYPE_FULL_GUEST;
32 import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
33 import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
34 import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
35 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
36 import static android.os.UserManager.USER_TYPE_PROFILE_COMMUNAL;
37 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
38 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
39 import static android.os.UserManager.USER_TYPE_PROFILE_TEST;
40 import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
41 
42 import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;
43 
44 import android.content.pm.UserInfo;
45 import android.content.pm.UserProperties;
46 import android.content.res.Resources;
47 import android.content.res.XmlResourceParser;
48 import android.os.Build;
49 import android.os.Bundle;
50 import android.os.UserManager;
51 import android.util.ArrayMap;
52 import android.util.Slog;
53 
54 import com.android.internal.R;
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.internal.util.XmlUtils;
57 
58 import org.xmlpull.v1.XmlPullParserException;
59 
60 import java.io.IOException;
61 import java.util.ArrayList;
62 import java.util.List;
63 import java.util.function.Consumer;
64 
65 /**
66  * Class for creating all {@link UserTypeDetails} on the device.
67  *
68  * This class is responsible both for defining the AOSP use types, as well as reading in customized
69  * user types from {@link com.android.internal.R.xml#config_user_types}.
70  *
71  * Tests are located in {@link UserManagerServiceUserTypeTest}.
72  * @hide
73  */
74 public final class UserTypeFactory {
75 
76     private static final String LOG_TAG = "UserTypeFactory";
77 
78     /** This is a utility class, so no instantiable constructor. */
UserTypeFactory()79     private UserTypeFactory() {}
80 
81     /**
82      * Obtains the user types (built-in and customized) for this device.
83      *
84      * @return mapping from the name of each user type to its {@link UserTypeDetails} object
85      */
getUserTypes()86     public static ArrayMap<String, UserTypeDetails> getUserTypes() {
87         final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders();
88 
89         try (XmlResourceParser parser =
90                      Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
91             customizeBuilders(builders, parser);
92         }
93 
94         final ArrayMap<String, UserTypeDetails> types = new ArrayMap<>(builders.size());
95         for (int i = 0; i < builders.size(); i++) {
96             types.put(builders.keyAt(i), builders.valueAt(i).createUserTypeDetails());
97         }
98         return types;
99     }
100 
getDefaultBuilders()101     private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() {
102         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
103 
104         builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged());
105         builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem());
106         builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary());
107         builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest());
108         builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo());
109         builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted());
110         builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
111         builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone());
112         builders.put(USER_TYPE_PROFILE_COMMUNAL, getDefaultTypeProfileCommunal());
113         builders.put(USER_TYPE_PROFILE_PRIVATE, getDefaultTypeProfilePrivate());
114         if (Build.IS_DEBUGGABLE) {
115             builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest());
116         }
117 
118         return builders;
119     }
120 
121     /**
122      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_CLONE}
123      * configuration.
124      */
125     // TODO(b/182396009): Add default restrictions, if needed for clone user type.
getDefaultTypeProfileClone()126     private static UserTypeDetails.Builder getDefaultTypeProfileClone() {
127         return new UserTypeDetails.Builder()
128                 .setName(USER_TYPE_PROFILE_CLONE)
129                 .setBaseType(FLAG_PROFILE)
130                 .setMaxAllowedPerParent(1)
131                 .setLabels(R.string.profile_label_clone)
132                 .setIconBadge(com.android.internal.R.drawable.ic_clone_icon_badge)
133                 .setBadgePlain(com.android.internal.R.drawable.ic_clone_badge)
134                 // Clone doesn't use BadgeNoBackground, so just set to BadgePlain as a placeholder.
135                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_clone_badge)
136                 .setStatusBarIcon(Resources.ID_NULL)
137                 .setBadgeLabels(
138                         com.android.internal.R.string.clone_profile_label_badge)
139                 .setBadgeColors(
140                         com.android.internal.R.color.system_neutral2_800)
141                 .setDarkThemeBadgeColors(
142                         com.android.internal.R.color.system_neutral2_900)
143                 .setAccessibilityString(com.android.internal
144                         .R.string.accessibility_label_clone_profile)
145                 .setDefaultRestrictions(null)
146                 .setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
147                 .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
148                 .setDefaultUserProperties(new UserProperties.Builder()
149                         .setStartWithParent(true)
150                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
151                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_WITH_PARENT)
152                         .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
153                         .setUseParentsContacts(true)
154                         .setUpdateCrossProfileIntentFiltersOnOTA(true)
155                         .setCrossProfileIntentFilterAccessControl(
156                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
157                         .setCrossProfileIntentResolutionStrategy(UserProperties
158                                 .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
159                         .setShowInQuietMode(
160                                 UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
161                         .setShowInSharingSurfaces(
162                                 UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
163                         .setMediaSharedWithParent(true)
164                         .setCredentialShareableWithParent(true)
165                         .setDeleteAppWithParent(true)
166                         .setCrossProfileContentSharingStrategy(UserProperties
167                                 .CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
168     }
169 
170     /**
171      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED}
172      * configuration.
173      */
getDefaultTypeProfileManaged()174     private static UserTypeDetails.Builder getDefaultTypeProfileManaged() {
175         return new UserTypeDetails.Builder()
176                 .setName(USER_TYPE_PROFILE_MANAGED)
177                 .setBaseType(FLAG_PROFILE)
178                 .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
179                 .setMaxAllowedPerParent(1)
180                 .setLabels(
181                         R.string.profile_label_work,
182                         R.string.profile_label_work_2,
183                         R.string.profile_label_work_3)
184                 .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case)
185                 .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case)
186                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background)
187                 .setStatusBarIcon(com.android.internal.R.drawable.stat_sys_managed_profile_status)
188                 .setBadgeLabels(
189                         com.android.internal.R.string.managed_profile_label_badge,
190                         com.android.internal.R.string.managed_profile_label_badge_2,
191                         com.android.internal.R.string.managed_profile_label_badge_3)
192                 .setBadgeColors(
193                         com.android.internal.R.color.profile_badge_1,
194                         com.android.internal.R.color.profile_badge_2,
195                         com.android.internal.R.color.profile_badge_3)
196                 .setDarkThemeBadgeColors(
197                         com.android.internal.R.color.profile_badge_1_dark,
198                         com.android.internal.R.color.profile_badge_2_dark,
199                         com.android.internal.R.color.profile_badge_3_dark)
200                 .setAccessibilityString(com.android.internal
201                         .R.string.accessibility_label_managed_profile)
202                 .setDefaultRestrictions(getDefaultProfileRestrictions())
203                 .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
204                 .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter())
205                 .setDefaultUserProperties(new UserProperties.Builder()
206                         .setStartWithParent(true)
207                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
208                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
209                         .setShowInQuietMode(
210                                 UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
211                         .setShowInSharingSurfaces(
212                                 UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
213                         .setAuthAlwaysRequiredToDisableQuietMode(false)
214                         .setCredentialShareableWithParent(true));
215     }
216 
217     /**
218      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_TEST}
219      * configuration (for userdebug builds). For now it just uses managed profile badges.
220      */
getDefaultTypeProfileTest()221     private static UserTypeDetails.Builder getDefaultTypeProfileTest() {
222         final Bundle restrictions = getDefaultProfileRestrictions();
223         restrictions.putBoolean(UserManager.DISALLOW_FUN, true);
224 
225         return new UserTypeDetails.Builder()
226                 .setName(USER_TYPE_PROFILE_TEST)
227                 .setBaseType(FLAG_PROFILE)
228                 .setMaxAllowedPerParent(2)
229                 .setLabels(
230                         R.string.profile_label_test,
231                         R.string.profile_label_test,
232                         R.string.profile_label_test)
233                 .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
234                 .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
235                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
236                 .setStatusBarIcon(com.android.internal.R.drawable.ic_test_badge_experiment)
237                 .setBadgeLabels(
238                         com.android.internal.R.string.managed_profile_label_badge,
239                         com.android.internal.R.string.managed_profile_label_badge_2,
240                         com.android.internal.R.string.managed_profile_label_badge_3)
241                 .setBadgeColors(
242                         com.android.internal.R.color.profile_badge_1,
243                         com.android.internal.R.color.profile_badge_2,
244                         com.android.internal.R.color.profile_badge_3)
245                 .setDarkThemeBadgeColors(
246                         com.android.internal.R.color.profile_badge_1_dark,
247                         com.android.internal.R.color.profile_badge_2_dark,
248                         com.android.internal.R.color.profile_badge_3_dark)
249                 .setDefaultRestrictions(restrictions)
250                 .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings());
251     }
252 
253     /**
254      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_COMMUNAL}
255      * configuration. For now it just uses managed profile badges.
256      */
getDefaultTypeProfileCommunal()257     private static UserTypeDetails.Builder getDefaultTypeProfileCommunal() {
258         return new UserTypeDetails.Builder()
259                 .setName(USER_TYPE_PROFILE_COMMUNAL)
260                 .setBaseType(FLAG_PROFILE)
261                 .setMaxAllowed(1)
262                 .setEnabled(UserManager.isCommunalProfileEnabled() ? 1 : 0)
263                 .setLabels(R.string.profile_label_communal)
264                 .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
265                 .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
266                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
267                 .setStatusBarIcon(com.android.internal.R.drawable.ic_test_badge_experiment)
268                 .setBadgeLabels(
269                         com.android.internal.R.string.managed_profile_label_badge,
270                         com.android.internal.R.string.managed_profile_label_badge_2,
271                         com.android.internal.R.string.managed_profile_label_badge_3)
272                 .setBadgeColors(
273                         com.android.internal.R.color.profile_badge_1,
274                         com.android.internal.R.color.profile_badge_2,
275                         com.android.internal.R.color.profile_badge_3)
276                 .setDarkThemeBadgeColors(
277                         com.android.internal.R.color.profile_badge_1_dark,
278                         com.android.internal.R.color.profile_badge_2_dark,
279                         com.android.internal.R.color.profile_badge_3_dark)
280                 .setDefaultRestrictions(getDefaultProfileRestrictions())
281                 .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
282                 .setDefaultUserProperties(new UserProperties.Builder()
283                         .setStartWithParent(false)
284                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
285                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
286                         .setCredentialShareableWithParent(false)
287                         .setAlwaysVisible(true));
288     }
289 
290     /**
291      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_PRIVATE}
292      * configuration.
293      */
getDefaultTypeProfilePrivate()294     private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() {
295         return new UserTypeDetails.Builder()
296                 .setName(USER_TYPE_PROFILE_PRIVATE)
297                 .setBaseType(FLAG_PROFILE)
298                 .setMaxAllowedPerParent(1)
299                 .setEnabled(UserManager.isPrivateProfileEnabled() ? 1 : 0)
300                 .setLabels(R.string.profile_label_private)
301                 .setIconBadge(com.android.internal.R.drawable.ic_private_profile_icon_badge)
302                 .setBadgePlain(com.android.internal.R.drawable.ic_private_profile_badge)
303                 // Private Profile doesn't use BadgeNoBackground, so just set to BadgePlain
304                 // as a placeholder.
305                 .setBadgeNoBackground(com.android.internal.R.drawable.ic_private_profile_badge)
306                 .setStatusBarIcon(com.android.internal.R.drawable.stat_sys_private_profile_status)
307                 .setBadgeLabels(
308                         com.android.internal.R.string.private_profile_label_badge)
309                 .setBadgeColors(
310                         R.color.black)
311                 .setDarkThemeBadgeColors(
312                         R.color.white)
313                 .setAccessibilityString(com.android.internal
314                         .R.string.accessibility_label_private_profile)
315                 .setDefaultRestrictions(getDefaultPrivateProfileRestrictions())
316                 .setDefaultCrossProfileIntentFilters(getDefaultPrivateCrossProfileIntentFilter())
317                 .setDefaultUserProperties(new UserProperties.Builder()
318                         .setStartWithParent(true)
319                         .setCredentialShareableWithParent(true)
320                         .setAuthAlwaysRequiredToDisableQuietMode(true)
321                         .setAllowStoppingUserWithDelayedLocking(true)
322                         .setMediaSharedWithParent(false)
323                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
324                         .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
325                         .setShowInQuietMode(
326                                 UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
327                         .setShowInSharingSurfaces(
328                                 UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
329                         .setCrossProfileIntentFilterAccessControl(
330                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
331                         .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
332                         .setCrossProfileContentSharingStrategy(
333                                 UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
334                         .setProfileApiVisibility(
335                                 UserProperties.PROFILE_API_VISIBILITY_HIDDEN)
336                         .setItemsRestrictedOnHomeScreen(true)
337                         .setUpdateCrossProfileIntentFiltersOnOTA(true));
338     }
339 
340     /**
341      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
342      * configuration.
343      */
getDefaultTypeFullSecondary()344     private static UserTypeDetails.Builder getDefaultTypeFullSecondary() {
345         return new UserTypeDetails.Builder()
346                 .setName(USER_TYPE_FULL_SECONDARY)
347                 .setBaseType(FLAG_FULL)
348                 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
349                 .setDefaultRestrictions(getDefaultSecondaryUserRestrictions());
350     }
351 
352     /**
353      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_GUEST} configuration.
354      */
getDefaultTypeFullGuest()355     private static UserTypeDetails.Builder getDefaultTypeFullGuest() {
356         final boolean ephemeralGuests = Resources.getSystem()
357                 .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
358         final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0);
359 
360         return new UserTypeDetails.Builder()
361                 .setName(USER_TYPE_FULL_GUEST)
362                 .setBaseType(FLAG_FULL)
363                 .setDefaultUserInfoPropertyFlags(flags)
364                 .setMaxAllowed(1)
365                 .setDefaultRestrictions(getDefaultGuestUserRestrictions());
366     }
367 
368     /**
369      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_DEMO} configuration.
370      */
getDefaultTypeFullDemo()371     private static UserTypeDetails.Builder getDefaultTypeFullDemo() {
372         return new UserTypeDetails.Builder()
373                 .setName(USER_TYPE_FULL_DEMO)
374                 .setBaseType(FLAG_FULL)
375                 .setDefaultUserInfoPropertyFlags(FLAG_DEMO)
376                 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
377                 .setDefaultRestrictions(null);
378     }
379 
380     /**
381      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_RESTRICTED}
382      * configuration.
383      */
getDefaultTypeFullRestricted()384     private static UserTypeDetails.Builder getDefaultTypeFullRestricted() {
385         return new UserTypeDetails.Builder()
386                 .setName(USER_TYPE_FULL_RESTRICTED)
387                 .setBaseType(FLAG_FULL)
388                 .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED)
389                 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
390                 // NB: UserManagerService.createRestrictedProfile() applies hardcoded restrictions.
391                 .setDefaultRestrictions(null);
392     }
393 
394     /**
395      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SYSTEM} configuration.
396      */
getDefaultTypeFullSystem()397     private static UserTypeDetails.Builder getDefaultTypeFullSystem() {
398         return new UserTypeDetails.Builder()
399                 .setName(USER_TYPE_FULL_SYSTEM)
400                 .setBaseType(FLAG_SYSTEM | FLAG_FULL)
401                 .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN | FLAG_MAIN)
402                 .setMaxAllowed(1);
403     }
404 
405     /**
406      * Returns the Builder for the default {@link UserManager#USER_TYPE_SYSTEM_HEADLESS}
407      * configuration.
408      */
getDefaultTypeSystemHeadless()409     private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() {
410         return new UserTypeDetails.Builder()
411                 .setName(USER_TYPE_SYSTEM_HEADLESS)
412                 .setBaseType(FLAG_SYSTEM)
413                 .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN)
414                 .setMaxAllowed(1);
415     }
416 
getDefaultSecondaryUserRestrictions()417     private static Bundle getDefaultSecondaryUserRestrictions() {
418         final Bundle restrictions = new Bundle();
419         restrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
420         restrictions.putBoolean(UserManager.DISALLOW_SMS, true);
421         return restrictions;
422     }
423 
getDefaultGuestUserRestrictions()424     private static Bundle getDefaultGuestUserRestrictions() {
425         // Guest inherits the secondary user's restrictions, plus has some extra ones.
426         final Bundle restrictions = getDefaultSecondaryUserRestrictions();
427         restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, true);
428         restrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
429         restrictions.putBoolean(UserManager.DISALLOW_CONFIG_CREDENTIALS, true);
430         return restrictions;
431     }
432 
getDefaultProfileRestrictions()433     private static Bundle getDefaultProfileRestrictions() {
434         final Bundle restrictions = new Bundle();
435         restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
436         return restrictions;
437     }
438 
439     @VisibleForTesting
getDefaultPrivateProfileRestrictions()440     static Bundle getDefaultPrivateProfileRestrictions() {
441         final Bundle restrictions = getDefaultProfileRestrictions();
442         restrictions.putBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING, true);
443         return restrictions;
444     }
445 
getDefaultManagedProfileSecureSettings()446     private static Bundle getDefaultManagedProfileSecureSettings() {
447         // Only add String values to the bundle, settings are written as Strings eventually
448         final Bundle settings = new Bundle();
449         settings.putString(
450                 android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
451         settings.putString(
452                 android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
453         return settings;
454     }
455 
456     private static List<DefaultCrossProfileIntentFilter>
getDefaultManagedCrossProfileIntentFilter()457             getDefaultManagedCrossProfileIntentFilter() {
458         return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
459     }
460 
getDefaultCloneCrossProfileIntentFilter()461     private static List<DefaultCrossProfileIntentFilter> getDefaultCloneCrossProfileIntentFilter() {
462         return DefaultCrossProfileIntentFiltersUtils.getDefaultCloneProfileFilters();
463     }
464 
getDefaultPrivateCrossProfileIntentFilter()465     private static List<DefaultCrossProfileIntentFilter> getDefaultPrivateCrossProfileIntentFilter()
466     {
467         return DefaultCrossProfileIntentFiltersUtils.getDefaultPrivateProfileFilters();
468     }
469 
470     /** Gets a default bundle, keyed by Settings.Secure String names, for non-managed profiles. */
getDefaultNonManagedProfileSecureSettings()471     private static Bundle getDefaultNonManagedProfileSecureSettings() {
472         final Bundle settings = new Bundle();
473         // Non-managed profiles go through neither SetupWizard nor DPC flows, so we automatically
474         // mark them as setup.
475         settings.putString(android.provider.Settings.Secure.USER_SETUP_COMPLETE, "1");
476         return settings;
477     }
478 
479     /**
480      * Reads the given xml parser to obtain device user-type customization, and updates the given
481      * map of {@link UserTypeDetails.Builder}s accordingly.
482      * <p>
483      * The xml file can specify the attributes according to the set... methods below.
484      */
485     // TODO(b/176973369): Add parsing logic to support custom settings/filters
486     //  in config_user_types.xml
487     @VisibleForTesting
customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser)488     static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
489             XmlResourceParser parser) {
490         try {
491             XmlUtils.beginDocument(parser, "user-types");
492             for (XmlUtils.nextElement(parser);
493                     parser.getEventType() != XmlResourceParser.END_DOCUMENT;
494                     XmlUtils.nextElement(parser)) {
495                 final boolean isProfile;
496                 final String elementName = parser.getName();
497                 if ("profile-type".equals(elementName)) {
498                     isProfile = true;
499                 } else if ("full-type".equals(elementName)) {
500                     isProfile = false;
501                 } else if ("change-user-type".equals(elementName)) {
502                     // parsed in parseUserUpgrades
503                     XmlUtils.skipCurrentTag(parser);
504                     continue;
505                 } else {
506                     Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in "
507                                 + parser.getPositionDescription());
508                     XmlUtils.skipCurrentTag(parser);
509                     continue;
510                 }
511 
512                 String typeName = parser.getAttributeValue(null, "name");
513                 if (typeName == null || typeName.equals("")) {
514                     Slog.w(LOG_TAG, "Skipping user type with no name in "
515                             + parser.getPositionDescription());
516                     XmlUtils.skipCurrentTag(parser);
517                     continue;
518                 }
519                 typeName = typeName.intern();
520 
521                 UserTypeDetails.Builder builder;
522                 if (typeName.startsWith("android.")) {
523                     // typeName refers to a AOSP-defined type which we are modifying.
524                     Slog.i(LOG_TAG, "Customizing user type " + typeName);
525                     builder = builders.get(typeName);
526                     if (builder == null) {
527                         throw new IllegalArgumentException("Illegal custom user type name "
528                                 + typeName + ": Non-AOSP user types cannot start with 'android.'");
529                     }
530                     final boolean isValid =
531                             (isProfile && builder.getBaseType() == UserInfo.FLAG_PROFILE)
532                             || (!isProfile && builder.getBaseType() == UserInfo.FLAG_FULL);
533                     if (!isValid) {
534                         throw new IllegalArgumentException("Wrong base type to customize user type "
535                                 + "(" + typeName + "), which is type "
536                                 + UserInfo.flagsToString(builder.getBaseType()));
537                     }
538                 } else if (isProfile) {
539                     // typeName refers to a new OEM-defined profile type which we are defining.
540                     Slog.i(LOG_TAG, "Creating custom user type " + typeName);
541                     builder = new UserTypeDetails.Builder();
542                     builder.setName(typeName);
543                     builder.setBaseType(FLAG_PROFILE);
544                     builders.put(typeName, builder);
545                 } else {
546                     throw new IllegalArgumentException("Creation of non-profile user type "
547                             + "(" + typeName + ") is not currently supported.");
548                 }
549 
550                 // Process the attributes (other than name).
551                 if (isProfile) {
552                     setIntAttribute(parser, "max-allowed-per-parent",
553                             builder::setMaxAllowedPerParent);
554                     setResAttribute(parser, "icon-badge", builder::setIconBadge);
555                     setResAttribute(parser, "badge-plain", builder::setBadgePlain);
556                     setResAttribute(parser, "badge-no-background", builder::setBadgeNoBackground);
557                     setResAttribute(parser, "status-bar-icon", builder::setStatusBarIcon);
558                 }
559 
560                 setIntAttribute(parser, "enabled", builder::setEnabled);
561                 setIntAttribute(parser, "max-allowed", builder::setMaxAllowed);
562 
563                 // Process child elements.
564                 final int depth = parser.getDepth();
565                 while (XmlUtils.nextElementWithin(parser, depth)) {
566                     final String childName = parser.getName();
567                     if ("default-restrictions".equals(childName)) {
568                         final Bundle restrictions = UserRestrictionsUtils
569                                 .readRestrictions(XmlUtils.makeTyped(parser));
570                         builder.setDefaultRestrictions(restrictions);
571                     } else if (isProfile && "badge-labels".equals(childName)) {
572                         setResAttributeArray(parser, builder::setBadgeLabels);
573                     } else if (isProfile && "badge-colors".equals(childName)) {
574                         setResAttributeArray(parser, builder::setBadgeColors);
575                     } else if (isProfile && "badge-colors-dark".equals(childName)) {
576                         setResAttributeArray(parser, builder::setDarkThemeBadgeColors);
577                     } else if ("user-properties".equals(childName)) {
578                         builder.getDefaultUserProperties()
579                                 .updateFromXml(XmlUtils.makeTyped(parser));
580                     } else {
581                         Slog.w(LOG_TAG, "Unrecognized tag " + childName + " in "
582                                 + parser.getPositionDescription());
583                     }
584                 }
585             }
586         } catch (XmlPullParserException | IOException e) {
587             Slog.w(LOG_TAG, "Cannot read user type configuration file.", e);
588         }
589     }
590 
591     /**
592      * If the given attribute exists, gets the int stored in it and performs the given fcn using it.
593      * The stored value must be an int or NumberFormatException will be thrown.
594      *
595      * @param parser xml parser from which to read the attribute
596      * @param attributeName name of the attribute
597      * @param fcn one-int-argument function,
598      *            like {@link UserTypeDetails.Builder#setMaxAllowedPerParent(int)}
599      */
setIntAttribute(XmlResourceParser parser, String attributeName, Consumer<Integer> fcn)600     private static void setIntAttribute(XmlResourceParser parser, String attributeName,
601             Consumer<Integer> fcn) {
602         final String intValue = parser.getAttributeValue(null, attributeName);
603         if (intValue == null) {
604             return;
605         }
606         try {
607             fcn.accept(Integer.parseInt(intValue));
608         } catch (NumberFormatException e) {
609             Slog.e(LOG_TAG, "Cannot parse value of '" + intValue + "' for " + attributeName
610                     + " in " + parser.getPositionDescription(), e);
611             throw e;
612         }
613     }
614 
615     /**
616      * If the given attribute exists, gets the resId stored in it (or 0 if it is not a valid resId)
617      * and performs the given fcn using it.
618      *
619      * @param parser xml parser from which to read the attribute
620      * @param attributeName name of the attribute
621      * @param fcn one-argument function, like {@link UserTypeDetails.Builder#setIconBadge(int)}
622      */
setResAttribute(XmlResourceParser parser, String attributeName, Consumer<Integer> fcn)623     private static void setResAttribute(XmlResourceParser parser, String attributeName,
624             Consumer<Integer> fcn) {
625         if (parser.getAttributeValue(null, attributeName) == null) {
626             // Attribute is not present, i.e. use the default value.
627             return;
628         }
629         final int resId = parser.getAttributeResourceValue(null, attributeName, Resources.ID_NULL);
630         fcn.accept(resId);
631     }
632 
633     /**
634      * Gets the resIds stored in "item" elements (in their "res" attribute) at the current depth.
635      * Then performs the given fcn using the int[] array of these resIds.
636      * <p>
637      * Each xml element is expected to be of the form {@code <item res="someResValue" />}.
638      *
639      * @param parser xml parser from which to read the elements and their attributes
640      * @param fcn function, like {@link UserTypeDetails.Builder#setBadgeColors(int...)}
641      */
setResAttributeArray(XmlResourceParser parser, Consumer<int[]> fcn)642     private static void setResAttributeArray(XmlResourceParser parser, Consumer<int[]> fcn)
643             throws IOException, XmlPullParserException {
644 
645         ArrayList<Integer> resList = new ArrayList<>();
646         final int depth = parser.getDepth();
647         while (XmlUtils.nextElementWithin(parser, depth)) {
648             final String elementName = parser.getName();
649             if (!"item".equals(elementName)) {
650                 Slog.w(LOG_TAG, "Skipping unknown child element " + elementName + " in "
651                         + parser.getPositionDescription());
652                 XmlUtils.skipCurrentTag(parser);
653                 continue;
654             }
655             final int resId = parser.getAttributeResourceValue(null, "res", -1);
656             if (resId == -1) {
657                 continue;
658             }
659             resList.add(resId);
660         }
661 
662         int[] result = new int[resList.size()];
663         for (int i = 0; i < resList.size(); i++) {
664             result[i] = resList.get(i);
665         }
666         fcn.accept(result);
667     }
668 
669     /**
670      * Returns the user type version of the config XML file.
671      * @return user type version defined in XML file, 0 if none.
672      */
getUserTypeVersion()673     public static int getUserTypeVersion() {
674         try (XmlResourceParser parser =
675                      Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
676             return getUserTypeVersion(parser);
677         }
678     }
679 
680     @VisibleForTesting
getUserTypeVersion(XmlResourceParser parser)681     static int getUserTypeVersion(XmlResourceParser parser) {
682         int version = 0;
683 
684         try {
685             XmlUtils.beginDocument(parser, "user-types");
686             String versionValue = parser.getAttributeValue(null, "version");
687             if (versionValue != null) {
688                 try {
689                     version = Integer.parseInt(versionValue);
690                 } catch (NumberFormatException e) {
691                     Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in "
692                             + parser.getPositionDescription(), e);
693                     throw e;
694                 }
695             }
696         } catch (XmlPullParserException | IOException e) {
697             Slog.w(LOG_TAG, "Cannot read user type configuration file.", e);
698         }
699 
700         return version;
701     }
702 
703     /**
704      * Obtains the user type upgrades for this device.
705      * @return The list of user type upgrades.
706      */
getUserTypeUpgrades()707     public static List<UserTypeUpgrade> getUserTypeUpgrades() {
708         final List<UserTypeUpgrade> userUpgrades;
709         try (XmlResourceParser parser =
710                      Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
711             userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser);
712         }
713         return userUpgrades;
714     }
715 
716     @VisibleForTesting
parseUserUpgrades( ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser)717     static List<UserTypeUpgrade> parseUserUpgrades(
718             ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) {
719         final List<UserTypeUpgrade> userUpgrades = new ArrayList<>();
720 
721         try {
722             XmlUtils.beginDocument(parser, "user-types");
723             for (XmlUtils.nextElement(parser);
724                     parser.getEventType() != XmlResourceParser.END_DOCUMENT;
725                     XmlUtils.nextElement(parser)) {
726                 final String elementName = parser.getName();
727                 if ("change-user-type".equals(elementName)) {
728                     final String fromType = parser.getAttributeValue(null, "from");
729                     final String toType = parser.getAttributeValue(null, "to");
730                     // Check that the base type doesn't change.
731                     // Currently, only the base type of PROFILE is supported.
732                     validateUserTypeIsProfile(fromType, builders);
733                     validateUserTypeIsProfile(toType, builders);
734 
735                     final int maxVersionToConvert;
736                     try {
737                         maxVersionToConvert = Integer.parseInt(
738                                 parser.getAttributeValue(null, "whenVersionLeq"));
739                     } catch (NumberFormatException e) {
740                         Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in "
741                                 + parser.getPositionDescription(), e);
742                         throw e;
743                     }
744 
745                     UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType,
746                             maxVersionToConvert);
747                     userUpgrades.add(userTypeUpgrade);
748                     continue;
749                 } else {
750                     XmlUtils.skipCurrentTag(parser);
751                     continue;
752                 }
753             }
754         } catch (XmlPullParserException | IOException e) {
755             Slog.w(LOG_TAG, "Cannot read user type configuration file.", e);
756         }
757 
758         return userUpgrades;
759     }
760 
validateUserTypeIsProfile(String userType, ArrayMap<String, UserTypeDetails.Builder> builders)761     private static void validateUserTypeIsProfile(String userType,
762             ArrayMap<String, UserTypeDetails.Builder> builders) {
763         UserTypeDetails.Builder builder = builders.get(userType);
764         if (builder != null && builder.getBaseType() != FLAG_PROFILE) {
765             throw new IllegalArgumentException("Illegal upgrade of user type " + userType
766                     + " : Can only upgrade profiles user types");
767         }
768     }
769 
770     /**
771      * Contains details required for an upgrade operation for {@link UserTypeDetails};
772      */
773     public static class UserTypeUpgrade {
774         private final String mFromType;
775         private final String mToType;
776         private final int mUpToVersion;
777 
UserTypeUpgrade(String fromType, String toType, int upToVersion)778         public UserTypeUpgrade(String fromType, String toType, int upToVersion) {
779             mFromType = fromType;
780             mToType = toType;
781             mUpToVersion = upToVersion;
782         }
783 
getFromType()784         public String getFromType() {
785             return mFromType;
786         }
787 
getToType()788         public String getToType() {
789             return mToType;
790         }
791 
getUpToVersion()792         public int getUpToVersion() {
793             return mUpToVersion;
794         }
795     }
796 }
797