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