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