1 /*
2  * Copyright (C) 2023 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.systemui.car.userpicker;
18 
19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_CREATED;
20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE;
21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_REMOVED;
22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
24 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPING;
25 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
26 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
27 import static android.car.user.CarUserManager.lifecycleEventTypeToString;
28 import static android.os.UserHandle.USER_ALL;
29 import static android.os.UserHandle.USER_SYSTEM;
30 import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
31 import static android.os.UserManager.isHeadlessSystemUserMode;
32 
33 import android.annotation.MainThread;
34 import android.annotation.Nullable;
35 import android.annotation.UserIdInt;
36 import android.app.ActivityManager;
37 import android.car.SyncResultCallback;
38 import android.car.user.CarUserManager;
39 import android.car.user.CarUserManager.UserLifecycleListener;
40 import android.car.user.UserCreationResult;
41 import android.car.user.UserLifecycleEventFilter;
42 import android.car.user.UserStartRequest;
43 import android.car.user.UserStartResponse;
44 import android.car.user.UserStopRequest;
45 import android.car.user.UserStopResponse;
46 import android.car.user.UserSwitchRequest;
47 import android.car.user.UserSwitchResult;
48 import android.car.util.concurrent.AsyncFuture;
49 import android.content.BroadcastReceiver;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.pm.UserInfo;
54 import android.os.Handler;
55 import android.os.Looper;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.util.Log;
59 import android.util.Slog;
60 import android.util.SparseArray;
61 
62 import androidx.annotation.VisibleForTesting;
63 
64 import com.android.systemui.R;
65 
66 import java.util.List;
67 import java.util.concurrent.CountDownLatch;
68 import java.util.concurrent.ExecutorService;
69 import java.util.concurrent.Executors;
70 import java.util.concurrent.TimeUnit;
71 
72 import javax.inject.Inject;
73 
74 /**
75  * Helper class for {@link UserManager}, this is meant to be used by builds that support
76  * {@link UserManager#isVisibleBackgroundUsersEnabled() Multi-user model with Concurrent Multi
77  * User Feature.}
78  *
79  * <p>This class handles user event such as creating, removing, unlocking, stopped, and so on.
80  * Also, it provides methods for creating, stopping, starting users.
81  */
82 @UserPickerScope
83 public final class UserEventManager {
84     private static final String TAG = UserEventManager.class.getSimpleName();
85     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
86 
87     private static final long USER_TIMEOUT_MS = 10_000;
88 
89     private final UserLifecycleEventFilter mFilter = new UserLifecycleEventFilter.Builder()
90             .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
91             .addEventType(USER_LIFECYCLE_EVENT_TYPE_INVISIBLE)
92             .addEventType(USER_LIFECYCLE_EVENT_TYPE_CREATED)
93             .addEventType(USER_LIFECYCLE_EVENT_TYPE_REMOVED)
94             .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
95             .addEventType(USER_LIFECYCLE_EVENT_TYPE_STARTING)
96             .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPING)
97             .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPED).build();
98 
99     private final Context mContext;
100     private final UserManager mUserManager;
101     private final CarServiceMediator mCarServiceMediator;
102     private final UserPickerSharedState mUserPickerSharedState;
103 
104     /**
105      * {@link UserPickerController} is per-display object. It adds listener to UserEventManager to
106      * update user information, and UserEventManager will call listeners whenever user event occurs.
107      * mUpdateListeners is used only on main thread.
108      */
109     private final SparseArray<OnUpdateUsersListener> mUpdateListeners;
110 
111     private final Handler mMainHandler;
112 
113     /**
114      * This is used to wait until previous user is in invisible state.
115      * When changing user, previous user is stopped, and new user is started. But new user can not
116      * be started if occupant zone is not unassigned for previous user yet, and occupant zone
117      * unassignment is processed on user invisible event. In this reason, we should wait until
118      * previous user is in invisible state for stable user starting. <b/275973135>
119      */
120     private final UserInvisibleWaiter mUserInvisibleWaiter = new UserInvisibleWaiter();
121 
122     /**
123      * We don't use the main thread for UX responsiveness when handling user events.
124      */
125     private final ExecutorService mUserLifecycleReceiver;
126 
127     @VisibleForTesting
128     final UserLifecycleListener mUserLifecycleListener = event -> {
129         onUserEvent(event);
130     };
131 
132     private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() {
133         @Override
134         public void onReceive(Context context, Intent intent) {
135             runUpdateUsersOnMainThread();
136         }
137     };
138 
139     @Inject
UserEventManager(Context context, CarServiceMediator carServiceMediator, UserPickerSharedState userPickerSharedState)140     UserEventManager(Context context, CarServiceMediator carServiceMediator,
141             UserPickerSharedState userPickerSharedState) {
142         mUpdateListeners = new SparseArray<>();
143         mContext = context.getApplicationContext();
144         mUserLifecycleReceiver = Executors.newSingleThreadExecutor();
145         mMainHandler = new Handler(Looper.getMainLooper());
146         mUserManager = mContext.getSystemService(UserManager.class);
147         mUserPickerSharedState = userPickerSharedState;
148         mCarServiceMediator = carServiceMediator;
149         mCarServiceMediator.registerUserChangeEventsListener(mUserLifecycleReceiver, mFilter,
150                 mUserLifecycleListener);
151         registerUserInfoChangedReceiver();
152     }
153 
154     /**
155      * Unregisters all the listeners when the owners is being destroyed
156      */
onDestroy()157     void onDestroy() {
158         mCarServiceMediator.onDestroy();
159         mContext.unregisterReceiver(mUserUpdateReceiver);
160     }
161 
onUserEvent(CarUserManager.UserLifecycleEvent event)162     private void onUserEvent(CarUserManager.UserLifecycleEvent event) {
163         int eventType = event.getEventType();
164         int userId = event.getUserId();
165         if (DEBUG) {
166             Slog.d(TAG, "event=" + lifecycleEventTypeToString(eventType) + " userId=" + userId);
167         }
168         if (eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPING) {
169             mUserPickerSharedState.addStoppingUserId(userId);
170         } else if (eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPED) {
171             if (mUserPickerSharedState.isStoppingUser(userId)) {
172                 mUserPickerSharedState.removeStoppingUserId(userId);
173             }
174         } else if (eventType == USER_LIFECYCLE_EVENT_TYPE_INVISIBLE) {
175             mUserInvisibleWaiter.onUserInvisible(userId);
176         }
177         runUpdateUsersOnMainThread(userId, eventType);
178     }
179 
registerUserInfoChangedReceiver()180     private void registerUserInfoChangedReceiver() {
181         IntentFilter filter = new IntentFilter();
182         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
183         mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL, filter, null, null);
184     }
185 
registerOnUpdateUsersListener(OnUpdateUsersListener listener, int displayId)186     void registerOnUpdateUsersListener(OnUpdateUsersListener listener, int displayId) {
187         if (listener == null) {
188             return;
189         }
190         mUpdateListeners.put(displayId, listener);
191     }
192 
unregisterOnUpdateUsersListener(int displayId)193     void unregisterOnUpdateUsersListener(int displayId) {
194         mUpdateListeners.remove(displayId);
195     }
196 
197     @MainThread
updateUsers(@serIdInt int userId, int userEvent)198     private void updateUsers(@UserIdInt int userId, int userEvent) {
199         for (int i = 0; i < mUpdateListeners.size(); i++) {
200             OnUpdateUsersListener listener = mUpdateListeners.valueAt(i);
201             if (listener != null) {
202                 listener.onUpdateUsers(userId, userEvent);
203             }
204         }
205     }
206 
runUpdateUsersOnMainThread()207     void runUpdateUsersOnMainThread() {
208         runUpdateUsersOnMainThread(USER_ALL, 0);
209     }
210 
runUpdateUsersOnMainThread(@serIdInt int userId, int userEvent)211     void runUpdateUsersOnMainThread(@UserIdInt int userId, int userEvent) {
212         if (Looper.myLooper() != Looper.getMainLooper()) {
213             mMainHandler.post(() -> updateUsers(userId, userEvent));
214         } else {
215             updateUsers(userId, userEvent);
216         }
217     }
218 
getMaxSupportedUsers()219     static int getMaxSupportedUsers() {
220         int maxSupportedUsers = UserManager.getMaxSupportedUsers();
221         if (isHeadlessSystemUserMode()) {
222             maxSupportedUsers -= 1;
223         }
224         return maxSupportedUsers;
225     }
226 
getUserInfo(@serIdInt int userId)227     UserInfo getUserInfo(@UserIdInt int userId) {
228         return mUserManager.getUserInfo(userId);
229     }
230 
getCurrentForegroundUserInfo()231     UserInfo getCurrentForegroundUserInfo() {
232         return mUserManager.getUserInfo(ActivityManager.getCurrentUser());
233     }
234 
235     /**
236      * Gets alive users from user manager except guest users to create user records.
237      * If it is headless system user mode, removes system user info from the list by
238      * {@link UserManager#getAliveUsers}.
239      *
240      * @return the list of users that were created except guest users.
241      */
getAliveUsers()242     List<UserInfo> getAliveUsers() {
243         List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
244         for (int i = aliveUsers.size() - 1; i >= 0; i--) {
245             UserInfo userInfo = aliveUsers.get(i);
246             if ((isHeadlessSystemUserMode() && userInfo.id == USER_SYSTEM)
247                     || userInfo.isGuest()) {
248                 aliveUsers.remove(i);
249             }
250         }
251         return aliveUsers;
252     }
253 
isUserLimitReached()254     boolean isUserLimitReached() {
255         int countNonGuestUsers = getAliveUsers().size();
256         int maxSupportedUsers = getMaxSupportedUsers();
257 
258         if (countNonGuestUsers > maxSupportedUsers) {
259             Slog.e(TAG, "There are more users on the device than allowed.");
260             return true;
261         }
262         return countNonGuestUsers == maxSupportedUsers;
263     }
264 
canForegroundUserAddUsers()265     boolean canForegroundUserAddUsers() {
266         return !mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_ADD_USER,
267                 UserHandle.of(ActivityManager.getCurrentUser()));
268     }
269 
isForegroundUserNotSwitchable(UserHandle fgUserHandle)270     boolean isForegroundUserNotSwitchable(UserHandle fgUserHandle) {
271         return mUserManager.getUserSwitchability(fgUserHandle) != SWITCHABILITY_STATUS_OK;
272     }
273 
274     @Nullable
createNewUser()275     UserCreationResult createNewUser() {
276         CarUserManager carUserManager = mCarServiceMediator.getCarUserManager();
277         AsyncFuture<UserCreationResult> future = carUserManager.createUser(
278                 mContext.getString(R.string.car_new_user), 0);
279         return getUserCreationResult(future);
280     }
281 
282     @Nullable
createGuest()283     UserCreationResult createGuest() {
284         CarUserManager carUserManager = mCarServiceMediator.getCarUserManager();
285         AsyncFuture<UserCreationResult> future = carUserManager.createGuest(
286                 mContext.getString(R.string.car_guest));
287         return getUserCreationResult(future);
288     }
289 
290     @Nullable
getUserCreationResult(AsyncFuture<UserCreationResult> future)291     private UserCreationResult getUserCreationResult(AsyncFuture<UserCreationResult> future) {
292         UserCreationResult result = null;
293         try {
294             result = future.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
295             if (result == null) {
296                 Slog.e(TAG, "Timed out creating guest after " + USER_TIMEOUT_MS + "ms...");
297                 return null;
298             }
299         } catch (InterruptedException e) {
300             Slog.w(TAG, "Interrupted waiting for future " + future, e);
301             Thread.currentThread().interrupt();
302             return null;
303         } catch (Exception e) {
304             Slog.w(TAG, "Exception getting future " + future, e);
305             return null;
306         }
307 
308         return result;
309     }
310 
isUserRunningUnlocked(@serIdInt int userId)311     boolean isUserRunningUnlocked(@UserIdInt int userId) {
312         return mUserManager.isUserRunning(userId) && mUserManager.isUserUnlocked(userId);
313     }
314 
isUserRunning(@serIdInt int userId)315     boolean isUserRunning(@UserIdInt int userId) {
316         return mUserManager.isUserRunning(userId);
317     }
318 
startUserForDisplay(@serIdInt int prevCurrentUser, @UserIdInt int userId, int displayId, boolean isFgUserStart)319     boolean startUserForDisplay(@UserIdInt int prevCurrentUser, @UserIdInt int userId,
320             int displayId, boolean isFgUserStart) {
321         if (DEBUG) {
322             Slog.d(TAG, "switchToUserForDisplay " + userId + " State :  Running "
323                     + mUserManager.isUserRunning(userId) + " Unlocked "
324                     + mUserManager.isUserUnlocked(userId) + " displayId=" + displayId
325                     + " prevCurrentUser=" + prevCurrentUser + " isFgUserStart=" + isFgUserStart);
326         }
327         UserHandle userHandle = UserHandle.of(userId);
328         CarUserManager carUserManager = mCarServiceMediator.getCarUserManager();
329         if (carUserManager == null) {
330             Slog.w(TAG, "car user manager is not available when starting user " + userId);
331             return false;
332         }
333         if (isFgUserStart) {
334             // Old user will be stopped by {@link UserController} after user switching
335             // completed. In the case of user switching, to avoid clicking stopping user, we can
336             // block previous current user immediately here by adding to the list of stopping
337             // users.
338             mUserPickerSharedState.addStoppingUserId(prevCurrentUser);
339             try {
340                 SyncResultCallback<UserSwitchResult> userSwitchCallback =
341                         new SyncResultCallback<>();
342                 carUserManager.switchUser(new UserSwitchRequest.Builder(
343                         userHandle).build(), Runnable::run, userSwitchCallback);
344                 UserSwitchResult userSwitchResult =
345                         userSwitchCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
346                 if (userSwitchResult.isSuccess()) {
347                     Slog.i(TAG, "Successful switchUser from " + prevCurrentUser + " to " + userId
348                             + ". Result: " + userSwitchResult);
349                     return true;
350                 }
351                 Slog.w(TAG, "Failed to switchUser from " + prevCurrentUser + " to " + userId
352                         + ". Result: " + userSwitchResult);
353             } catch (Exception e) {
354                 Slog.e(TAG, "Exception during switchUser from " + prevCurrentUser + " to "
355                         + userId, e);
356                 return false;
357             }
358         }
359 
360         try {
361             SyncResultCallback<UserStartResponse> userStartCallback = new SyncResultCallback<>();
362             carUserManager.startUser(
363                     new UserStartRequest.Builder(UserHandle.of(userId))
364                             .setDisplayId(displayId).build(),
365                     Runnable::run, userStartCallback);
366             UserStartResponse userStartResponse =
367                     userStartCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
368             if (userStartResponse.isSuccess()) {
369                 Slog.i(TAG, "Successful startUser for user " + userId + " on display "
370                         + displayId + ". Result: " + userStartResponse);
371                 return true;
372             }
373             Slog.w(TAG, "startUser failed for " + userId + " on display " + displayId
374                     + ". Result: " + userStartResponse);
375         } catch (Exception e) {
376             Slog.e(TAG, "Exception during startUser for user " + userId + " on display "
377                     + displayId, e);
378         }
379 
380         return false;
381     }
382 
stopUserUnchecked(@serIdInt int userId, int displayId)383     boolean stopUserUnchecked(@UserIdInt int userId, int displayId) {
384         if (DEBUG) {
385             Slog.d(TAG, "stop user:" + userId);
386         }
387 
388         mUserPickerSharedState.addStoppingUserId(userId);
389 
390         CarUserManager carUserManager = mCarServiceMediator.getCarUserManager();
391         if (carUserManager == null) {
392             Slog.w(TAG, "car user manager is not available when stopping user " + userId);
393             return false;
394         }
395 
396         // We do not need to unassign the user from the occupant zone, because it is handled by
397         // CarUserService#onUserInvisible().
398         try {
399             mUserInvisibleWaiter.init(userId);
400             SyncResultCallback<UserStopResponse> userStopCallback = new SyncResultCallback<>();
401             carUserManager.stopUser(new UserStopRequest.Builder(UserHandle.of(userId)).build(),
402                     Runnable::run, userStopCallback);
403             UserStopResponse userStopResponse =
404                     userStopCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
405             if (userStopResponse.isSuccess()) {
406                 Slog.i(TAG, "Successful stopUser for user " + userId + " on display " + displayId
407                         + ". Result: " + userStopResponse);
408                 return mUserInvisibleWaiter.waitUserInvisible();
409             }
410             Slog.w(TAG, "stopUser failed for user " + userId + " on display " + displayId
411                     + ". Result: " + userStopResponse);
412         } catch (Exception e) {
413             Slog.e(TAG, "Exception during stopUser for user " + userId + " on display "
414                     + displayId, e);
415         }
416 
417         mUserPickerSharedState.removeStoppingUserId(userId);
418         return false;
419     }
420 
421     private static class UserInvisibleWaiter {
422         private @UserIdInt int mUserId;
423         private CountDownLatch mWaiter;
424 
init(@serIdInt int userId)425         void init(@UserIdInt int userId) {
426             mUserId = userId;
427             mWaiter = new CountDownLatch(1);
428         }
429 
waitUserInvisible()430         boolean waitUserInvisible() {
431             if (mWaiter != null) {
432                 try {
433                     // This method returns false when timeout occurs so that user can re-try to
434                     // login. A timeout means that stopUser() has been called successfully, but
435                     // the user hasn't changed to invisible yet.
436                     return mWaiter.await(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
437                 } catch (InterruptedException e) {
438                     mWaiter = null;
439                 }
440             }
441             return true;
442         }
443 
onUserInvisible(@serIdInt int userId)444         void onUserInvisible(@UserIdInt int userId) {
445             if (userId == mUserId && mWaiter != null) {
446                 mWaiter.countDown();
447                 mWaiter = null;
448             }
449         }
450     }
451 
452     /**
453      * Interface for listeners that want to register for receiving updates to changes to the users
454      * on the system including removing and adding users, and changing user info.
455      */
456     public interface OnUpdateUsersListener {
457         /**
458          * Method that will get called when users list has been changed.
459          */
onUpdateUsers(@serIdInt int userId, int userEvent)460         void onUpdateUsers(@UserIdInt int userId, int userEvent);
461     }
462 }
463