1 /*
2  * Copyright (C) 2022 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.car;
18 
19 import static android.car.CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED;
20 import static android.car.CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE;
21 import static android.car.CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.Mockito.when;
26 
27 import android.car.CarOccupantZoneManager;
28 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
29 import android.car.VehicleAreaSeat;
30 import android.os.UserHandle;
31 import android.util.SparseIntArray;
32 import android.view.Display;
33 
34 import org.mockito.Mock;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * This provides simple mock for {@link com.android.car.CarOccupantZoneService}.
41  *
42  * <p>For simplicity, only main display is supported and display id and zone id will be the same.
43  */
44 public final class OccupantZoneHelper {
45 
46     public final CarOccupantZoneManager.OccupantZoneInfo zoneDriverLHD = new OccupantZoneInfo(0,
47             CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
48             VehicleAreaSeat.SEAT_ROW_1_LEFT);
49     public final OccupantZoneInfo zoneFrontPassengerLHD = new OccupantZoneInfo(1,
50             CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER,
51             VehicleAreaSeat.SEAT_ROW_1_RIGHT);
52     public final OccupantZoneInfo zoneRearLeft = new OccupantZoneInfo(2,
53             CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
54             VehicleAreaSeat.SEAT_ROW_2_LEFT);
55     public final OccupantZoneInfo zoneRearRight = new OccupantZoneInfo(3,
56             CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
57             VehicleAreaSeat.SEAT_ROW_2_RIGHT);
58     public final OccupantZoneInfo zoneRearCenter = new OccupantZoneInfo(4,
59             CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
60             VehicleAreaSeat.SEAT_ROW_2_CENTER);
61 
62     private final List<CarOccupantZoneManager.OccupantZoneInfo> mZones = new ArrayList<>();
63     private final SparseIntArray mZoneToUser = new SparseIntArray(); // key: zone, value: user id
64 
setUpOccupantZones(@ock CarOccupantZoneService service, boolean hasDriver, boolean hasFrontPassenger, int numRearPassengers)65     public void setUpOccupantZones(@Mock CarOccupantZoneService service, boolean hasDriver,
66             boolean hasFrontPassenger, int numRearPassengers) {
67         if (hasDriver) {
68             mZones.add(zoneDriverLHD);
69         }
70         if (hasFrontPassenger) {
71             mZones.add(zoneFrontPassengerLHD);
72         }
73         if (numRearPassengers >= 1) {
74             mZones.add(zoneRearLeft);
75         }
76         if (numRearPassengers == 2) {
77             mZones.add(zoneRearRight);
78         }
79         if (numRearPassengers == 3) {
80             mZones.add(zoneRearCenter);
81         }
82         if (numRearPassengers > 3) {
83             throw new IllegalArgumentException("Supports only up to 3 rear passengers");
84         }
85 
86         // All following mocking are dynamic based on mZones and other params.
87         when(service.getAllOccupantZones()).thenAnswer(inv -> mZones);
88         when(service.hasDriverZone()).thenReturn(hasDriver);
89         when(service.getNumberOfPassengerZones()).thenReturn(
90                 numRearPassengers + (hasFrontPassenger ? 1 : 0));
91         when(service.getOccupantZone(anyInt(), anyInt())).thenAnswer(
92                 inv -> {
93                     int type = (int) inv.getArgument(0);
94                     int seat = (int) inv.getArgument(1);
95                     if (type == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) {
96                         if (hasDriver) {
97                             return zoneDriverLHD;
98                         } else {
99                             return null;
100                         }
101                     }
102                     for (OccupantZoneInfo zone : mZones) {
103                         if (zone.occupantType == type && zone.seat == seat) {
104                             return zone;
105                         }
106                     }
107                     return null;
108                 }
109         );
110         when(service.getDisplayForOccupant(anyInt(), anyInt())).thenAnswer(
111                 inv -> {
112                     int zoneId = (int) inv.getArgument(0);
113                     int displayType = (int) inv.getArgument(1);
114                     if (displayType != CarOccupantZoneManager.DISPLAY_TYPE_MAIN) {
115                         return Display.INVALID_DISPLAY;
116                     }
117                     for (OccupantZoneInfo zone : mZones) {
118                         if (zone.zoneId == zoneId) {
119                             return zoneId; // display id and zone id the same
120                         }
121                     }
122                     return Display.INVALID_DISPLAY;
123                 }
124         );
125         when(service.assignVisibleUserToOccupantZone(anyInt(), any())).thenAnswer(
126                 inv -> {
127                     int zoneId = (int) inv.getArgument(0);
128                     UserHandle user = (UserHandle) inv.getArgument(1);
129                     int userId = user.getIdentifier();
130                     if (!hasZone(zoneId)) {
131                         return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED;
132                     }
133                     if (mZones.contains(zoneDriverLHD) && zoneDriverLHD.zoneId == zoneId) {
134                         return USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE;
135                     }
136                     int existingIndex = mZoneToUser.indexOfValue(userId);
137                     if (existingIndex >= 0) {
138                         int preassignedZone = mZoneToUser.keyAt(existingIndex);
139                         if (preassignedZone == zoneId) {
140                             return USER_ASSIGNMENT_RESULT_OK;
141                         } else {
142                             return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED;
143                         }
144                     }
145                     mZoneToUser.append(zoneId, userId);
146                     return USER_ASSIGNMENT_RESULT_OK;
147                 }
148         );
149         when(service.unassignOccupantZone(anyInt())).thenAnswer(
150                 inv -> {
151                     int zoneId = (int) inv.getArgument(0);
152                     if (!hasZone(zoneId)) {
153                         return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED;
154                     }
155                     if (mZones.contains(zoneDriverLHD) && zoneDriverLHD.zoneId == zoneId) {
156                         return USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE;
157                     }
158                     int existingIndex = mZoneToUser.indexOfKey(zoneId);
159                     if (existingIndex >= 0) {
160                         mZoneToUser.delete(zoneId);
161                     }
162                     return USER_ASSIGNMENT_RESULT_OK;
163                 }
164         );
165         when(service.getOccupantZoneIdForUserId(anyInt())).thenAnswer(
166                 inv -> {
167                     int userId = (int) inv.getArgument(0);
168                     for (int i = 0; i < mZoneToUser.size(); i++) {
169                         int user = mZoneToUser.valueAt(i);
170                         int zoneId = mZoneToUser.keyAt(i);
171                         if (user == userId) {
172                             return zoneId;
173                         }
174                     }
175                     return OccupantZoneInfo.INVALID_ZONE_ID;
176                 }
177         );
178         when(service.getUserForOccupant(anyInt())).thenAnswer(
179                 inv -> {
180                     int zoneId = (int) inv.getArgument(0);
181                     int existingIndex = mZoneToUser.indexOfKey(zoneId);
182                     if (existingIndex >= 0) {
183                         return mZoneToUser.valueAt(existingIndex);
184                     }
185                     return CarOccupantZoneManager.INVALID_USER_ID;
186                 }
187         );
188     }
189 
getDisplayId(OccupantZoneInfo zone)190     public int getDisplayId(OccupantZoneInfo zone) {
191         return zone.zoneId; // For simplicity, use the same id with zone.
192     }
193 
hasZone(int zoneId)194     private boolean hasZone(int zoneId) {
195         for (OccupantZoneInfo zone : mZones) {
196             if (zoneId == zone.zoneId) {
197                 return true;
198             }
199         }
200         return false;
201     }
202 }
203