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 android.car.user;
18 
19 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
20 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
21 import static android.os.Process.myUid;
22 
23 import static com.android.internal.util.FunctionalUtils.getLambdaName;
24 
25 import android.annotation.CallbackExecutor;
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.RequiresPermission;
30 import android.annotation.SystemApi;
31 import android.annotation.TestApi;
32 import android.annotation.UserIdInt;
33 import android.car.Car;
34 import android.car.CarManagerBase;
35 import android.car.ICarUserService;
36 import android.content.pm.UserInfo;
37 import android.content.pm.UserInfo.UserInfoFlag;
38 import android.os.Bundle;
39 import android.os.IBinder;
40 import android.os.RemoteException;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.provider.Settings;
44 import android.sysprop.CarProperties;
45 import android.util.ArrayMap;
46 import android.util.EventLog;
47 import android.util.Log;
48 
49 import com.android.internal.annotations.GuardedBy;
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.car.EventLogTags;
52 import com.android.internal.infra.AndroidFuture;
53 import com.android.internal.os.IResultReceiver;
54 import com.android.internal.util.ArrayUtils;
55 import com.android.internal.util.Preconditions;
56 
57 import java.lang.annotation.Retention;
58 import java.lang.annotation.RetentionPolicy;
59 import java.util.Arrays;
60 import java.util.List;
61 import java.util.Objects;
62 import java.util.concurrent.Executor;
63 import java.util.stream.Collectors;
64 
65 /**
66  * API to manage users related to car.
67  *
68  * @hide
69  */
70 @SystemApi
71 @TestApi
72 public final class CarUserManager extends CarManagerBase {
73 
74     private static final String TAG = CarUserManager.class.getSimpleName();
75     private static final int HAL_TIMEOUT_MS = CarProperties.user_hal_timeout().orElse(5_000);
76 
77     private static final boolean DBG = false;
78 
79     /**
80      * {@link UserLifecycleEvent} called when the user is starting, for components to initialize
81      * any per-user state they maintain for running users.
82      *
83      * @hide
84      */
85     @SystemApi
86     @TestApi
87     public static final int USER_LIFECYCLE_EVENT_TYPE_STARTING = 1;
88 
89     /**
90      * {@link UserLifecycleEvent} called when switching to a different foreground user, for
91      * components that have special behavior for whichever user is currently in the foreground.
92      *
93      * <p>This is called before any application processes are aware of the new user.
94      *
95      * <p>Notice that internal system services might not have handled user switching yet, so be
96      * careful with interaction with them.
97      *
98      * @hide
99      */
100     @SystemApi
101     @TestApi
102     public static final int USER_LIFECYCLE_EVENT_TYPE_SWITCHING = 2;
103 
104     /**
105      * {@link UserLifecycleEvent} called when an existing user is in the process of being unlocked.
106      *
107      * <p>This means the credential-encrypted storage for that user is now available, and
108      * encryption-aware component filtering is no longer in effect.
109      *
110      * <p>Notice that internal system services might not have handled unlock yet, so most components
111      * should ignore this callback and rely on {@link #USER_LIFECYCLE_EVENT_TYPE_UNLOCKED} instead.
112      *
113      * @hide
114      */
115     @SystemApi
116     @TestApi
117     public static final int USER_LIFECYCLE_EVENT_TYPE_UNLOCKING = 3;
118 
119     /**
120      * {@link UserLifecycleEvent} called after an existing user is unlocked.
121      *
122      * @hide
123      */
124     @SystemApi
125     @TestApi
126     public static final int USER_LIFECYCLE_EVENT_TYPE_UNLOCKED = 4;
127 
128     /**
129      * {@link UserLifecycleEvent} called when an existing user is stopping, for components to
130      * finalize any per-user state they maintain for running users.
131      *
132      * <p>This is called prior to sending the {@code SHUTDOWN} broadcast to the user; it is a good
133      * place to stop making use of any resources of that user (such as binding to a service running
134      * in the user).
135      *
136      * <p><b>Note:</b> this is the last callback where the callee may access the target user's CE
137      * storage.
138      *
139      * @hide
140      */
141     @SystemApi
142     @TestApi
143     public static final int USER_LIFECYCLE_EVENT_TYPE_STOPPING = 5;
144 
145     /**
146      * {@link UserLifecycleEvent} called after an existing user is stopped.
147      *
148      * <p>This is called after all application process teardown of the user is complete.
149      *
150      * @hide
151      */
152     @SystemApi
153     @TestApi
154     public static final int USER_LIFECYCLE_EVENT_TYPE_STOPPED = 6;
155 
156     /** @hide */
157     @IntDef(prefix = { "USER_LIFECYCLE_EVENT_TYPE_" }, value = {
158             USER_LIFECYCLE_EVENT_TYPE_STARTING,
159             USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
160             USER_LIFECYCLE_EVENT_TYPE_UNLOCKING,
161             USER_LIFECYCLE_EVENT_TYPE_UNLOCKED,
162             USER_LIFECYCLE_EVENT_TYPE_STOPPING,
163             USER_LIFECYCLE_EVENT_TYPE_STOPPED,
164     })
165     @Retention(RetentionPolicy.SOURCE)
166     public @interface UserLifecycleEventType{}
167 
168     /** @hide */
169     public static final String BUNDLE_PARAM_ACTION = "action";
170     /** @hide */
171     public static final String BUNDLE_PARAM_PREVIOUS_USER_ID = "previous_user";
172 
173     private final Object mLock = new Object();
174     private final ICarUserService mService;
175     private final UserManager mUserManager;
176 
177     @Nullable
178     @GuardedBy("mLock")
179     private ArrayMap<UserLifecycleListener, Executor> mListeners;
180 
181     @Nullable
182     @GuardedBy("mLock")
183     private LifecycleResultReceiver mReceiver;
184 
185     /**
186      * @hide
187      */
CarUserManager(@onNull Car car, @NonNull IBinder service)188     public CarUserManager(@NonNull Car car, @NonNull IBinder service) {
189         this(car, ICarUserService.Stub.asInterface(service), UserManager.get(car.getContext()));
190     }
191 
192     /**
193      * @hide
194      */
195     @VisibleForTesting
CarUserManager(@onNull Car car, @NonNull ICarUserService service, @NonNull UserManager userManager)196     public CarUserManager(@NonNull Car car, @NonNull ICarUserService service,
197             @NonNull UserManager userManager) {
198         super(car);
199         mService = service;
200         mUserManager = userManager;
201     }
202 
203     /**
204      * Switches the foreground user to the given target user.
205      *
206      * @hide
207      */
208     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
switchUser(@serIdInt int targetUserId)209     public AndroidFuture<UserSwitchResult> switchUser(@UserIdInt int targetUserId) {
210         int uid = myUid();
211 
212         if (mUserManager.getUserSwitchability() != UserManager.SWITCHABILITY_STATUS_OK) {
213             return newSwitchResuiltForFailure(UserSwitchResult.STATUS_NOT_SWITCHABLE);
214         }
215 
216         try {
217             AndroidFuture<UserSwitchResult> future = new AndroidFuture<UserSwitchResult>() {
218                 @Override
219                 protected void onCompleted(UserSwitchResult result, Throwable err) {
220                     if (result != null) {
221                         EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_RESP, uid,
222                                 result.getStatus(), result.getErrorMessage());
223                     } else {
224                         Log.w(TAG, "switchUser(" + targetUserId + ") failed: " + err);
225                     }
226                     super.onCompleted(result, err);
227                 }
228             };
229             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SWITCH_USER_REQ, uid, targetUserId);
230             mService.switchUser(targetUserId, HAL_TIMEOUT_MS, future);
231             return future;
232         } catch (RemoteException e) {
233             AndroidFuture<UserSwitchResult> future =
234                     newSwitchResuiltForFailure(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
235             return handleRemoteExceptionFromCarService(e, future);
236         }
237     }
238 
newSwitchResuiltForFailure( @serSwitchResult.Status int status)239     private AndroidFuture<UserSwitchResult> newSwitchResuiltForFailure(
240             @UserSwitchResult.Status int status) {
241         AndroidFuture<UserSwitchResult> future = new AndroidFuture<>();
242         future.complete(new UserSwitchResult(status, null));
243         return future;
244     }
245 
246     /**
247      * Creates a new Android user.
248      *
249      * @hide
250      */
251     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
252             android.Manifest.permission.CREATE_USERS})
createUser(@ullable String name, @NonNull String userType, @UserInfoFlag int flags)253     public AndroidFuture<UserCreationResult> createUser(@Nullable String name,
254             @NonNull String userType, @UserInfoFlag int flags) {
255         int uid = myUid();
256         try {
257             AndroidFuture<UserCreationResult> future = new AndroidFuture<UserCreationResult>() {
258                 @Override
259                 protected void onCompleted(UserCreationResult result, Throwable err) {
260                     if (result != null) {
261                         EventLog.writeEvent(EventLogTags.CAR_USER_MGR_CREATE_USER_RESP, uid,
262                                 result.getStatus(), result.getErrorMessage());
263                         UserInfo user = result.getUser();
264                         if (result.isSuccess() && user != null && user.isGuest()) {
265                             onGuestCreated(user);
266                         }
267                     } else {
268                         Log.w(TAG, "createUser(" + userType + "," + UserInfo.flagsToString(flags)
269                                 + ") failed: " + err);
270                     }
271                     super.onCompleted(result, err);
272                 };
273             };
274             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_CREATE_USER_REQ, uid,
275                     safeName(name), userType, flags);
276             mService.createUser(name, userType, flags, HAL_TIMEOUT_MS, future);
277             return future;
278         } catch (RemoteException e) {
279             AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
280             future.complete(new UserCreationResult(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE,
281                     null, null));
282             return handleRemoteExceptionFromCarService(e, future);
283         }
284     }
285 
286     /**
287      * Creates a new guest Android user.
288      *
289      * @hide
290      */
291     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
292             android.Manifest.permission.CREATE_USERS})
createGuest(@ullable String name)293     public AndroidFuture<UserCreationResult> createGuest(@Nullable String name) {
294         return createUser(name, UserManager.USER_TYPE_FULL_GUEST, /* flags= */ 0);
295     }
296 
297     /**
298      * Creates a new Android user.
299      *
300      * @hide
301      */
302     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
303             android.Manifest.permission.CREATE_USERS})
createUser(@ullable String name, @UserInfoFlag int flags)304     public AndroidFuture<UserCreationResult> createUser(@Nullable String name,
305             @UserInfoFlag int flags) {
306         return createUser(name, UserManager.USER_TYPE_FULL_SECONDARY, flags);
307     }
308 
309     // TODO(b/159283854): move to UserManager
onGuestCreated(UserInfo user)310     private void onGuestCreated(UserInfo user) {
311         Settings.Secure.putStringForUser(getContext().getContentResolver(),
312                 Settings.Secure.SKIP_FIRST_USE_HINTS, "1", user.id);
313     }
314 
315      /**
316      * Removes a user.
317      *
318      * @hide
319      */
320     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
removeUser(@serIdInt int userId)321     public UserRemovalResult removeUser(@UserIdInt int userId) {
322         int uid = myUid();
323         EventLog.writeEvent(EventLogTags.CAR_USER_MGR_REMOVE_USER_REQ, uid, userId);
324         int status = UserRemovalResult.STATUS_HAL_INTERNAL_FAILURE;
325         try {
326             UserRemovalResult result = mService.removeUser(userId);
327             status = result.getStatus();
328             return result;
329         } catch (RemoteException e) {
330             return handleRemoteExceptionFromCarService(e,
331                     new UserRemovalResult(UserRemovalResult.STATUS_HAL_INTERNAL_FAILURE));
332         } finally {
333             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_REMOVE_USER_RESP, uid, status);
334         }
335     }
336 
337     /**
338      * Adds a listener for {@link UserLifecycleEvent user lifecycle events}.
339      *
340      * @throws IllegalStateException if the listener was already added.
341      *
342      * @hide
343      */
344     @SystemApi
345     @TestApi
346     @RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL})
addListener(@onNull @allbackExecutor Executor executor, @NonNull UserLifecycleListener listener)347     public void addListener(@NonNull @CallbackExecutor Executor executor,
348             @NonNull UserLifecycleListener listener) {
349         Objects.requireNonNull(executor, "executor cannot be null");
350         Objects.requireNonNull(listener, "listener cannot be null");
351 
352         int uid = myUid();
353         synchronized (mLock) {
354             Preconditions.checkState(mListeners == null || !mListeners.containsKey(listener),
355                     "already called for this listener");
356             if (mReceiver == null) {
357                 mReceiver = new LifecycleResultReceiver();
358                 try {
359                     EventLog.writeEvent(EventLogTags.CAR_USER_MGR_ADD_LISTENER, uid);
360                     if (DBG) Log.d(TAG, "Setting lifecycle receiver for uid " + uid);
361                     mService.setLifecycleListenerForUid(mReceiver);
362                 } catch (RemoteException e) {
363                     handleRemoteExceptionFromCarService(e);
364                 }
365             } else {
366                 if (DBG) Log.d(TAG, "Already set receiver for uid " + uid);
367             }
368 
369             if (mListeners == null) {
370                 mListeners = new ArrayMap<>(1); // Most likely app will have just one listener
371             } else if (DBG) {
372                 Log.d(TAG, "addListener(" + getLambdaName(listener) + "): context " + getContext()
373                         + " already has " + mListeners.size() + " listeners: "
374                         + mListeners.keySet().stream()
375                                 .map((l) -> getLambdaName(l))
376                                 .collect(Collectors.toList()), new Exception());
377             }
378             if (DBG) Log.d(TAG, "Adding listener: " + listener);
379             mListeners.put(listener, executor);
380         }
381     }
382 
383     /**
384      * Removes a listener for {@link UserLifecycleEvent user lifecycle events}.
385      *
386      * @throws IllegalStateException if the listener was not added beforehand.
387      *
388      * @hide
389      */
390     @SystemApi
391     @TestApi
392     @RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL})
removeListener(@onNull UserLifecycleListener listener)393     public void removeListener(@NonNull UserLifecycleListener listener) {
394         Objects.requireNonNull(listener, "listener cannot be null");
395 
396         int uid = myUid();
397         synchronized (mLock) {
398             Preconditions.checkState(mListeners != null && mListeners.containsKey(listener),
399                     "not called for this listener yet");
400             mListeners.remove(listener);
401 
402             if (!mListeners.isEmpty()) {
403                 if (DBG) Log.d(TAG, "removeListeners(): still " + mListeners.size() + " left");
404                 return;
405             }
406             mListeners = null;
407 
408             if (mReceiver == null) {
409                 Log.wtf(TAG, "removeListener(): receiver already null");
410                 return;
411             }
412 
413             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_REMOVE_LISTENER, uid);
414             if (DBG) Log.d(TAG, "Removing lifecycle receiver for uid=" + uid);
415             try {
416                 mService.resetLifecycleListenerForUid();
417                 mReceiver = null;
418             } catch (RemoteException e) {
419                 handleRemoteExceptionFromCarService(e);
420             }
421         }
422     }
423 
424     /**
425      * Check if user hal supports user association.
426      *
427      * @hide
428      */
isUserHalUserAssociationSupported()429     public boolean isUserHalUserAssociationSupported() {
430         try {
431             return mService.isUserHalUserAssociationSupported();
432         } catch (RemoteException e) {
433             return handleRemoteExceptionFromCarService(e, false);
434         }
435     }
436 
437     /**
438      * Gets the user authentication types associated with this manager's user.
439      *
440      * @hide
441      */
442     @NonNull
443     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
getUserIdentificationAssociation( @onNull int... types)444     public UserIdentificationAssociationResponse getUserIdentificationAssociation(
445             @NonNull int... types) {
446         Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
447         EventLog.writeEvent(EventLogTags.CAR_USER_MGR_GET_USER_AUTH_REQ, types.length);
448         try {
449             UserIdentificationAssociationResponse response =
450                     mService.getUserIdentificationAssociation(types);
451             if (response != null) {
452                 int[] values = response.getValues();
453                 EventLog.writeEvent(EventLogTags.CAR_USER_MGR_GET_USER_AUTH_RESP,
454                         values != null ? values.length : 0);
455             }
456             return response;
457         } catch (RemoteException e) {
458             return handleRemoteExceptionFromCarService(e, null);
459         }
460     }
461 
462     /**
463      * Sets the user authentication types associated with this manager's user.
464      *
465      * @hide
466      */
467     @NonNull
468     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
setUserIdentificationAssociation( @onNull int[] types, @NonNull int[] values)469     public AndroidFuture<UserIdentificationAssociationResponse> setUserIdentificationAssociation(
470             @NonNull int[] types, @NonNull int[] values) {
471         Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
472         Preconditions.checkArgument(!ArrayUtils.isEmpty(values), "must have at least one value");
473         if (types.length != values.length) {
474             throw new IllegalArgumentException("types (" + Arrays.toString(types) + ") and values ("
475                     + Arrays.toString(values) + ") should have the same length");
476         }
477         // TODO(b/153900032): move this logic to a common helper
478         Object[] loggedValues = new Integer[types.length * 2];
479         for (int i = 0; i < types.length; i++) {
480             loggedValues[i * 2] = types[i];
481             loggedValues[i * 2 + 1 ] = values[i];
482         }
483         EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SET_USER_AUTH_REQ, loggedValues);
484 
485         try {
486             AndroidFuture<UserIdentificationAssociationResponse> future =
487                     new AndroidFuture<UserIdentificationAssociationResponse>() {
488                 @Override
489                 protected void onCompleted(UserIdentificationAssociationResponse result,
490                         Throwable err) {
491                     if (result != null) {
492                         int[] rawValues = result.getValues();
493                         // TODO(b/153900032): move this logic to a common helper
494                         if (rawValues != null) {
495                             Object[] loggedValues = new Object[rawValues.length];
496                             for (int i = 0; i < rawValues.length; i++) {
497                                 loggedValues[i] = rawValues[i];
498                             }
499                             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_SET_USER_AUTH_RESP,
500                                     loggedValues);
501                         }
502                     } else {
503                         Log.w(TAG, "setUserIdentificationAssociation(" + Arrays.toString(types)
504                                 + ", " + Arrays.toString(values) + ") failed: " + err);
505                     }
506                     super.onCompleted(result, err);
507                 };
508             };
509             mService.setUserIdentificationAssociation(HAL_TIMEOUT_MS, types, values, future);
510             return future;
511         } catch (RemoteException e) {
512             AndroidFuture<UserIdentificationAssociationResponse> future = new AndroidFuture<>();
513             future.complete(UserIdentificationAssociationResponse.forFailure());
514             return handleRemoteExceptionFromCarService(e, future);
515         }
516     }
517 
518     /**
519      * Sets a callback to be notified before user switch. It should only be used by Car System UI.
520      *
521      * @hide
522      */
523     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
setUserSwitchUiCallback(@onNull UserSwitchUiCallback callback)524     public void setUserSwitchUiCallback(@NonNull UserSwitchUiCallback callback) {
525         Preconditions.checkArgument(callback != null, "Null callback");
526         UserSwitchUiCallbackReceiver userSwitchUiCallbackReceiver =
527                 new UserSwitchUiCallbackReceiver(callback);
528         try {
529             mService.setUserSwitchUiCallback(userSwitchUiCallbackReceiver);
530         } catch (RemoteException e) {
531             handleRemoteExceptionFromCarService(e);
532         }
533     }
534 
535     /**
536      * {@code IResultReceiver} used to receive user switch UI Callback.
537      */
538     // TODO(b/154958003): use mReceiver instead as now there are two binder objects
539     private final class UserSwitchUiCallbackReceiver extends IResultReceiver.Stub {
540 
541         private final UserSwitchUiCallback mUserSwitchUiCallback;
542 
UserSwitchUiCallbackReceiver(UserSwitchUiCallback callback)543         UserSwitchUiCallbackReceiver(UserSwitchUiCallback callback) {
544             mUserSwitchUiCallback = callback;
545         }
546 
547         @Override
send(int userId, Bundle unused)548         public void send(int userId, Bundle unused) throws RemoteException {
549             mUserSwitchUiCallback.showUserSwitchDialog(userId);
550         }
551     }
552 
553     /**
554      * {@code IResultReceiver} used to receive lifecycle events and dispatch to the proper listener.
555      */
556     private class LifecycleResultReceiver extends IResultReceiver.Stub {
557         @Override
send(int resultCode, Bundle resultData)558         public void send(int resultCode, Bundle resultData) {
559             if (resultData == null) {
560                 Log.w(TAG, "Received result (" + resultCode + ") without data");
561                 return;
562             }
563             int from = resultData.getInt(BUNDLE_PARAM_PREVIOUS_USER_ID, UserHandle.USER_NULL);
564             int to = resultCode;
565             int eventType = resultData.getInt(BUNDLE_PARAM_ACTION);
566             UserLifecycleEvent event = new UserLifecycleEvent(eventType, from, to);
567             ArrayMap<UserLifecycleListener, Executor> listeners;
568             synchronized (mLock) {
569                 listeners = mListeners;
570             }
571             if (listeners == null) {
572                 Log.w(TAG, "No listeners for event " + event);
573                 return;
574             }
575             int size = listeners.size();
576             EventLog.writeEvent(EventLogTags.CAR_USER_MGR_NOTIFY_LIFECYCLE_LISTENER,
577                     size, eventType, from, to);
578             for (int i = 0; i < size; i++) {
579                 UserLifecycleListener listener = listeners.keyAt(i);
580                 Executor executor = listeners.valueAt(i);
581                 if (DBG) {
582                     Log.d(TAG, "Calling " + getLambdaName(listener) + " for event " + event);
583                 }
584                 executor.execute(() -> listener.onEvent(event));
585             }
586         }
587     }
588 
589     /** @hide */
590     @Override
onCarDisconnected()591     public void onCarDisconnected() {
592         // nothing to do
593     }
594 
595     /**
596      * @hide
597      */
598     @TestApi
lifecycleEventTypeToString(@serLifecycleEventType int type)599     public static String lifecycleEventTypeToString(@UserLifecycleEventType int type) {
600         switch (type) {
601             case USER_LIFECYCLE_EVENT_TYPE_STARTING:
602                 return "STARTING";
603             case USER_LIFECYCLE_EVENT_TYPE_SWITCHING:
604                 return "SWITCHING";
605             case USER_LIFECYCLE_EVENT_TYPE_UNLOCKING:
606                 return "UNLOCKING";
607             case USER_LIFECYCLE_EVENT_TYPE_UNLOCKED:
608                 return "UNLOCKED";
609             case USER_LIFECYCLE_EVENT_TYPE_STOPPING:
610                 return "STOPPING";
611             case USER_LIFECYCLE_EVENT_TYPE_STOPPED:
612                 return "STOPPED";
613             default:
614                 return "UNKNOWN-" + type;
615         }
616     }
617 
618     // NOTE: this method is called by ExperimentalCarUserManager, so it can get the mService.
619     // "Real" ExperimentalCarUserManager instances should be obtained through
620     //    ExperimentalCarUserManager.from(mCarUserManager)
621     // instead.
newExperimentalCarUserManager()622     ExperimentalCarUserManager newExperimentalCarUserManager() {
623         return new ExperimentalCarUserManager(mCar, mService);
624     }
625 
626     /**
627      * Checks if the given {@code userId} represents a valid user.
628      *
629      * <p>A "valid" user:
630      *
631      * <ul>
632      *   <li>Must exist in the device.
633      *   <li>Is not in the process of being deleted.
634      *   <li>Cannot be the {@link UserHandle#isSystem() system} user on devices that use
635      *   {@link UserManager#isHeadlessSystemUserMode() headless system mode}.
636      * </ul>
637      *
638      * @hide
639      */
isValidUser(@serIdInt int userId)640     public boolean isValidUser(@UserIdInt int userId) {
641         List<UserInfo> allUsers = mUserManager.getUsers(/* excludeDying= */ true);
642         for (int i = 0; i < allUsers.size(); i++) {
643             UserInfo user = allUsers.get(i);
644             if (user.id == userId && (userId != UserHandle.USER_SYSTEM
645                     || !UserManager.isHeadlessSystemUserMode())) {
646                 return true;
647             }
648         }
649         return false;
650     }
651 
652     // TODO(b/150413515): use from UserHelper instead (would require a new make target, otherwise it
653     // would include the whole car-user-lib)
isHeadlessSystemUser(int targetUserId)654     private boolean isHeadlessSystemUser(int targetUserId) {
655         return targetUserId == UserHandle.USER_SYSTEM && UserManager.isHeadlessSystemUserMode();
656     }
657 
658     // TODO(b/150413515): use from UserHelper instead (would require a new make target, otherwise it
659     // would include the whole car-user-lib)
660     @Nullable
safeName(@ullable String name)661     private static String safeName(@Nullable String name) {
662         return name == null ? name : name.length() + "_chars";
663     }
664 
665     /**
666      * Defines a lifecycle event for an Android user.
667      *
668      * @hide
669      */
670     @SystemApi
671     @TestApi
672     public static final class UserLifecycleEvent {
673         private final @UserLifecycleEventType int mEventType;
674         private final @UserIdInt int mUserId;
675         private final @UserIdInt int mPreviousUserId;
676 
677         /** @hide */
UserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int from, @UserIdInt int to)678         public UserLifecycleEvent(@UserLifecycleEventType int eventType,
679                 @UserIdInt int from, @UserIdInt int to) {
680             mEventType = eventType;
681             mPreviousUserId = from;
682             mUserId = to;
683         }
684 
685         /** @hide */
UserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int to)686         public UserLifecycleEvent(@UserLifecycleEventType int eventType, @UserIdInt int to) {
687             this(eventType, UserHandle.USER_NULL, to);
688         }
689 
690         /**
691          * Gets the event type.
692          *
693          * @return either {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STARTING},
694          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING},
695          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKING},
696          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKED},
697          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STOPPING}, or
698          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STOPPED}.
699          */
700         @UserLifecycleEventType
getEventType()701         public int getEventType() {
702             return mEventType;
703         }
704 
705         /**
706          * Gets the id of the user whose event is being reported.
707          *
708          * @hide
709          */
710         @UserIdInt
getUserId()711         public int getUserId() {
712             return mUserId;
713         }
714 
715         /**
716          * Gets the handle of the user whose event is being reported.
717          */
718         @NonNull
getUserHandle()719         public UserHandle getUserHandle() {
720             return UserHandle.of(mUserId);
721         }
722 
723         /**
724          * Gets the id of the user being switched from.
725          *
726          * <p>This method returns {@link UserHandle#USER_NULL} for all event types but
727          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING}.
728          *
729          * @hide
730          */
731         @UserIdInt
getPreviousUserId()732         public int getPreviousUserId() {
733             return mPreviousUserId;
734         }
735 
736         /**
737          * Gets the handle of the user being switched from.
738          *
739          * <p>This method returns {@code null} for all event types but
740          * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING}.
741          */
742         @Nullable
getPreviousUserHandle()743         public UserHandle getPreviousUserHandle() {
744             return mPreviousUserId == UserHandle.USER_NULL ? null : UserHandle.of(mPreviousUserId);
745         }
746 
747         @Override
toString()748         public String toString() {
749             StringBuilder builder = new StringBuilder("Event[type=")
750                     .append(lifecycleEventTypeToString(mEventType));
751             if (mPreviousUserId != UserHandle.USER_NULL) {
752                 builder
753                     .append(",from=").append(mPreviousUserId)
754                     .append(",to=").append(mUserId);
755             } else {
756                 builder.append(",user=").append(mUserId);
757             }
758 
759             return builder.append(']').toString();
760         }
761     }
762 
763     /**
764      * Listener for Android User lifecycle events.
765      *
766      * <p>Must be registered using {@link CarUserManager#addListener(UserLifecycleListener)} and
767      * unregistered through {@link CarUserManager#removeListener(UserLifecycleListener)}.
768      *
769      * @hide
770      */
771     @SystemApi
772     @TestApi
773     public interface UserLifecycleListener {
774 
775         /**
776          * Called to notify the given {@code event}.
777          */
onEvent(@onNull UserLifecycleEvent event)778         void onEvent(@NonNull UserLifecycleEvent event);
779     }
780 
781     /**
782      * Callback for notifying user switch before switch started.
783      *
784      * <p> It should only be user by Car System UI. The purpose of this callback is notify the
785      * Car System UI to display the user switch UI.
786      *
787      * @hide
788      */
789     public interface UserSwitchUiCallback {
790 
791         /**
792          * Called to notify that user switch dialog should be shown now.
793          */
showUserSwitchDialog(@serIdInt int userId)794         void showUserSwitchDialog(@UserIdInt int userId);
795     }
796 }
797