1 /* 2 * Copyright (C) 2016 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 23 import android.app.PropertyInvalidatedCache; 24 import android.content.pm.UserInfo; 25 import android.os.Looper; 26 import android.platform.test.annotations.Postsubmit; 27 28 import androidx.test.InstrumentationRegistry; 29 import androidx.test.filters.MediumTest; 30 import androidx.test.runner.AndroidJUnit4; 31 32 import com.android.server.LocalServices; 33 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.util.LinkedHashSet; 39 40 /** 41 * <p>Run with:<pre> 42 * m FrameworksServicesTests && 43 * adb install \ 44 * -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && 45 * adb shell am instrument -e class com.android.server.pm.UserManagerServiceIdRecyclingTest \ 46 * -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner 47 * </pre> 48 */ 49 @Postsubmit 50 @RunWith(AndroidJUnit4.class) 51 @MediumTest 52 public class UserManagerServiceIdRecyclingTest { 53 private UserManagerService mUserManagerService; 54 55 @Before setup()56 public void setup() { 57 // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup 58 // TODO: Remove once UMS supports proper dependency injection 59 if (Looper.myLooper() == null) { 60 Looper.prepare(); 61 } 62 // Disable binder caches in this process. 63 PropertyInvalidatedCache.disableForTestMode(); 64 65 LocalServices.removeServiceForTest(UserManagerInternal.class); 66 mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext()); 67 } 68 69 @Test testUserCreateRecycleIdsAddAllThenRemove()70 public void testUserCreateRecycleIdsAddAllThenRemove() { 71 // Add max possible users 72 for (int i = UserManagerService.MIN_USER_ID; i < UserManagerService.MAX_USER_ID; i++) { 73 int userId = mUserManagerService.getNextAvailableId(); 74 assertEquals(i, userId); 75 mUserManagerService.putUserInfo(newUserInfo(userId)); 76 } 77 78 assertNoNextIdAvailable("All ids should be assigned"); 79 // Now remove RECENTLY_REMOVED_IDS_MAX_SIZE users in the middle 80 int startFrom = UserManagerService.MIN_USER_ID + 10000 /* arbitrary number */; 81 int lastId = startFrom + UserManagerService.MAX_RECENTLY_REMOVED_IDS_SIZE; 82 for (int i = startFrom; i < lastId; i++) { 83 removeUser(i); 84 assertNoNextIdAvailable("There is not enough recently removed users. " 85 + "Next id should not be available. Failed at u" + i); 86 } 87 88 // Now remove first user 89 removeUser(UserManagerService.MIN_USER_ID); 90 91 // Released UserIDs should be returned in the FIFO order 92 int nextId = mUserManagerService.getNextAvailableId(); 93 assertEquals(startFrom, nextId); 94 } 95 96 @Test testUserCreateRecycleIdsOverflow()97 public void testUserCreateRecycleIdsOverflow() { 98 LinkedHashSet<Integer> queue = new LinkedHashSet<>(); 99 // Make sure we can generate more than 2x ids without issues 100 for (int i = 0; i < UserManagerService.MAX_USER_ID * 2; i++) { 101 int userId = mUserManagerService.getNextAvailableId(); 102 assertTrue("Returned id should not be recent. Id=" + userId + ". Recents=" + queue, 103 queue.add(userId)); 104 if (queue.size() > UserManagerService.MAX_RECENTLY_REMOVED_IDS_SIZE) { 105 queue.remove(queue.iterator().next()); 106 } 107 mUserManagerService.putUserInfo(newUserInfo(userId)); 108 removeUser(userId); 109 } 110 } 111 removeUser(int userId)112 private void removeUser(int userId) { 113 mUserManagerService.removeUserInfo(userId); 114 mUserManagerService.addRemovingUserId(userId); 115 } 116 assertNoNextIdAvailable(String message)117 private void assertNoNextIdAvailable(String message) { 118 try { 119 mUserManagerService.getNextAvailableId(); 120 fail(message); 121 } catch (IllegalStateException e) { 122 //OK 123 } 124 } 125 newUserInfo(int userId)126 private static UserInfo newUserInfo(int userId) { 127 return new UserInfo(userId, "User " + userId, 0); 128 } 129 } 130