• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
20 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.annotation.UserIdInt;
28 import android.car.annotation.AddedInOrBefore;
29 import android.car.annotation.ApiRequirements;
30 import android.hardware.display.DisplayManager;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.Parcel;
36 import android.os.Parcelable;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.util.Log;
41 import android.view.Display;
42 
43 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
44 import com.android.car.internal.util.Lists;
45 
46 import java.lang.annotation.ElementType;
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.lang.annotation.Target;
50 import java.lang.ref.WeakReference;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.concurrent.CopyOnWriteArrayList;
55 
56 /**
57  * API to get information on displays and users in the car.
58  *
59  * <p>From car version {@link CarVersion.VERSION_CODES#UPSIDE_DOWN_CAKE_0}, system without the
60  * driver zone is allowed and the current user will not be the driver.
61  */
62 public class CarOccupantZoneManager extends CarManagerBase {
63 
64     private static final String TAG = CarOccupantZoneManager.class.getSimpleName();
65 
66     /**
67      * Display type is not known. In some system, some displays may be just public display without
68      * any additional information and such displays will be treated as unknown.
69      */
70     @AddedInOrBefore(majorVersion = 33)
71     public static final int DISPLAY_TYPE_UNKNOWN = 0;
72 
73     /**
74      * Main display users are interacting with. UI for the user will be launched to this display by
75      * default. {@link Display#DEFAULT_DISPLAY} will be always have this type. But there can be
76      * multiple of this type as each passenger can have their own main display.
77      */
78     @AddedInOrBefore(majorVersion = 33)
79     public static final int DISPLAY_TYPE_MAIN = 1;
80 
81     /** Instrument cluster display. This may exist only for driver. */
82     @AddedInOrBefore(majorVersion = 33)
83     public static final int DISPLAY_TYPE_INSTRUMENT_CLUSTER = 2;
84 
85     /** Head Up Display. This may exist only for driver. */
86     @AddedInOrBefore(majorVersion = 33)
87     public static final int DISPLAY_TYPE_HUD = 3;
88 
89     /** Dedicated display for showing IME for {@link #DISPLAY_TYPE_MAIN} */
90     @AddedInOrBefore(majorVersion = 33)
91     public static final int DISPLAY_TYPE_INPUT = 4;
92 
93     /**
94      * Auxiliary display which can provide additional screen for {@link #DISPLAY_TYPE_MAIN}.
95      * Activity running in {@link #DISPLAY_TYPE_MAIN} may use {@link android.app.Presentation} to
96      * show additional information.
97      */
98     @AddedInOrBefore(majorVersion = 33)
99     public static final int DISPLAY_TYPE_AUXILIARY = 5;
100 
101     /** @hide */
102     @Retention(RetentionPolicy.SOURCE)
103     @IntDef(prefix = "DISPLAY_TYPE_", value = {
104             DISPLAY_TYPE_UNKNOWN,
105             DISPLAY_TYPE_MAIN,
106             DISPLAY_TYPE_INSTRUMENT_CLUSTER,
107             DISPLAY_TYPE_HUD,
108             DISPLAY_TYPE_INPUT,
109             DISPLAY_TYPE_AUXILIARY,
110     })
111     @Target({ElementType.TYPE_USE})
112     public @interface DisplayTypeEnum {}
113 
114     /** @hide */
115     @AddedInOrBefore(majorVersion = 33)
116     public static final int OCCUPANT_TYPE_INVALID = -1;
117 
118     /**
119      * Represents the driver. There can be one or zero driver for the system. Zero driver situation
120      * can happen if the system is configured to support only passengers.
121      */
122     @AddedInOrBefore(majorVersion = 33)
123     public static final int OCCUPANT_TYPE_DRIVER = 0;
124 
125     /**
126      * Represents front passengers who sit in front side of car. Most cars will have only
127      * one passenger of this type but this can be multiple.
128      */
129     @AddedInOrBefore(majorVersion = 33)
130     public static final int OCCUPANT_TYPE_FRONT_PASSENGER = 1;
131 
132     /** Represents passengers in rear seats. There can be multiple passengers of this type. */
133     @AddedInOrBefore(majorVersion = 33)
134     public static final int OCCUPANT_TYPE_REAR_PASSENGER = 2;
135 
136     /** @hide */
137     @Retention(RetentionPolicy.SOURCE)
138     @IntDef(prefix = "OCCUPANT_TYPE_", value = {
139             OCCUPANT_TYPE_DRIVER,
140             OCCUPANT_TYPE_FRONT_PASSENGER,
141             OCCUPANT_TYPE_REAR_PASSENGER,
142     })
143     @Target({ElementType.TYPE_USE})
144     public @interface OccupantTypeEnum {}
145 
146     /**
147      * Represents an occupant zone in a car.
148      *
149      * <p>Each occupant does not necessarily represent single person but it is for mapping to one
150      * set of displays. For example, for display located in center rear seat, both left and right
151      * side passengers may use it but it is abstracted as a single occupant zone.</p>
152      */
153     public static final class OccupantZoneInfo implements Parcelable {
154         /** @hide */
155         @AddedInOrBefore(majorVersion = 33)
156         public static final int INVALID_ZONE_ID = -1;
157 
158         /**
159          * This is an unique id to distinguish each occupant zone.
160          *
161          * <p>This can be helpful to distinguish different zones when {@link #occupantType} and
162          * {@link #seat} are the same for multiple occupant / passenger zones.</p>
163          *
164          * <p>This id will remain the same for the same zone across configuration changes like
165          * user switching or display changes</p>
166          */
167         @AddedInOrBefore(majorVersion = 33)
168         public int zoneId;
169         /** Represents type of passenger */
170         @OccupantTypeEnum
171         @AddedInOrBefore(majorVersion = 33)
172         public final int occupantType;
173         /**
174          * Represents seat assigned for the occupant. In some system, this can have value of
175          * {@link VehicleAreaSeat#SEAT_UNKNOWN}.
176          */
177         @VehicleAreaSeat.Enum
178         @AddedInOrBefore(majorVersion = 33)
179         public final int seat;
180 
181         /** @hide */
OccupantZoneInfo(int zoneId, @OccupantTypeEnum int occupantType, @VehicleAreaSeat.Enum int seat)182         public OccupantZoneInfo(int zoneId, @OccupantTypeEnum int occupantType,
183                 @VehicleAreaSeat.Enum int seat) {
184             this.zoneId = zoneId;
185             this.occupantType = occupantType;
186             this.seat = seat;
187         }
188 
189         /** @hide */
OccupantZoneInfo(Parcel in)190         public OccupantZoneInfo(Parcel in) {
191             zoneId = in.readInt();
192             occupantType = in.readInt();
193             seat = in.readInt();
194         }
195 
196         @Override
197         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
198         @AddedInOrBefore(majorVersion = 33)
describeContents()199         public int describeContents() {
200             return 0;
201         }
202 
203         @Override
204         @AddedInOrBefore(majorVersion = 33)
writeToParcel(Parcel dest, int flags)205         public void writeToParcel(Parcel dest, int flags) {
206             dest.writeInt(zoneId);
207             dest.writeInt(occupantType);
208             dest.writeInt(seat);
209         }
210 
211         @Override
equals(Object other)212         public boolean equals(Object other) {
213             if (this == other) {
214                 return true;
215             }
216             if (!(other instanceof OccupantZoneInfo)) {
217                 return false;
218             }
219             OccupantZoneInfo that = (OccupantZoneInfo) other;
220             return zoneId == that.zoneId && occupantType == that.occupantType
221                     && seat == that.seat;
222         }
223 
224         @Override
hashCode()225         public int hashCode() {
226             int hash = 23;
227             hash = hash * 17 + zoneId;
228             hash = hash * 17 + occupantType;
229             hash = hash * 17 + seat;
230             return hash;
231         }
232 
233         @AddedInOrBefore(majorVersion = 33)
234         public static final Parcelable.Creator<OccupantZoneInfo> CREATOR =
235                 new Parcelable.Creator<>() {
236                     public OccupantZoneInfo createFromParcel(Parcel in) {
237                         return new OccupantZoneInfo(in);
238                     }
239 
240                     public OccupantZoneInfo[] newArray(int size) {
241                         return new OccupantZoneInfo[size];
242                     }
243                 };
244 
245         @Override
toString()246         public String toString() {
247             StringBuilder b = new StringBuilder(64);
248             b.append("OccupantZoneInfo{zoneId=");
249             b.append(zoneId);
250             b.append(" type=");
251             b.append(occupantType);
252             b.append(" seat=");
253             b.append(Integer.toHexString(seat));
254             b.append("}");
255             return b.toString();
256         }
257     }
258 
259     /**
260      * Zone config change caused by display changes. A display could have been added / removed.
261      * Besides change in display itself. this can lead into removal / addition of passenger zones.
262      */
263     @AddedInOrBefore(majorVersion = 33)
264     public static final int ZONE_CONFIG_CHANGE_FLAG_DISPLAY = 0x1;
265 
266     /** Zone config change caused by user change. Assigned user for passenger zones have changed. */
267     @AddedInOrBefore(majorVersion = 33)
268     public static final int ZONE_CONFIG_CHANGE_FLAG_USER = 0x2;
269 
270     /**
271      * Zone config change caused by audio zone change.
272      * Assigned audio zone for passenger zones have changed.
273      **/
274     @AddedInOrBefore(majorVersion = 33)
275     public static final int ZONE_CONFIG_CHANGE_FLAG_AUDIO = 0x4;
276 
277     /** @hide */
278     @IntDef(flag = true, prefix = {"ZONE_CONFIG_CHANGE_FLAG_"}, value = {
279             ZONE_CONFIG_CHANGE_FLAG_DISPLAY,
280             ZONE_CONFIG_CHANGE_FLAG_USER,
281             ZONE_CONFIG_CHANGE_FLAG_AUDIO,
282     })
283     @Retention(RetentionPolicy.SOURCE)
284     @interface ZoneConfigChangeFlags {}
285 
286     /**
287      * The assignment was successful.
288      *
289      * @hide
290      */
291     @SystemApi
292     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
293             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
294     public static final int USER_ASSIGNMENT_RESULT_OK = 0;
295 
296     /**
297      * The operation has failed as the user is already assigned to other zone. If the goal is to
298      * move the user, the current zone should be unassigned first.
299      *
300      * @hide
301      */
302     @SystemApi
303     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
304             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
305     public static final int USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED = 1;
306 
307     /**
308      * The assigned user is not a {@link UserManager#isUserVisible() visible user}.
309      *
310      * @hide
311      */
312     @SystemApi
313     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
314             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
315     public static final int USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER = 2;
316 
317     /**
318      * Assigning non-current user to driver zone or un-assigning driver zone will fail with this
319      * error.
320      *
321      * @hide
322      */
323     @SystemApi
324     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
325             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
326     public static final int USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE = 3;
327 
328     /** @hide */
329     @IntDef(flag = false, prefix = {"USER_ASSIGNMENT_RESULT_"}, value = {
330             USER_ASSIGNMENT_RESULT_OK,
331             USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED,
332             USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER,
333             USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE,
334     })
335     @Retention(RetentionPolicy.SOURCE)
336     @interface UserAssignmentResult {}
337 
338     /**
339      * Invalid user ID. Zone with this user ID has no allocated user. Should have the same value
340      * with {@link UserHandle#USER_NULL}.
341      */
342     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
343             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
344     public static final @UserIdInt int INVALID_USER_ID = -10000;
345 
346     /**
347      * Listener to monitor any Occupant Zone configuration change. The configuration change can
348      * involve some displays removed or new displays added. Also it can happen when assigned user
349      * for any zone changes.
350      */
351     public interface OccupantZoneConfigChangeListener {
352 
353         /**
354          * Configuration for occupant zones has changed. Apps should re-check all
355          * occupant zone configs. This can be caused by events like user switching and
356          * display addition / removal.
357          *
358          * @param changeFlags Reason for the zone change.
359          */
360         @AddedInOrBefore(majorVersion = 33)
onOccupantZoneConfigChanged(@oneConfigChangeFlags int changeFlags)361         void onOccupantZoneConfigChanged(@ZoneConfigChangeFlags int changeFlags);
362     }
363 
364     private final DisplayManager mDisplayManager;
365     private final EventHandler mEventHandler;
366 
367     private final ICarOccupantZone mService;
368 
369     private final ICarOccupantZoneCallbackImpl mBinderCallback;
370 
371     private final CopyOnWriteArrayList<OccupantZoneConfigChangeListener> mListeners =
372             new CopyOnWriteArrayList<>();
373 
374     /**
375      * Gets an instance of the CarOccupantZoneManager.
376      *
377      * <p>Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
378      *
379      * @hide
380      */
CarOccupantZoneManager(Car car, IBinder service)381     public CarOccupantZoneManager(Car car, IBinder service) {
382         super(car);
383         mService = ICarOccupantZone.Stub.asInterface(service);
384         mBinderCallback = new ICarOccupantZoneCallbackImpl(this);
385         mDisplayManager = getContext().getSystemService(DisplayManager.class);
386         mEventHandler = new EventHandler(getEventHandler().getLooper());
387     }
388 
389     /**
390      * Returns all available occupants in the system. If no occupant zone is defined in the system
391      * or none is available at the moment, it will return empty list.
392      */
393     @NonNull
394     @AddedInOrBefore(majorVersion = 33)
getAllOccupantZones()395     public List<OccupantZoneInfo> getAllOccupantZones() {
396         try {
397             return mService.getAllOccupantZones();
398         } catch (RemoteException e) {
399             return handleRemoteExceptionFromCarService(e, Collections.emptyList());
400         }
401     }
402 
403     /**
404      * Returns all displays assigned for the given occupant zone. If no display is available for
405      * the passenger, it will return empty list.
406      */
407     @NonNull
408     @AddedInOrBefore(majorVersion = 33)
getAllDisplaysForOccupant(@onNull OccupantZoneInfo occupantZone)409     public List<Display> getAllDisplaysForOccupant(@NonNull OccupantZoneInfo occupantZone) {
410         assertNonNullOccupant(occupantZone);
411         try {
412             int[] displayIds = mService.getAllDisplaysForOccupantZone(occupantZone.zoneId);
413             ArrayList<Display> displays = new ArrayList<>(displayIds.length);
414             for (int i = 0; i < displayIds.length; i++) {
415                 // quick confidence check while getDisplay can still handle invalid display
416                 if (displayIds[i] == Display.INVALID_DISPLAY) {
417                     continue;
418                 }
419                 Display display = mDisplayManager.getDisplay(displayIds[i]);
420                 if (display != null) { // necessary as display list could have changed.
421                     displays.add(display);
422                 }
423             }
424             return displays;
425         } catch (RemoteException e) {
426             return handleRemoteExceptionFromCarService(e, Collections.emptyList());
427         }
428     }
429 
430     /**
431      * Gets the display for the occupant for the specified display type, or returns {@code null}
432      * if no display matches the requirements.
433      *
434      * @param displayType This should be a valid display type and passing
435      *                    {@link #DISPLAY_TYPE_UNKNOWN} will always lead into {@code null} return.
436      */
437     @Nullable
438     @AddedInOrBefore(majorVersion = 33)
getDisplayForOccupant(@onNull OccupantZoneInfo occupantZone, @DisplayTypeEnum int displayType)439     public Display getDisplayForOccupant(@NonNull OccupantZoneInfo occupantZone,
440             @DisplayTypeEnum int displayType) {
441         assertNonNullOccupant(occupantZone);
442         try {
443             int displayId = mService.getDisplayForOccupant(occupantZone.zoneId, displayType);
444             // quick confidence check while getDisplay can still handle invalid display
445             if (displayId == Display.INVALID_DISPLAY) {
446                 return null;
447             }
448             return mDisplayManager.getDisplay(displayId);
449         } catch (RemoteException e) {
450             return handleRemoteExceptionFromCarService(e, null);
451         }
452     }
453 
454     /**
455      * Returns the display id for the driver.
456      *
457      * <p>This method just returns the display id for the requested type. The returned display id
458      * may correspond to a private display and the client may not have access to it.
459      *
460      * @param displayType the display type
461      * @return the driver's display id or {@link Display#INVALID_DISPLAY} when no such display
462      * exists or if the driver zone does not exist.
463      *
464      * @hide
465      */
466     @SystemApi
467     @RequiresPermission(Car.ACCESS_PRIVATE_DISPLAY_ID)
468     @AddedInOrBefore(majorVersion = 33)
getDisplayIdForDriver(@isplayTypeEnum int displayType)469     public int getDisplayIdForDriver(@DisplayTypeEnum int displayType) {
470         try {
471             return mService.getDisplayIdForDriver(displayType);
472         } catch (RemoteException e) {
473             return handleRemoteExceptionFromCarService(e, Display.INVALID_DISPLAY);
474         }
475     }
476 
477     /**
478      * Gets the audio zone id for the occupant, or returns
479      * {@code CarAudioManager.INVALID_AUDIO_ZONE} if no audio zone matches the requirements.
480      * throws InvalidArgumentException if occupantZone does not exist.
481      *
482      * @hide
483      */
484     @SystemApi
485     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
486     @AddedInOrBefore(majorVersion = 33)
getAudioZoneIdForOccupant(@onNull OccupantZoneInfo occupantZone)487     public int getAudioZoneIdForOccupant(@NonNull OccupantZoneInfo occupantZone) {
488         assertNonNullOccupant(occupantZone);
489         try {
490             return mService.getAudioZoneIdForOccupant(occupantZone.zoneId);
491         } catch (RemoteException e) {
492             return handleRemoteExceptionFromCarService(e, null);
493         }
494     }
495 
496     /**
497      * Gets occupant for the audio zone id, or returns {@code null}
498      * if no audio zone matches the requirements.
499      *
500      * @hide
501      */
502     @Nullable
503     @SystemApi
504     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
505     @AddedInOrBefore(majorVersion = 33)
getOccupantForAudioZoneId(int audioZoneId)506     public OccupantZoneInfo getOccupantForAudioZoneId(int audioZoneId) {
507         try {
508             return mService.getOccupantForAudioZoneId(audioZoneId);
509         } catch (RemoteException e) {
510             return handleRemoteExceptionFromCarService(e, null);
511         }
512     }
513 
514     /**
515      * Returns assigned display type for the display. It will return {@link #DISPLAY_TYPE_UNKNOWN}
516      * if type is not specified or if display is no longer available.
517      */
518     @DisplayTypeEnum
519     @AddedInOrBefore(majorVersion = 33)
getDisplayType(@onNull Display display)520     public int getDisplayType(@NonNull Display display) {
521         assertNonNullDisplay(display);
522         try {
523             return mService.getDisplayType(display.getDisplayId());
524         } catch (RemoteException e) {
525             return handleRemoteExceptionFromCarService(e, DISPLAY_TYPE_UNKNOWN);
526         }
527     }
528 
529     /**
530      * Returns android user id assigned for the given zone. It will return
531      * {@link #INVALID_USER_ID} if user is not assigned or if zone is not available.
532      */
533     @UserIdInt
534     @AddedInOrBefore(majorVersion = 33)
getUserForOccupant(@onNull OccupantZoneInfo occupantZone)535     public int getUserForOccupant(@NonNull OccupantZoneInfo occupantZone) {
536         assertNonNullOccupant(occupantZone);
537         try {
538             return mService.getUserForOccupant(occupantZone.zoneId);
539         } catch (RemoteException e) {
540             return handleRemoteExceptionFromCarService(e, INVALID_USER_ID);
541         }
542     }
543 
544     /**
545      * Returns assigned user id for the given display id.
546      *
547      * @param displayId Should be valid display id. Passing invalid display id will lead into
548      *        getting {@link #INVALID_USER_ID} result.
549      * @return Valid user id or {@link #INVALID_USER_ID} if no user is assigned for the display.
550      */
551     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
552             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
553     @UserIdInt
getUserForDisplayId(int displayId)554     public int getUserForDisplayId(int displayId) {
555         try {
556             return mService.getUserForDisplayId(displayId);
557         } catch (RemoteException e) {
558             return handleRemoteExceptionFromCarService(e, INVALID_USER_ID);
559         }
560     }
561 
562     /**
563      * Returns the info for the occupant zone that has the display identified by the given
564      * {@code displayId}.
565      *
566      * @param displayId Should be valid display id. Passing in invalid display id will lead into
567      *        getting {@code null} occupant zone info result.
568      * @return Occupant zone info or {@code null} if no occupant zone is found which has the given
569      * display.
570      *
571      * @hide
572      */
573     @SystemApi
574     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
575             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
576     @Nullable
getOccupantZoneForDisplayId(int displayId)577     public OccupantZoneInfo getOccupantZoneForDisplayId(int displayId) {
578         try {
579             return mService.getOccupantZoneForDisplayId(displayId);
580         } catch (RemoteException e) {
581             return handleRemoteExceptionFromCarService(e, null);
582         }
583     }
584 
585     /**
586      * Assigns the given profile {@code userId} to the {@code occupantZone}. Returns true when the
587      * request succeeds.
588      *
589      * <p>Note that only non-driver zone can be assigned with this call. Calling this for driver
590      * zone will lead into {@code IllegalArgumentException}.
591      *
592      * @param occupantZone Zone to assign user.
593      * @param userId       profile user id to assign. Passing {@link #INVALID_USER_ID} leads into
594      *                     removing the current user assignment.
595      * @return true if the request succeeds or if the user is already assigned to the zone.
596      * @deprecated Use {@link #assignVisibleUserToOccupantZone(OccupantZoneInfo, UserHandle)}
597      * instead.
598      *
599      * @hide
600      */
601     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
602             Car.PERMISSION_MANAGE_OCCUPANT_ZONE})
603     @AddedInOrBefore(majorVersion = 33)
604     @Deprecated
assignProfileUserToOccupantZone(@onNull OccupantZoneInfo occupantZone, @UserIdInt int userId)605     public boolean assignProfileUserToOccupantZone(@NonNull OccupantZoneInfo occupantZone,
606             @UserIdInt int userId) {
607         assertNonNullOccupant(occupantZone);
608         try {
609             return mService.assignProfileUserToOccupantZone(occupantZone.zoneId, userId);
610         } catch (RemoteException e) {
611             return handleRemoteExceptionFromCarService(e, false);
612         }
613     }
614 
615     /**
616      * Assign a visible user, which gets {@code true} from ({@link UserManager#isUserVisible()},
617      * to the specified occupant zone.
618      *
619      * <p>This API handles occupant zone change.
620      *
621      * <p>This API can take a long time, so it is recommended to call this from non-main thread.
622      *
623      * <p> The return value is {@link #USER_ASSIGNMENT_RESULT_OK} when the assignment succeeds or if
624      * the user is already allocated to the zone. Note that new error code can be added in the
625      * future. For now, following error codes will be returned for a Failure:
626      * <ul>
627      *   <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER} for non-visible user.
628      *   <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED} if the user is already assigned
629      *       to other zone. New error code can be added in future.
630      *   <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE} if non-current user is assigned to the
631      *       driver zone.
632      * </ul>
633      *
634      * <p>The system requires one user to  have one zone and moving user from one zone to another
635      * requires unassigning the zone using {@link #unassignOccupantZone(OccupantZoneInfo)}
636      * first.
637      *
638      * @param occupantZone the occupant zone to change user allocation
639      * @param user the user to allocate. {@code null} user removes the allocation for the zone
640      *             {@link UserHandle#CURRENT} will assign the current user to the zone
641      * @return Check the above
642      *
643      * @hide
644      */
645     @SystemApi
646     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
647             Car.PERMISSION_MANAGE_OCCUPANT_ZONE})
648     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
649             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
650     @UserAssignmentResult
assignVisibleUserToOccupantZone(@onNull OccupantZoneInfo occupantZone, @NonNull UserHandle user)651     public int assignVisibleUserToOccupantZone(@NonNull OccupantZoneInfo occupantZone,
652             @NonNull UserHandle user) {
653         assertPlatformVersionAtLeastU();
654         assertNonNullOccupant(occupantZone);
655         try {
656             return mService.assignVisibleUserToOccupantZone(occupantZone.zoneId, user);
657         } catch (RemoteException e) {
658             // Return any error code if car service is gone.
659             return handleRemoteExceptionFromCarService(e,
660                     USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED);
661         }
662     }
663 
664     /**
665      * Un-assign user from the specified occupant zone. The zone will return
666      * {@link #INVALID_USER_ID} for {@link #getUserForOccupant(OccupantZoneInfo)} after this call.
667      *
668      * @param occupantZone Zone to unassign.
669      * @return {@link #USER_ASSIGNMENT_RESULT_OK} if the zone is unassigned by the call or was
670      * already unassigned. Error code of {@link #USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE}
671      * will be returned if driver zone is asked.
672      *
673      * @hide
674      */
675     @SystemApi
676     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
677             Car.PERMISSION_MANAGE_OCCUPANT_ZONE})
678     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
679             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
680     @UserAssignmentResult
unassignOccupantZone(@onNull OccupantZoneInfo occupantZone)681     public int unassignOccupantZone(@NonNull OccupantZoneInfo occupantZone) {
682         assertPlatformVersionAtLeastU();
683         try {
684             return mService.unassignOccupantZone(occupantZone.zoneId);
685         } catch (RemoteException e) {
686             // Return any error code if car service is gone.
687             return handleRemoteExceptionFromCarService(e, USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE);
688         }
689     }
690 
assertNonNullOccupant(OccupantZoneInfo occupantZone)691     private void assertNonNullOccupant(OccupantZoneInfo occupantZone) {
692         if (occupantZone == null) {
693             throw new IllegalArgumentException("null OccupantZoneInfo");
694         }
695     }
696 
assertNonNullDisplay(Display display)697     private void assertNonNullDisplay(Display display) {
698         if (display == null) {
699             throw new IllegalArgumentException("null Display");
700         }
701     }
702 
703     /**
704      * Registers the listener for occupant zone config change. Registering multiple listeners are
705      * allowed.
706      */
707     @AddedInOrBefore(majorVersion = 33)
registerOccupantZoneConfigChangeListener( @onNull OccupantZoneConfigChangeListener listener)708     public void registerOccupantZoneConfigChangeListener(
709             @NonNull OccupantZoneConfigChangeListener listener) {
710         if (mListeners.addIfAbsent(listener)) {
711             if (mListeners.size() == 1) {
712                 try {
713                     mService.registerCallback(mBinderCallback);
714                 } catch (RemoteException e) {
715                     handleRemoteExceptionFromCarService(e);
716                 }
717             }
718         }
719     }
720 
721     /**
722      * Unregisters the listener. Listeners not registered before will be ignored.
723      */
724     @AddedInOrBefore(majorVersion = 33)
unregisterOccupantZoneConfigChangeListener( @onNull OccupantZoneConfigChangeListener listener)725     public void unregisterOccupantZoneConfigChangeListener(
726             @NonNull OccupantZoneConfigChangeListener listener) {
727         if (mListeners.remove(listener)) {
728             if (mListeners.size() == 0) {
729                 try {
730                     mService.unregisterCallback(mBinderCallback);
731                 } catch (RemoteException ignored) {
732                     // ignore for unregistering
733                 }
734             }
735         }
736     }
737 
738     /**
739      * Returns {@link OccupantZoneInfo} for the calling process's android user.
740      * It will return {@code null} if there is no occupant zone assigned for the user.
741      *
742      * <p>When there is no occupant zone allocated for the user, most likely the user is not allowed
743      * to run Activity or play audio, which are the main use cases to get the zone. So apps should
744      * not try such tasks when {@code null} {@code OccupantZoneInfo} is returned. There can be an
745      * exception for system user running under
746      * {@link UserManager#isHeadlessSystemUserMode() Headless System User Mode}: The system user
747      * apps may show UI even if there is no zone allocated.
748      */
749     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
750             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
751     @Nullable
getMyOccupantZone()752     public OccupantZoneInfo getMyOccupantZone() {
753         try {
754             return mService.getMyOccupantZone();
755         } catch (RemoteException e) {
756             return handleRemoteExceptionFromCarService(e, null);
757         }
758     }
759 
760     /**
761      * Returns {@code OccupantZoneInfo} associated with the given {@code UserHandle}. In the case
762      * that the user is associated with multiple zones, this API returns the first matched zone.
763      *
764      * @param user The user to find.
765      * @return Matching occupant zone or {@code null} if the user is not assigned or user has a
766      * userId of {@code UserHandle#USER_NULL}.
767      */
768     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
769             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
770     @SuppressWarnings("UserHandle")
771     @Nullable
getOccupantZoneForUser(@onNull UserHandle user)772     public OccupantZoneInfo getOccupantZoneForUser(@NonNull UserHandle user) {
773         try {
774             return mService.getOccupantZoneForUser(user);
775         } catch (RemoteException e) {
776             return handleRemoteExceptionFromCarService(e, /* returnValue= */null);
777         }
778     }
779 
780     /**
781      * Finds {@code OccupantZoneInfo} for the given occupant type and seat.
782      * <p>For{@link #OCCUPANT_TYPE_DRIVER} and {@link #OCCUPANT_TYPE_FRONT_PASSENGER}, {@code seat}
783      * argument will be ignored.
784      *
785      * @param occupantType should be one of {@link #OCCUPANT_TYPE_DRIVER},
786      *                     {@link #OCCUPANT_TYPE_FRONT_PASSENGER},
787      *                     {@link #OCCUPANT_TYPE_FRONT_PASSENGER}
788      * @param seat         Seat of the occupant. This is necessary for
789      *                     {@link #OCCUPANT_TYPE_REAR_PASSENGER}.
790      * @return Matching occupant zone or {@code null} if such zone does not exist.
791      */
792     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
793             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
794     @Nullable
getOccupantZone(@ccupantTypeEnum int occupantType, @VehicleAreaSeat.Enum int seat)795     public OccupantZoneInfo getOccupantZone(@OccupantTypeEnum int occupantType,
796             @VehicleAreaSeat.Enum int seat) {
797         try {
798             return mService.getOccupantZone(occupantType, seat);
799         } catch (RemoteException e) {
800             return handleRemoteExceptionFromCarService(e, null);
801         }
802 
803     }
804 
805     /**
806      * Returns {@code true} if the system has a driver zone. It will return false for system with
807      * only passenger zones.
808      *
809      * <p> Note that at least one zone must be present and following system configurations are
810      * possible:
811      * <ul>
812      *     <li>One driver zone only.
813      *     <li>One driver zone with at least one passenger zone.
814      *     <li>At least one passenger zone.
815      * </ul>
816      */
817     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
818             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
hasDriverZone()819     public boolean hasDriverZone() {
820         try {
821             return mService.hasDriverZone();
822         } catch (RemoteException e) {
823             return handleRemoteExceptionFromCarService(e, false);
824         }
825     }
826 
827     /**
828      * Returns {@code true} if the system has front or rear passenger zones. Check
829      * {@link #hasDriverZone()} for possible system configurations.
830      */
831     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
832             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
hasPassengerZones()833     public boolean hasPassengerZones() {
834         try {
835             return mService.hasPassengerZones();
836         } catch (RemoteException e) {
837             return handleRemoteExceptionFromCarService(e, false);
838         }
839     }
840 
handleOnOccupantZoneConfigChanged(int flags)841     private void handleOnOccupantZoneConfigChanged(int flags) {
842         for (OccupantZoneConfigChangeListener listener : mListeners) {
843             listener.onOccupantZoneConfigChanged(flags);
844         }
845     }
846 
847     private final class EventHandler extends Handler {
848         private static final int MSG_ZONE_CHANGE = 1;
849 
EventHandler(Looper looper)850         private EventHandler(Looper looper) {
851             super(looper);
852         }
853 
854         @Override
handleMessage(Message msg)855         public void handleMessage(Message msg) {
856             switch (msg.what) {
857                 case MSG_ZONE_CHANGE:
858                     handleOnOccupantZoneConfigChanged(msg.arg1);
859                     break;
860                 default:
861                     Log.e(TAG, "Unknown msg not handdled:" + msg.what);
862                     break;
863             }
864         }
865 
dispatchOnOccupantZoneConfigChanged(int flags)866         private void dispatchOnOccupantZoneConfigChanged(int flags) {
867             sendMessage(obtainMessage(MSG_ZONE_CHANGE, flags, 0));
868         }
869     }
870 
871     private static class ICarOccupantZoneCallbackImpl extends ICarOccupantZoneCallback.Stub {
872         private final WeakReference<CarOccupantZoneManager> mManager;
873 
ICarOccupantZoneCallbackImpl(CarOccupantZoneManager manager)874         private ICarOccupantZoneCallbackImpl(CarOccupantZoneManager manager) {
875             mManager = new WeakReference<>(manager);
876         }
877 
878         @Override
onOccupantZoneConfigChanged(int flags)879         public void onOccupantZoneConfigChanged(int flags) {
880             CarOccupantZoneManager manager = mManager.get();
881             if (manager != null) {
882                 manager.mEventHandler.dispatchOnOccupantZoneConfigChanged(flags);
883             }
884         }
885     }
886 
887     /** @hide */
888     @Override
889     @AddedInOrBefore(majorVersion = 33)
onCarDisconnected()890     public void onCarDisconnected() {
891         // nothing to do
892     }
893 
894     /**
895      * Returns the supported input types for the occupant zone info and display type passed as
896      * the argument.
897      *
898      * <p>It returns an empty list if the input type is unknown. Starting in Android
899      * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, all associated occupant zones and
900      * display types in {@code config_occupant_display_mapping} must define at least one input type.
901      *
902      * <p>If the display doesn't have any input type associated, then it should return a list
903      * containing {@link android.car.input.CarInputManager#INPUT_TYPE_NONE} only.
904      *
905      * <p>This is the list of all available input types this method may return:
906      * <ul>
907      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_ROTARY_NAVIGATION}</li>
908      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_ROTARY_VOLUME}</li>
909      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_DPAD_KEYS}</li>
910      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_NAVIGATE_KEYS}</li>
911      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_SYSTEM_NAVIGATE_KEYS}</li>
912      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_CUSTOM_INPUT_EVENT}</li>
913      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_TOUCH_SCREEN}</li>
914      *   <li>{@link android.car.input.CarInputManager#INPUT_TYPE_NONE}</li>
915      * </ul>
916      *
917      * @param occupantZoneInfo the occupant zone info of the supported input types to find
918      * @param displayType      the display type of the supported input types to find
919      * @return the supported input types for the occupant zone info and display type passed in as
920      * the argument (see the full list of supported input types in the above)
921      */
922     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
923             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
924     @NonNull
getSupportedInputTypes(@onNull OccupantZoneInfo occupantZoneInfo, @DisplayTypeEnum int displayType)925     public List<Integer> getSupportedInputTypes(@NonNull OccupantZoneInfo occupantZoneInfo,
926             @DisplayTypeEnum int displayType) {
927         assertNonNullOccupant(occupantZoneInfo);
928         List<Integer> inputTypes;
929         try {
930             int[] ints = mService.getSupportedInputTypes(occupantZoneInfo.zoneId, displayType);
931             inputTypes = Lists.asImmutableList(ints);
932         } catch (RemoteException e) {
933             inputTypes = handleRemoteExceptionFromCarService(e, Collections.emptyList());
934         }
935         return inputTypes;
936     }
937 }
938