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 // Default driver user ID is 10. 65 private int mDriverUserId = 10; 66 67 /** 68 * Sets up occupant zones. 69 * 70 * @param service The mocked service 71 * @param hasDriver Does it have a driver 72 * @param hasFrontPassenger Does the front passenger exist 73 * @param numRearPassengers number of rear passengers 74 */ setUpOccupantZones(@ock CarOccupantZoneService service, boolean hasDriver, boolean hasFrontPassenger, int numRearPassengers)75 public void setUpOccupantZones(@Mock CarOccupantZoneService service, boolean hasDriver, 76 boolean hasFrontPassenger, int numRearPassengers) { 77 if (hasDriver) { 78 mZones.add(zoneDriverLHD); 79 } 80 if (hasFrontPassenger) { 81 mZones.add(zoneFrontPassengerLHD); 82 } 83 if (numRearPassengers >= 1) { 84 mZones.add(zoneRearLeft); 85 } 86 if (numRearPassengers == 2) { 87 mZones.add(zoneRearRight); 88 } 89 if (numRearPassengers == 3) { 90 mZones.add(zoneRearCenter); 91 } 92 if (numRearPassengers > 3) { 93 throw new IllegalArgumentException("Supports only up to 3 rear passengers"); 94 } 95 96 // All following mocking are dynamic based on mZones and other params. 97 when(service.getAllOccupantZones()).thenAnswer(inv -> mZones); 98 when(service.hasDriverZone()).thenReturn(hasDriver); 99 when(service.getNumberOfPassengerZones()).thenReturn( 100 numRearPassengers + (hasFrontPassenger ? 1 : 0)); 101 when(service.getOccupantZone(anyInt(), anyInt())).thenAnswer( 102 inv -> { 103 int type = (int) inv.getArgument(0); 104 int seat = (int) inv.getArgument(1); 105 if (type == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 106 if (hasDriver) { 107 return zoneDriverLHD; 108 } else { 109 return null; 110 } 111 } 112 for (OccupantZoneInfo zone : mZones) { 113 if (zone.occupantType == type && zone.seat == seat) { 114 return zone; 115 } 116 } 117 return null; 118 } 119 ); 120 when(service.getDisplayForOccupant(anyInt(), anyInt())).thenAnswer( 121 inv -> { 122 int zoneId = (int) inv.getArgument(0); 123 int displayType = (int) inv.getArgument(1); 124 if (displayType != CarOccupantZoneManager.DISPLAY_TYPE_MAIN) { 125 return Display.INVALID_DISPLAY; 126 } 127 for (OccupantZoneInfo zone : mZones) { 128 if (zone.zoneId == zoneId) { 129 return zoneId; // display id and zone id the same 130 } 131 } 132 return Display.INVALID_DISPLAY; 133 } 134 ); 135 when(service.assignVisibleUserToOccupantZone(anyInt(), any())).thenAnswer( 136 inv -> { 137 int zoneId = (int) inv.getArgument(0); 138 UserHandle user = (UserHandle) inv.getArgument(1); 139 int userId = user.getIdentifier(); 140 if (!hasZone(zoneId)) { 141 return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED; 142 } 143 if (mZones.contains(zoneDriverLHD) && zoneDriverLHD.zoneId == zoneId) { 144 return USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE; 145 } 146 int existingIndex = mZoneToUser.indexOfValue(userId); 147 if (existingIndex >= 0) { 148 int preassignedZone = mZoneToUser.keyAt(existingIndex); 149 if (preassignedZone == zoneId) { 150 return USER_ASSIGNMENT_RESULT_OK; 151 } else { 152 return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED; 153 } 154 } 155 mZoneToUser.append(zoneId, userId); 156 return USER_ASSIGNMENT_RESULT_OK; 157 } 158 ); 159 when(service.unassignOccupantZone(anyInt())).thenAnswer( 160 inv -> { 161 int zoneId = (int) inv.getArgument(0); 162 if (!hasZone(zoneId)) { 163 return USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED; 164 } 165 if (mZones.contains(zoneDriverLHD) && zoneDriverLHD.zoneId == zoneId) { 166 return USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE; 167 } 168 int existingIndex = mZoneToUser.indexOfKey(zoneId); 169 if (existingIndex >= 0) { 170 mZoneToUser.delete(zoneId); 171 } 172 return USER_ASSIGNMENT_RESULT_OK; 173 } 174 ); 175 when(service.getOccupantZoneIdForUserId(anyInt())).thenAnswer( 176 inv -> { 177 int userId = (int) inv.getArgument(0); 178 for (int i = 0; i < mZoneToUser.size(); i++) { 179 int user = mZoneToUser.valueAt(i); 180 int zoneId = mZoneToUser.keyAt(i); 181 if (user == userId) { 182 return zoneId; 183 } 184 } 185 return OccupantZoneInfo.INVALID_ZONE_ID; 186 } 187 ); 188 when(service.getUserForOccupant(anyInt())).thenAnswer( 189 inv -> { 190 int zoneId = (int) inv.getArgument(0); 191 if (mZones.contains(zoneDriverLHD) && zoneDriverLHD.zoneId == zoneId) { 192 return mDriverUserId; 193 } 194 int existingIndex = mZoneToUser.indexOfKey(zoneId); 195 if (existingIndex >= 0) { 196 return mZoneToUser.valueAt(existingIndex); 197 } 198 return CarOccupantZoneManager.INVALID_USER_ID; 199 } 200 ); 201 } 202 203 /** 204 * Gets the display Id 205 * @param zone The OccupantZoneInfo 206 * @return DisplayId 207 */ getDisplayId(OccupantZoneInfo zone)208 public int getDisplayId(OccupantZoneInfo zone) { 209 return zone.zoneId; // For simplicity, use the same id with zone. 210 } 211 setDriverUserId(int userId)212 public void setDriverUserId(int userId) { 213 mDriverUserId = userId; 214 } 215 hasZone(int zoneId)216 private boolean hasZone(int zoneId) { 217 for (OccupantZoneInfo zone : mZones) { 218 if (zoneId == zone.zoneId) { 219 return true; 220 } 221 } 222 return false; 223 } 224 } 225