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