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.CarOccupantZoneManager.DISPLAY_TYPE_MAIN;
20 import static android.car.CarOccupantZoneManager.INVALID_USER_ID;
21 import static android.car.CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER;
22 import static android.car.CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER;
23 import static android.car.CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER;
24 import static android.car.CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE;
25 import static android.car.VehicleAreaSeat.SEAT_ROW_1_CENTER;
26 import static android.car.VehicleAreaSeat.SEAT_ROW_1_LEFT;
27 import static android.car.VehicleAreaSeat.SEAT_ROW_1_RIGHT;
28 import static android.car.VehicleAreaSeat.SEAT_ROW_2_CENTER;
29 import static android.car.VehicleAreaSeat.SEAT_ROW_2_LEFT;
30 import static android.car.VehicleAreaSeat.SEAT_ROW_2_RIGHT;
31 import static android.car.VehicleAreaSeat.SEAT_ROW_3_CENTER;
32 import static android.car.VehicleAreaSeat.SEAT_ROW_3_LEFT;
33 import static android.car.VehicleAreaSeat.SEAT_ROW_3_RIGHT;
34 import static android.view.Display.INVALID_DISPLAY;
35 
36 import android.annotation.Nullable;
37 import android.annotation.UserIdInt;
38 import android.car.Car;
39 import android.car.CarOccupantZoneManager;
40 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
41 import android.car.hardware.power.CarPowerManager;
42 import android.car.user.CarUserManager;
43 import android.car.user.CarUserManager.UserLifecycleListener;
44 import android.car.user.UserLifecycleEventFilter;
45 import android.content.Context;
46 import android.util.Pair;
47 import android.util.Slog;
48 import android.view.Display;
49 
50 import androidx.annotation.VisibleForTesting;
51 
52 import com.android.systemui.R;
53 import com.android.systemui.car.CarServiceProvider;
54 
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.concurrent.Executor;
59 
60 import javax.inject.Inject;
61 
62 @UserPickerScope
63 final class CarServiceMediator {
64     private static final String TAG = CarServiceMediator.class.getSimpleName();
65 
66     // These are needed when getting the string of the seat for users.
67     private String mSeatDriver;
68     private String mSeatFront;
69     private String mSeatRear;
70     private String mSeatLeftSide;
71     private String mSeatCenterSide;
72     private String mSeatRightSide;
73 
74     private CarOccupantZoneManager mCarOccupantZoneManager;
75     private CarUserManager mCarUserManager;
76     private CarPowerManager mCarPowerManager;
77 
78     private final Context mContext;
79     private final CarServiceProvider mCarServiceProvider;
80     private final Map<UserLifecycleListener, Pair<Executor, UserLifecycleEventFilter>>
81             mUserLifecycleListeners = new HashMap<>();
82 
83     private final CarServiceProvider.CarServiceOnConnectedListener mServiceOnConnectedListener =
84             new CarServiceProvider.CarServiceOnConnectedListener() {
85                 @Override
86                 public void onConnected(Car car) {
87                     onConnect(car);
88                 }
89             };
90 
91     @Inject
CarServiceMediator(Context context, CarServiceProvider carServiceProvider)92     CarServiceMediator(Context context, CarServiceProvider carServiceProvider) {
93         mContext = context.getApplicationContext();
94         mCarServiceProvider = carServiceProvider;
95         mCarServiceProvider.addListener(mServiceOnConnectedListener);
96 
97         updateTexts();
98     }
99 
100     @VisibleForTesting
onConnect(Car car)101     void onConnect(Car car) {
102         mCarOccupantZoneManager = car.getCarManager(CarOccupantZoneManager.class);
103         mCarUserManager = car.getCarManager(CarUserManager.class);
104         mCarPowerManager = car.getCarManager(CarPowerManager.class);
105         //re-register listeners in case of CarService crash and recreation
106         if (mCarUserManager != null) {
107             for (UserLifecycleListener listener : mUserLifecycleListeners.keySet()) {
108                 mCarUserManager.addListener(mUserLifecycleListeners.get(listener).first,
109                         mUserLifecycleListeners.get(listener).second, listener);
110             }
111         }
112     }
113 
updateTexts()114     void updateTexts() {
115         mSeatDriver = mContext.getString(R.string.seat_driver);
116         mSeatFront = mContext.getString(R.string.seat_front);
117         mSeatRear = mContext.getString(R.string.seat_rear);
118         mSeatLeftSide = mContext.getString(R.string.seat_left_side);
119         mSeatCenterSide = mContext.getString(R.string.seat_center_side);
120         mSeatRightSide = mContext.getString(R.string.seat_right_side);
121     }
122 
registerUserChangeEventsListener(Executor receiver, UserLifecycleEventFilter filter, UserLifecycleListener listener)123     void registerUserChangeEventsListener(Executor receiver, UserLifecycleEventFilter filter,
124             UserLifecycleListener listener) {
125         mUserLifecycleListeners.put(listener, new Pair<>(receiver, filter));
126         if (mCarUserManager != null) {
127             mCarServiceProvider.addListener(car -> {
128                 mCarUserManager.addListener(receiver, filter, listener);
129             });
130         }
131     }
132 
onDestroy()133     void onDestroy() {
134         for (UserLifecycleListener listener : mUserLifecycleListeners.keySet()) {
135             mCarUserManager.removeListener(listener);
136         }
137         mUserLifecycleListeners.clear();
138         mCarServiceProvider.removeListener(mServiceOnConnectedListener);
139     }
140 
141     @Nullable
getCarUserManager()142     CarUserManager getCarUserManager() {
143         return mCarUserManager;
144     }
145 
146     @Nullable
getOccupantZoneForDisplayId(int displayId)147     OccupantZoneInfo getOccupantZoneForDisplayId(int displayId) {
148         if (mCarOccupantZoneManager == null || displayId == INVALID_DISPLAY) {
149             Slog.w(TAG, "Cannot get occupant zone manager or has been requested for invalid"
150                     + " display.");
151             return null;
152         }
153 
154         List<OccupantZoneInfo> occupantZoneInfos = mCarOccupantZoneManager.getAllOccupantZones();
155         for (int index = 0; index < occupantZoneInfos.size(); index++) {
156             CarOccupantZoneManager.OccupantZoneInfo occupantZoneInfo = occupantZoneInfos.get(index);
157             List<Display> displays = mCarOccupantZoneManager.getAllDisplaysForOccupant(
158                     occupantZoneInfo);
159             for (int displayIndex = 0; displayIndex < displays.size(); displayIndex++) {
160                 if (displays.get(displayIndex).getDisplayId() == displayId) {
161                     return occupantZoneInfo;
162                 }
163             }
164         }
165         return null;
166     }
167 
getDisplayIdForUser(@serIdInt int userId)168     int getDisplayIdForUser(@UserIdInt int userId) {
169         if (mCarOccupantZoneManager == null) {
170             Slog.w(TAG, "Cannot get occupant zone manager.");
171             return INVALID_DISPLAY;
172         }
173 
174         List<OccupantZoneInfo> occupantZoneInfos = mCarOccupantZoneManager.getAllOccupantZones();
175         for (int i = 0; i < occupantZoneInfos.size(); i++) {
176             OccupantZoneInfo zoneInfo = occupantZoneInfos.get(i);
177             int zoneUserId = mCarOccupantZoneManager.getUserForOccupant(zoneInfo);
178             if (zoneUserId == userId) {
179                 Display d = mCarOccupantZoneManager.getDisplayForOccupant(zoneInfo,
180                         DISPLAY_TYPE_MAIN);
181                 return d != null ? d.getDisplayId() : INVALID_DISPLAY;
182             }
183         }
184         return INVALID_DISPLAY;
185     }
186 
getUserForDisplay(int displayId)187     int getUserForDisplay(int displayId) {
188         if (mCarOccupantZoneManager == null) {
189             Slog.w(TAG, "Cannot get occupant zone manager.");
190             return INVALID_USER_ID;
191         }
192 
193         return mCarOccupantZoneManager.getUserForDisplayId(displayId);
194     }
195 
unassignOccupantZoneForDisplay(int displayId)196     int unassignOccupantZoneForDisplay(int displayId) {
197         OccupantZoneInfo zoneInfo = getOccupantZoneForDisplayId(displayId);
198         if (zoneInfo == null) {
199             // Return any error code for this situation.
200             Slog.w(TAG, "Cannot find occupant zone info associated with display " + displayId);
201             return USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE;
202         }
203         return mCarOccupantZoneManager.unassignOccupantZone(zoneInfo);
204     }
205 
getSeatString(int displayId)206     String getSeatString(int displayId) {
207         String seatString = "";
208         OccupantZoneInfo zoneInfo = getOccupantZoneForDisplayId(displayId);
209         if (zoneInfo == null) {
210             Slog.w(TAG, "Cannot find occupant zone info associated with display " + displayId);
211             return seatString;
212         }
213 
214         switch (zoneInfo.occupantType) {
215             case OCCUPANT_TYPE_FRONT_PASSENGER:
216                 return mSeatFront + " " + getSeatSideString(zoneInfo.seat);
217             case OCCUPANT_TYPE_REAR_PASSENGER:
218                 return mSeatRear + " " + getSeatSideString(zoneInfo.seat);
219             case OCCUPANT_TYPE_DRIVER:
220                 return mSeatDriver;
221         }
222         return seatString;
223     }
224 
getSeatSideString(int seat)225     private String getSeatSideString(int seat) {
226         switch (seat) {
227             case SEAT_ROW_1_LEFT:
228             case SEAT_ROW_2_LEFT:
229             case SEAT_ROW_3_LEFT:
230                 return mSeatLeftSide;
231             case SEAT_ROW_1_CENTER:
232             case SEAT_ROW_2_CENTER:
233             case SEAT_ROW_3_CENTER:
234                 return mSeatCenterSide;
235             case SEAT_ROW_1_RIGHT:
236             case SEAT_ROW_2_RIGHT:
237             case SEAT_ROW_3_RIGHT:
238                 return mSeatRightSide;
239             default:
240                 return "";
241         }
242     }
243 
screenOffDisplay(int displayId)244     void screenOffDisplay(int displayId) {
245         if (mCarPowerManager == null) {
246             Slog.e(TAG, "Cannot get power manager.");
247             return;
248         }
249         mCarPowerManager.setDisplayPowerState(displayId, /* enable= */ false);
250     }
251 }
252