blob: 94f3a88cb859669ddac9680ba60845c32db3d54b [file] [log] [blame]
Soonil Nagarkar9ec01852020-01-10 13:42:38 -08001/*
2 * Copyright (C) 2020 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
17package com.android.server.location;
18
19import android.annotation.Nullable;
20import android.annotation.UserIdInt;
21import android.app.ActivityManager;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.UserInfo;
Soonil Nagarkar7ebcaef2020-01-15 14:21:58 -080027import android.os.Binder;
Soonil Nagarkar9ec01852020-01-10 13:42:38 -080028import android.os.Build;
29import android.os.UserHandle;
30import android.os.UserManager;
31
32import com.android.internal.annotations.GuardedBy;
33import com.android.internal.util.ArrayUtils;
34import com.android.internal.util.Preconditions;
35import com.android.server.FgThread;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.util.Arrays;
40import java.util.concurrent.CopyOnWriteArrayList;
41
42/**
43 * Provides accessors and listeners for all user info.
44 */
Soonil Nagarkarb6375a42020-01-29 15:23:06 -080045public class UserInfoHelper {
Soonil Nagarkar9ec01852020-01-10 13:42:38 -080046
47 /**
48 * Listener for current user changes.
49 */
50 public interface UserChangedListener {
51 /**
52 * Called when the current user changes.
53 */
54 void onUserChanged(@UserIdInt int oldUserId, @UserIdInt int newUserId);
55 }
56
57 private final Context mContext;
58 private final CopyOnWriteArrayList<UserChangedListener> mListeners;
59
60 @GuardedBy("this")
Soonil Nagarkarb6375a42020-01-29 15:23:06 -080061 @Nullable private UserManager mUserManager;
62
63 @UserIdInt private volatile int mCurrentUserId;
Soonil Nagarkar9ec01852020-01-10 13:42:38 -080064
65 @GuardedBy("this")
Soonil Nagarkarb6375a42020-01-29 15:23:06 -080066 @UserIdInt private int mCachedParentUserId;
Soonil Nagarkar9ec01852020-01-10 13:42:38 -080067 @GuardedBy("this")
68 private int[] mCachedProfileUserIds;
69
Soonil Nagarkarb6375a42020-01-29 15:23:06 -080070 public UserInfoHelper(Context context) {
Soonil Nagarkar9ec01852020-01-10 13:42:38 -080071 mContext = context;
72 mListeners = new CopyOnWriteArrayList<>();
73
74 mCurrentUserId = UserHandle.USER_NULL;
75 mCachedParentUserId = UserHandle.USER_NULL;
76 mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
77 }
78
79 /** Called when system is ready. */
80 public synchronized void onSystemReady() {
81 if (mUserManager != null) {
82 return;
83 }
84
85 mUserManager = mContext.getSystemService(UserManager.class);
86
87 IntentFilter intentFilter = new IntentFilter();
88 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
89 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
90 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
91
92 mContext.registerReceiverAsUser(new BroadcastReceiver() {
93 @Override
94 public void onReceive(Context context, Intent intent) {
95 String action = intent.getAction();
96 if (action == null) {
97 return;
98 }
99 switch (action) {
100 case Intent.ACTION_USER_SWITCHED:
101 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
102 UserHandle.USER_NULL);
103 if (userId != UserHandle.USER_NULL) {
104 onUserChanged(userId);
105 }
106 break;
107 case Intent.ACTION_MANAGED_PROFILE_ADDED:
108 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
109 onUserProfilesChanged();
110 break;
111 }
112 }
113 }, UserHandle.ALL, intentFilter, null, FgThread.getHandler());
114
115 mCurrentUserId = ActivityManager.getCurrentUser();
116 }
117
118 /**
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800119 * Adds a listener for user changed events. Callbacks occur on an unspecified thread.
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800120 */
121 public void addListener(UserChangedListener listener) {
122 mListeners.add(listener);
123 }
124
125 /**
126 * Removes a listener for user changed events.
127 */
128 public void removeListener(UserChangedListener listener) {
129 mListeners.remove(listener);
130 }
131
132 private void onUserChanged(@UserIdInt int newUserId) {
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800133 if (newUserId == mCurrentUserId) {
134 return;
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800135 }
136
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800137 int oldUserId = mCurrentUserId;
138 mCurrentUserId = newUserId;
139
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800140 for (UserChangedListener listener : mListeners) {
141 listener.onUserChanged(oldUserId, newUserId);
142 }
143 }
144
145 private synchronized void onUserProfilesChanged() {
146 // this intent is only sent to the current user
147 if (mCachedParentUserId == mCurrentUserId) {
148 mCachedParentUserId = UserHandle.USER_NULL;
Soonil Nagarkar7ebcaef2020-01-15 14:21:58 -0800149 mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800150 }
151 }
152
153 /**
154 * Returns the user id of the current user.
155 */
156 @UserIdInt
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800157 public int getCurrentUserId() {
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800158 return mCurrentUserId;
159 }
160
161 /**
162 * Returns true if the given user id is either the current user or a profile of the current
163 * user.
164 */
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800165 public boolean isCurrentUserOrProfile(@UserIdInt int userId) {
166 int currentUserId = mCurrentUserId;
167 return userId == currentUserId || ArrayUtils.contains(
168 getProfileUserIdsForParentUser(currentUserId), userId);
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800169 }
170
171 /**
172 * Returns the parent user id of the given user id, or the user id itself if the user id either
173 * is a parent or has no profiles.
174 */
175 @UserIdInt
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800176 public int getParentUserId(@UserIdInt int userId) {
177 synchronized (this) {
178 if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds,
179 userId)) {
180 return mCachedParentUserId;
Soonil Nagarkar7ebcaef2020-01-15 14:21:58 -0800181 }
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800182
183 Preconditions.checkState(mUserManager != null);
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800184 }
185
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800186 int parentUserId;
187
188 long identity = Binder.clearCallingIdentity();
189 try {
190 UserInfo userInfo = mUserManager.getProfileParent(userId);
191 parentUserId = userInfo != null ? userInfo.id : userId;
192 } finally {
193 Binder.restoreCallingIdentity(identity);
194 }
195
196 // force profiles into cache
197 getProfileUserIdsForParentUser(parentUserId);
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800198 return parentUserId;
199 }
200
201 @GuardedBy("this")
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800202 private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800203 if (parentUserId != mCachedParentUserId) {
Soonil Nagarkar7ebcaef2020-01-15 14:21:58 -0800204 long identity = Binder.clearCallingIdentity();
205 try {
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800206 Preconditions.checkState(mUserManager != null);
207
208 // more expensive check - check that argument really is a parent user id
209 if (Build.IS_DEBUGGABLE) {
210 Preconditions.checkArgument(
211 mUserManager.getProfileParent(parentUserId) == null);
212 }
213
Soonil Nagarkar7ebcaef2020-01-15 14:21:58 -0800214 mCachedParentUserId = parentUserId;
215 mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
216 } finally {
217 Binder.restoreCallingIdentity(identity);
218 }
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800219 }
220
221 return mCachedProfileUserIds;
222 }
223
224 /**
225 * Dump info for debugging.
226 */
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800227 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
228 int currentUserId = mCurrentUserId;
229 pw.println("Current User: " + currentUserId + " " + Arrays.toString(
230 getProfileUserIdsForParentUser(currentUserId)));
Soonil Nagarkar9ec01852020-01-10 13:42:38 -0800231 }
232}