1 /* 2 * Copyright (C) 2024 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.remoteaccess; 18 19 import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_CUSTOM; 20 import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_ENTER_GARAGE_MODE; 21 import static android.car.remoteaccess.ICarRemoteAccessService.SERVICE_ERROR_CODE_GENERAL; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static org.junit.Assert.assertThrows; 27 import static org.mockito.Mockito.any; 28 import static org.mockito.Mockito.anyString; 29 import static org.mockito.Mockito.doThrow; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.timeout; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.car.feature.FakeFeatureFlagsImpl; 37 import android.car.feature.Flags; 38 import android.car.remoteaccess.CarRemoteAccessManager.CompletableRemoteTaskFuture; 39 import android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskSchedulerException; 40 import android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback; 41 import android.car.remoteaccess.CarRemoteAccessManager.ScheduleInfo; 42 import android.car.remoteaccess.CarRemoteAccessManager.TaskType; 43 import android.car.test.AbstractExpectableTestCase; 44 import android.car.test.mocks.JavaMockitoHelper; 45 import android.os.IBinder; 46 import android.os.RemoteException; 47 import android.os.ServiceSpecificException; 48 import android.platform.test.ravenwood.RavenwoodRule; 49 50 import com.android.car.internal.ICarBase; 51 52 import org.junit.Before; 53 import org.junit.Rule; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.ArgumentCaptor; 57 import org.mockito.Captor; 58 import org.mockito.Mock; 59 import org.mockito.junit.MockitoJUnitRunner; 60 61 import java.time.Duration; 62 import java.util.List; 63 import java.util.concurrent.CountDownLatch; 64 import java.util.concurrent.Executor; 65 66 /** 67 * <p>This class contains unit tests for the {@link CarRemoteAccessManager}. 68 */ 69 @RunWith(MockitoJUnitRunner.class) 70 public final class CarRemoteAccessManagerUnitTest extends AbstractExpectableTestCase { 71 private static final int DEFAULT_TIMEOUT = 3000; 72 73 private static final String TEST_SCHEDULE_ID = "test schedule id"; 74 private static final byte[] TEST_TASK_DATA = new byte[]{ 75 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef}; 76 private static final @TaskType int TEST_TASK_TYPE = TASK_TYPE_CUSTOM; 77 private static final long TEST_START_TIME = 1234; 78 private static final int TEST_TASK_COUNT = 10; 79 private static final Duration TEST_PERIODIC = Duration.ofSeconds(1); 80 81 @Rule 82 public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProcessApp().build(); 83 84 @Mock 85 private ICarBase mCar; 86 @Mock 87 private IBinder mBinder; 88 @Mock 89 private ICarRemoteAccessService mService; 90 @Captor 91 private ArgumentCaptor<CompletableRemoteTaskFuture> mFutureCaptor; 92 93 private CarRemoteAccessManager mRemoteAccessManager; 94 private final Executor mExecutor = Runnable::run; 95 96 @Before setUp()97 public void setUp() throws Exception { 98 when(mBinder.queryLocalInterface(anyString())).thenReturn(mService); 99 when(mCar.handleRemoteExceptionFromCarService(any(RemoteException.class), any())) 100 .thenAnswer((inv) -> { 101 return inv.getArgument(1); 102 }); 103 mRemoteAccessManager = new CarRemoteAccessManager(mCar, mBinder); 104 } 105 106 @Test testSetRemoteTaskClient()107 public void testSetRemoteTaskClient() throws Exception { 108 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 109 110 mRemoteAccessManager.setRemoteTaskClient(mExecutor, remoteTaskClient); 111 112 verify(mService).addCarRemoteTaskClient(any(ICarRemoteAccessCallback.class)); 113 } 114 115 @Test testSetRemoteTaskClient_invalidArguments()116 public void testSetRemoteTaskClient_invalidArguments() throws Exception { 117 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 118 119 assertThrows(IllegalArgumentException.class, () -> mRemoteAccessManager.setRemoteTaskClient( 120 /* executor= */ null, remoteTaskClient)); 121 assertThrows(IllegalArgumentException.class, () -> mRemoteAccessManager.setRemoteTaskClient( 122 mExecutor, /* callback= */ null)); 123 } 124 125 @Test testSetRemoteTaskClient_doubleRegistration()126 public void testSetRemoteTaskClient_doubleRegistration() throws Exception { 127 RemoteTaskClient remoteTaskClientOne = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 128 RemoteTaskClient remoteTaskClientTwo = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 129 130 mRemoteAccessManager.setRemoteTaskClient(mExecutor, remoteTaskClientOne); 131 132 assertThrows(IllegalStateException.class, () -> mRemoteAccessManager.setRemoteTaskClient( 133 mExecutor, remoteTaskClientTwo)); 134 } 135 136 @Test testSetRmoteTaskClient_remoteException()137 public void testSetRmoteTaskClient_remoteException() throws Exception { 138 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 139 doThrow(RemoteException.class).when(mService) 140 .addCarRemoteTaskClient(any(ICarRemoteAccessCallback.class)); 141 142 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 143 internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing", 144 /* data= */ null, /* taskMaxDurationInSec= */ 10); 145 146 assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull(); 147 } 148 149 @Test testClearRemoteTaskClient()150 public void testClearRemoteTaskClient() throws Exception { 151 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 152 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 153 154 mRemoteAccessManager.clearRemoteTaskClient(); 155 internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing", 156 /* data= */ null, /* taskMaxDurationInSec= */ 10); 157 158 assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull(); 159 } 160 161 @Test testClearRemoteTaskClient_remoteException()162 public void testClearRemoteTaskClient_remoteException() throws Exception { 163 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0); 164 doThrow(RemoteException.class).when(mService) 165 .removeCarRemoteTaskClient(any(ICarRemoteAccessCallback.class)); 166 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 167 168 mRemoteAccessManager.clearRemoteTaskClient(); 169 internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing", 170 /* data= */ null, /* taskMaxDurationInSec= */ 10); 171 172 assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull(); 173 } 174 175 @Test testClientRegistration()176 public void testClientRegistration() throws Exception { 177 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1); 178 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 179 String serviceId = "serviceId_testing"; 180 String vehicleId = "vehicleId_testing"; 181 String processorId = "processorId_testing"; 182 String clientId = "clientId_testing"; 183 184 internalCallback.onClientRegistrationUpdated( 185 new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId)); 186 187 assertWithMessage("Service ID").that(remoteTaskClient.getServiceId()).isEqualTo(serviceId); 188 assertWithMessage("Vehicle ID").that(remoteTaskClient.getVehicleId()).isEqualTo(vehicleId); 189 assertWithMessage("Processor ID").that(remoteTaskClient.getProcessorId()) 190 .isEqualTo(processorId); 191 assertWithMessage("Client ID").that(remoteTaskClient.getClientId()).isEqualTo(clientId); 192 } 193 194 @Test testServerlessClientRegistration()195 public void testServerlessClientRegistration() throws Exception { 196 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1); 197 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 198 String clientId = "clientId_testing"; 199 FakeFeatureFlagsImpl fakeFlagsImpl = new FakeFeatureFlagsImpl(); 200 fakeFlagsImpl.setFlag(Flags.FLAG_SERVERLESS_REMOTE_ACCESS, true); 201 mRemoteAccessManager.setFeatureFlags(fakeFlagsImpl); 202 203 internalCallback.onServerlessClientRegistered(clientId); 204 205 assertThat(remoteTaskClient.isServerlessClientRegistered()).isTrue(); 206 } 207 208 @Test testClientRegistrationFail()209 public void testClientRegistrationFail() throws Exception { 210 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1); 211 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 212 213 internalCallback.onClientRegistrationFailed(); 214 215 assertWithMessage("Registration fail").that(remoteTaskClient.isRegistrationFail()).isTrue(); 216 } 217 218 @Test testRemoteTaskRequested()219 public void testRemoteTaskRequested() throws Exception { 220 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2); 221 String clientId = "clientId_testing"; 222 String taskId = "taskId_testing"; 223 prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null); 224 225 assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isEqualTo(taskId); 226 assertWithMessage("Data").that(remoteTaskClient.getData()).isNull(); 227 } 228 229 @Test testRemoteTaskRequested_withData()230 public void testRemoteTaskRequested_withData() throws Exception { 231 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2); 232 String clientId = "clientId_testing"; 233 String taskId = "taskId_testing"; 234 byte[] data = new byte[]{1, 2, 3, 4}; 235 prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, data); 236 237 assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isEqualTo(taskId); 238 assertWithMessage("Data").that(remoteTaskClient.getData()).asList() 239 .containsExactlyElementsIn(new Byte[]{1, 2, 3, 4}); 240 } 241 242 @Test testRemoteTaskRequested_mismatchedClientId()243 public void testRemoteTaskRequested_mismatchedClientId() throws Exception { 244 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1); 245 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 246 String serviceId = "serviceId_testing"; 247 String vehicleId = "vehicleId_testing"; 248 String processorId = "processorId_testing"; 249 String clientId = "clientId_testing"; 250 String misMatchedClientId = "clientId_mismatch"; 251 String taskId = "taskId_testing"; 252 byte[] data = new byte[]{1, 2, 3, 4}; 253 254 internalCallback.onClientRegistrationUpdated( 255 new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId)); 256 internalCallback.onRemoteTaskRequested(misMatchedClientId, taskId, data, 257 /* taskMaximumDurationInSec= */ 10); 258 259 assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isNull(); 260 assertWithMessage("Data").that(remoteTaskClient.getData()).isNull(); 261 } 262 263 @Test testReportRemoteTaskDone()264 public void testReportRemoteTaskDone() throws Exception { 265 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2); 266 String clientId = "clientId_testing"; 267 String taskId = "taskId_testing"; 268 prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null); 269 270 mRemoteAccessManager.reportRemoteTaskDone(taskId); 271 272 verify(mService).reportRemoteTaskDone(clientId, taskId); 273 } 274 275 @Test testReportRemoteTaskDone_nullTaskId()276 public void testReportRemoteTaskDone_nullTaskId() throws Exception { 277 assertThrows(IllegalArgumentException.class, 278 () -> mRemoteAccessManager.reportRemoteTaskDone(/* taskId= */ null)); 279 } 280 281 @Test testReportRemoteTaskDone_noRegisteredClient()282 public void testReportRemoteTaskDone_noRegisteredClient() throws Exception { 283 assertThrows(IllegalStateException.class, 284 () -> mRemoteAccessManager.reportRemoteTaskDone("taskId_testing")); 285 } 286 287 @Test testReportRemoteTaskDone_invalidTaskId()288 public void testReportRemoteTaskDone_invalidTaskId() throws Exception { 289 RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2); 290 String clientId = "clientId_testing"; 291 String taskId = "taskId_testing"; 292 prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null); 293 doThrow(IllegalStateException.class).when(mService) 294 .reportRemoteTaskDone(clientId, taskId); 295 296 assertThrows(IllegalStateException.class, 297 () -> mRemoteAccessManager.reportRemoteTaskDone(taskId)); 298 } 299 300 @Test testSetPowerStatePostTaskExecution()301 public void testSetPowerStatePostTaskExecution() throws Exception { 302 int nextPowerState = CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_RAM; 303 boolean runGarageMode = true; 304 305 mRemoteAccessManager.setPowerStatePostTaskExecution(nextPowerState, runGarageMode); 306 307 verify(mService).setPowerStatePostTaskExecution(nextPowerState, runGarageMode); 308 } 309 310 @Test testOnShutdownStarting()311 public void testOnShutdownStarting() throws Exception { 312 RemoteTaskClientCallback remoteTaskClient = mock(RemoteTaskClientCallback.class); 313 String clientId = "clientId_testing"; 314 String serviceId = "serviceId_testing"; 315 String vehicleId = "vehicleId_testing"; 316 String processorId = "processorId_testing"; 317 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient); 318 319 internalCallback.onClientRegistrationUpdated( 320 new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId)); 321 322 verify(remoteTaskClient, timeout(DEFAULT_TIMEOUT)).onRegistrationUpdated(any()); 323 324 internalCallback.onShutdownStarting(); 325 326 verify(remoteTaskClient, timeout(DEFAULT_TIMEOUT)).onShutdownStarting( 327 mFutureCaptor.capture()); 328 CompletableRemoteTaskFuture future = mFutureCaptor.getValue(); 329 330 verify(mService, never()).confirmReadyForShutdown(any()); 331 332 future.complete(); 333 334 verify(mService).confirmReadyForShutdown(clientId); 335 } 336 337 @Test testScheduleInfoBuilder()338 public void testScheduleInfoBuilder() { 339 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE, 340 TEST_START_TIME); 341 builder.setTaskData(TEST_TASK_DATA); 342 ScheduleInfo scheduleInfo = builder.setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC) 343 .build(); 344 345 expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId()) 346 .isEqualTo(TEST_SCHEDULE_ID); 347 expectWithMessage("taskType from ScheduleInfo").that(scheduleInfo.getTaskType()) 348 .isEqualTo(TEST_TASK_TYPE); 349 expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData()) 350 .isEqualTo(TEST_TASK_DATA); 351 expectWithMessage("startTimeInEpochSeconds from ScheduleInfo") 352 .that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME); 353 expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount()) 354 .isEqualTo(TEST_TASK_COUNT); 355 expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic()) 356 .isEqualTo(TEST_PERIODIC); 357 } 358 359 @Test testScheduleInfoBuilder_enterGarageMode()360 public void testScheduleInfoBuilder_enterGarageMode() { 361 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, 362 TASK_TYPE_ENTER_GARAGE_MODE, TEST_START_TIME); 363 ScheduleInfo scheduleInfo = builder.setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC) 364 .build(); 365 366 expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId()) 367 .isEqualTo(TEST_SCHEDULE_ID); 368 expectWithMessage("taskType from ScheduleInfo").that(scheduleInfo.getTaskType()) 369 .isEqualTo(TASK_TYPE_ENTER_GARAGE_MODE); 370 expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData()) 371 .isEmpty(); 372 expectWithMessage("startTimeInEpochSeconds from ScheduleInfo") 373 .that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME); 374 expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount()) 375 .isEqualTo(TEST_TASK_COUNT); 376 expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic()) 377 .isEqualTo(TEST_PERIODIC); 378 } 379 380 @Test testScheduleInfoBuilder_invalidTaskType()381 public void testScheduleInfoBuilder_invalidTaskType() throws Exception { 382 assertThrows(IllegalArgumentException.class, () -> new ScheduleInfo.Builder( 383 TEST_SCHEDULE_ID, /*taskType=*/-1234, TEST_START_TIME)); 384 } 385 386 @Test testScheduleInfoBuilder_buildTwiceNotAllowed()387 public void testScheduleInfoBuilder_buildTwiceNotAllowed() { 388 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE, 389 TEST_START_TIME); 390 builder.setTaskData(TEST_TASK_DATA).setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC) 391 .build(); 392 393 assertThrows(IllegalStateException.class, () -> builder.build()); 394 } 395 396 @Test testScheduleInfoBuilder_nullScheduleId()397 public void testScheduleInfoBuilder_nullScheduleId() { 398 assertThrows(IllegalArgumentException.class, () -> new ScheduleInfo.Builder( 399 /* scheduleId= */ null, TEST_TASK_TYPE, TEST_START_TIME)); 400 } 401 402 @Test testScheduleInfoBuilder_nullTaskData()403 public void testScheduleInfoBuilder_nullTaskData() { 404 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE, 405 TEST_START_TIME); 406 407 assertThrows(IllegalArgumentException.class, () -> builder.setTaskData(null)); 408 } 409 410 @Test testScheduleInfoBuilder_negativeCount()411 public void testScheduleInfoBuilder_negativeCount() { 412 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, 413 TEST_TASK_TYPE, TEST_START_TIME); 414 415 assertThrows(IllegalArgumentException.class, () -> builder.setCount(-1)); 416 } 417 418 @Test testScheduleInfoBuilder_nullPeriodic()419 public void testScheduleInfoBuilder_nullPeriodic() { 420 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE, 421 TEST_START_TIME); 422 423 assertThrows(IllegalArgumentException.class, () -> builder.setPeriodic(null)); 424 } 425 426 @Test testIsTaskScheduleSupported()427 public void testIsTaskScheduleSupported() throws Exception { 428 when(mService.isTaskScheduleSupported()).thenReturn(true); 429 430 assertThat(mRemoteAccessManager.isTaskScheduleSupported()).isTrue(); 431 } 432 433 @Test testGetInVehicleTaskScheduler()434 public void testGetInVehicleTaskScheduler() throws Exception { 435 when(mService.isTaskScheduleSupported()).thenReturn(true); 436 437 assertThat(mRemoteAccessManager.getInVehicleTaskScheduler()).isNotNull(); 438 } 439 440 @Test testGetInVehicleTaskScheduler_notSupported()441 public void testGetInVehicleTaskScheduler_notSupported() throws Exception { 442 when(mService.isTaskScheduleSupported()).thenReturn(false); 443 444 assertThat(mRemoteAccessManager.getInVehicleTaskScheduler()).isNull(); 445 } 446 getTestTaskScheduleInfo()447 private TaskScheduleInfo getTestTaskScheduleInfo() { 448 TaskScheduleInfo taskScheduleInfo = new TaskScheduleInfo(); 449 taskScheduleInfo.scheduleId = TEST_SCHEDULE_ID; 450 taskScheduleInfo.taskType = TEST_TASK_TYPE; 451 taskScheduleInfo.taskData = TEST_TASK_DATA; 452 taskScheduleInfo.startTimeInEpochSeconds = TEST_START_TIME; 453 taskScheduleInfo.count = TEST_TASK_COUNT; 454 taskScheduleInfo.periodicInSeconds = TEST_PERIODIC.getSeconds(); 455 return taskScheduleInfo; 456 } 457 458 @Test testScheduleTask()459 public void testScheduleTask() throws Exception { 460 when(mService.isTaskScheduleSupported()).thenReturn(true); 461 ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE, 462 TEST_START_TIME); 463 ScheduleInfo scheduleInfo = builder.setTaskData(TEST_TASK_DATA).setCount(TEST_TASK_COUNT) 464 .setPeriodic(TEST_PERIODIC).build(); 465 466 mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(scheduleInfo); 467 468 verify(mService).scheduleTask(getTestTaskScheduleInfo()); 469 } 470 471 @Test testScheduleTask_nullScheduleInfo()472 public void testScheduleTask_nullScheduleInfo() throws Exception { 473 when(mService.isTaskScheduleSupported()).thenReturn(true); 474 475 assertThrows(IllegalArgumentException.class, () -> 476 mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(null)); 477 } 478 479 @Test testScheduleTask_ServiceSpecificException()480 public void testScheduleTask_ServiceSpecificException() throws Exception { 481 when(mService.isTaskScheduleSupported()).thenReturn(true); 482 doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService) 483 .scheduleTask(any()); 484 ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, 485 TEST_TASK_TYPE, TEST_START_TIME).setTaskData(TEST_TASK_DATA) 486 .setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC).build(); 487 488 assertThrows(InVehicleTaskSchedulerException.class, () -> 489 mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(scheduleInfo)); 490 } 491 492 @Test testUnscheduleTask()493 public void testUnscheduleTask() throws Exception { 494 when(mService.isTaskScheduleSupported()).thenReturn(true); 495 496 mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(TEST_SCHEDULE_ID); 497 498 verify(mService).unscheduleTask(TEST_SCHEDULE_ID); 499 } 500 501 @Test testUnscheduleTask_nullScheduleId()502 public void testUnscheduleTask_nullScheduleId() throws Exception { 503 when(mService.isTaskScheduleSupported()).thenReturn(true); 504 505 assertThrows(IllegalArgumentException.class, () -> 506 mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(null)); 507 } 508 509 @Test testUnscheduleTask_ServiceSpecificException()510 public void testUnscheduleTask_ServiceSpecificException() throws Exception { 511 when(mService.isTaskScheduleSupported()).thenReturn(true); 512 doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService) 513 .unscheduleTask(any()); 514 515 assertThrows(InVehicleTaskSchedulerException.class, () -> 516 mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(TEST_SCHEDULE_ID)); 517 } 518 519 @Test testGetAllPendingScheduledTasks()520 public void testGetAllPendingScheduledTasks() throws Exception { 521 when(mService.isTaskScheduleSupported()).thenReturn(true); 522 when(mService.getAllPendingScheduledTasks()).thenReturn(List.of(getTestTaskScheduleInfo())); 523 524 List<ScheduleInfo> scheduleInfoList = mRemoteAccessManager.getInVehicleTaskScheduler() 525 .getAllPendingScheduledTasks(); 526 527 assertThat(scheduleInfoList).hasSize(1); 528 ScheduleInfo scheduleInfo = scheduleInfoList.get(0); 529 expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId()) 530 .isEqualTo(TEST_SCHEDULE_ID); 531 expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData()) 532 .isEqualTo(TEST_TASK_DATA); 533 expectWithMessage("startTimeInEpochSeconds from ScheduleInfo") 534 .that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME); 535 expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount()) 536 .isEqualTo(TEST_TASK_COUNT); 537 expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic()) 538 .isEqualTo(TEST_PERIODIC); 539 } 540 541 @Test testGetAllPendingScheduledTasks_ServiceSpecificException()542 public void testGetAllPendingScheduledTasks_ServiceSpecificException() throws Exception { 543 when(mService.isTaskScheduleSupported()).thenReturn(true); 544 doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService) 545 .getAllPendingScheduledTasks(); 546 547 assertThrows(InVehicleTaskSchedulerException.class, () -> 548 mRemoteAccessManager.getInVehicleTaskScheduler().getAllPendingScheduledTasks()); 549 } 550 551 @Test testUnscheduleAllTasks()552 public void testUnscheduleAllTasks() throws Exception { 553 when(mService.isTaskScheduleSupported()).thenReturn(true); 554 555 mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleAllTasks(); 556 557 verify(mService).unscheduleAllTasks(); 558 } 559 560 @Test testUnscheduleAllTasks_ServiceSpecificException()561 public void testUnscheduleAllTasks_ServiceSpecificException() throws Exception { 562 when(mService.isTaskScheduleSupported()).thenReturn(true); 563 doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService) 564 .unscheduleAllTasks(); 565 566 assertThrows(InVehicleTaskSchedulerException.class, () -> 567 mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleAllTasks()); 568 } 569 570 @Test testIsTaskScheduled()571 public void testIsTaskScheduled() throws Exception { 572 when(mService.isTaskScheduleSupported()).thenReturn(true); 573 when(mService.isTaskScheduled(TEST_SCHEDULE_ID)).thenReturn(true); 574 575 assertThat(mRemoteAccessManager.getInVehicleTaskScheduler() 576 .isTaskScheduled(TEST_SCHEDULE_ID)).isTrue(); 577 } 578 579 @Test testIsTaskScheduled_nullScheduleId()580 public void testIsTaskScheduled_nullScheduleId() throws Exception { 581 when(mService.isTaskScheduleSupported()).thenReturn(true); 582 583 assertThrows(IllegalArgumentException.class, () -> 584 mRemoteAccessManager.getInVehicleTaskScheduler().isTaskScheduled(null)); 585 } 586 587 @Test testIsTaskScheduled_ServiceSpecificException()588 public void testIsTaskScheduled_ServiceSpecificException() throws Exception { 589 when(mService.isTaskScheduleSupported()).thenReturn(true); 590 when(mService.isTaskScheduled(any())).thenThrow(new ServiceSpecificException( 591 SERVICE_ERROR_CODE_GENERAL)); 592 593 assertThrows(InVehicleTaskSchedulerException.class, () -> 594 mRemoteAccessManager.getInVehicleTaskScheduler().isTaskScheduled(TEST_SCHEDULE_ID)); 595 } 596 597 @Test testGetSupportedTaskTypes()598 public void testGetSupportedTaskTypes() throws Exception { 599 when(mService.isTaskScheduleSupported()).thenReturn(true); 600 int[] taskTypes = new int[]{TASK_TYPE_CUSTOM, TASK_TYPE_ENTER_GARAGE_MODE}; 601 when(mService.getSupportedTaskTypesForScheduling()).thenReturn(taskTypes); 602 603 assertThat(mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes()) 604 .isEqualTo(taskTypes); 605 } 606 607 @Test testGetSupportedTaskTypes_RemoteException()608 public void testGetSupportedTaskTypes_RemoteException() throws Exception { 609 when(mService.isTaskScheduleSupported()).thenReturn(true); 610 when(mService.getSupportedTaskTypesForScheduling()).thenThrow(new RemoteException()); 611 612 assertThat(mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes()) 613 .isEmpty(); 614 } 615 616 @Test testGetSupportedTaskTypes_ServiceSpecificException()617 public void testGetSupportedTaskTypes_ServiceSpecificException() throws Exception { 618 when(mService.isTaskScheduleSupported()).thenReturn(true); 619 when(mService.getSupportedTaskTypesForScheduling()).thenThrow( 620 new ServiceSpecificException(0)); 621 622 assertThrows(InVehicleTaskSchedulerException.class, () -> 623 mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes()); 624 } 625 setClientAndGetCallback(RemoteTaskClientCallback client)626 private ICarRemoteAccessCallback setClientAndGetCallback(RemoteTaskClientCallback client) 627 throws Exception { 628 ArgumentCaptor<ICarRemoteAccessCallback> internalCallbackCaptor = 629 ArgumentCaptor.forClass(ICarRemoteAccessCallback.class); 630 mRemoteAccessManager.setRemoteTaskClient(mExecutor, client); 631 verify(mService).addCarRemoteTaskClient(internalCallbackCaptor.capture()); 632 return internalCallbackCaptor.getValue(); 633 } 634 prepareRemoteTaskRequested(RemoteTaskClient client, String clientId, String taskId, byte[] data)635 private void prepareRemoteTaskRequested(RemoteTaskClient client, String clientId, 636 String taskId, byte[] data) throws Exception { 637 ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(client); 638 String serviceId = "serviceId_testing"; 639 String vehicleId = "vehicleId_testing"; 640 String processorId = "processorId_testing"; 641 642 internalCallback.onClientRegistrationUpdated( 643 new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId)); 644 internalCallback.onRemoteTaskRequested(clientId, taskId, data, 645 /* taskMaximumDurationInSec= */ 10); 646 } 647 648 private static final class RemoteTaskClient implements RemoteTaskClientCallback { 649 private static final int DEFAULT_TIMEOUT = 3000; 650 651 private final CountDownLatch mLatch; 652 private String mServiceId; 653 private String mVehicleId; 654 private String mProcessorId; 655 private String mClientId; 656 private String mTaskId; 657 private boolean mRegistrationFailed; 658 private boolean mServerlessClientRegistered; 659 private byte[] mData; 660 RemoteTaskClient(int expectedCallbackCount)661 private RemoteTaskClient(int expectedCallbackCount) { 662 mLatch = new CountDownLatch(expectedCallbackCount); 663 } 664 665 @Override onRegistrationUpdated(RemoteTaskClientRegistrationInfo info)666 public void onRegistrationUpdated(RemoteTaskClientRegistrationInfo info) { 667 mServiceId = info.getServiceId(); 668 mVehicleId = info.getVehicleId(); 669 mProcessorId = info.getProcessorId(); 670 mClientId = info.getClientId(); 671 mLatch.countDown(); 672 } 673 674 @Override onServerlessClientRegistered()675 public void onServerlessClientRegistered() { 676 mServerlessClientRegistered = true; 677 mLatch.countDown(); 678 } 679 680 @Override onRegistrationFailed()681 public void onRegistrationFailed() { 682 mRegistrationFailed = true; 683 mLatch.countDown(); 684 } 685 686 @Override onRemoteTaskRequested(String taskId, byte[] data, int remainingTimeSec)687 public void onRemoteTaskRequested(String taskId, byte[] data, int remainingTimeSec) { 688 mTaskId = taskId; 689 mData = data; 690 mLatch.countDown(); 691 } 692 693 @Override onShutdownStarting(CarRemoteAccessManager.CompletableRemoteTaskFuture future)694 public void onShutdownStarting(CarRemoteAccessManager.CompletableRemoteTaskFuture future) { 695 mLatch.countDown(); 696 } 697 getServiceId()698 public String getServiceId() throws Exception { 699 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 700 return mServiceId; 701 } 702 getVehicleId()703 public String getVehicleId() throws Exception { 704 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 705 return mVehicleId; 706 } 707 getProcessorId()708 public String getProcessorId() throws Exception { 709 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 710 return mProcessorId; 711 } 712 getClientId()713 public String getClientId() throws Exception { 714 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 715 return mClientId; 716 } 717 getTaskId()718 public String getTaskId() throws Exception { 719 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 720 return mTaskId; 721 } 722 getData()723 public byte[] getData() throws Exception { 724 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 725 return mData; 726 } 727 isRegistrationFail()728 public boolean isRegistrationFail() throws Exception { 729 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 730 return mRegistrationFailed; 731 } 732 isServerlessClientRegistered()733 public boolean isServerlessClientRegistered() throws Exception { 734 JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT); 735 return mServerlessClientRegistered; 736 } 737 } 738 } 739