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 android.car.cts; 18 19 import static android.car.Car.PERMISSION_CONTROL_REMOTE_ACCESS; 20 import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_CUSTOM; 21 import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_ENTER_GARAGE_MODE; 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.junit.Assume.assumeFalse; 28 import static org.junit.Assume.assumeNoException; 29 import static org.junit.Assume.assumeTrue; 30 31 import android.car.Car; 32 import android.car.feature.Flags; 33 import android.car.remoteaccess.CarRemoteAccessManager; 34 import android.car.remoteaccess.CarRemoteAccessManager.CompletableRemoteTaskFuture; 35 import android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler; 36 import android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskSchedulerException; 37 import android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback; 38 import android.car.remoteaccess.CarRemoteAccessManager.ScheduleInfo; 39 import android.car.remoteaccess.RemoteTaskClientRegistrationInfo; 40 import android.car.test.PermissionsCheckerRule.EnsureHasPermission; 41 import android.platform.test.annotations.AppModeFull; 42 import android.platform.test.annotations.RequiresFlagsEnabled; 43 import android.util.Log; 44 45 import androidx.annotation.NonNull; 46 import androidx.annotation.Nullable; 47 import androidx.test.platform.app.InstrumentationRegistry; 48 49 import com.android.car.remoteaccess.CarRemoteAccessDumpProto; 50 import com.android.car.remoteaccess.CarRemoteAccessDumpProto.ServerlessClientInfo; 51 import com.android.compatibility.common.util.ApiTest; 52 import com.android.compatibility.common.util.PollingCheck; 53 import com.android.compatibility.common.util.ProtoUtils; 54 import com.android.internal.annotations.GuardedBy; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Test; 59 60 import java.time.Duration; 61 import java.util.ArrayList; 62 import java.util.List; 63 import java.util.concurrent.Executor; 64 65 @AppModeFull(reason = "Instant Apps cannot get car related permissions") 66 public final class CarRemoteAccessManagerTest extends AbstractCarTestCase { 67 68 private static final String TAG = CarRemoteAccessManagerTest.class.getSimpleName(); 69 private static final int CALLBACK_WAIT_TIME_MS = 2_000; 70 private static final String INVALID_TASK_ID = "THIS_ID_CANNOT_BE_VALID_!@#$%^&*()"; 71 private static final String DUMP_COMMAND = 72 "dumpsys car_service --services CarRemoteAccessService --proto"; 73 private static final String SERVERLESS_CLIENT_ID = "TestServerlessClientId"; 74 private static final String TEST_SCHEDULE_ID_1 = "TestScheduleId1"; 75 private static final String TEST_SCHEDULE_ID_2 = "TestScheduleId2"; 76 private static final byte[] TEST_TASK_DATA = "test data".getBytes(); 77 78 private Executor mExecutor; 79 private CarRemoteAccessManager mCarRemoteAccessManager; 80 private boolean mServerlessRemoteTaskClientSet = false; 81 private String mPackageName; 82 83 @Before setUp()84 public void setUp() throws Exception { 85 assumeTrue("CarRemoteAccessService is not enabled, skipping test", 86 getCar().isFeatureEnabled(Car.CAR_REMOTE_ACCESS_SERVICE)); 87 88 mExecutor = mContext.getMainExecutor(); 89 mCarRemoteAccessManager = (CarRemoteAccessManager) getCar() 90 .getCarManager(Car.CAR_REMOTE_ACCESS_SERVICE); 91 assertThat(mCarRemoteAccessManager).isNotNull(); 92 mPackageName = mContext.getPackageName(); 93 } 94 95 @After tearDown()96 public void tearDown() throws Exception { 97 if (mServerlessRemoteTaskClientSet) { 98 mCarRemoteAccessManager.removeServerlessRemoteTaskClient(mPackageName); 99 } 100 } 101 102 @Test 103 @ApiTest(apis = { 104 "android.car.remoteaccess.CarRemoteAccessManager#setRemoteTaskClient", 105 }) 106 @RequiresFlagsEnabled(Flags.FLAG_CAR_DUMP_TO_PROTO) testSetRemoteTaskClient_regularClient()107 public void testSetRemoteTaskClient_regularClient() throws Exception { 108 assumeFalse("This test requires the test package to be a regular remote task client", 109 isTestPkgServerlessClient()); 110 111 RemoteTaskClientCallbackImpl callback = new RemoteTaskClientCallbackImpl(); 112 113 mCarRemoteAccessManager.setRemoteTaskClient(mExecutor, callback); 114 115 PollingCheck.waitFor(CALLBACK_WAIT_TIME_MS, () -> callback.getServiceId() != null); 116 PollingCheck.waitFor(CALLBACK_WAIT_TIME_MS, () -> callback.getVehicleId() != null); 117 PollingCheck.waitFor(CALLBACK_WAIT_TIME_MS, () -> callback.getProcessorId() != null); 118 PollingCheck.waitFor(CALLBACK_WAIT_TIME_MS, () -> callback.getClientId() != null); 119 } 120 121 @Test 122 @ApiTest(apis = { 123 "android.car.remoteaccess.CarRemoteAccessManager#setRemoteTaskClient" 124 }) 125 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 126 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testSetRemoteTaskClient_serverlessClient()127 public void testSetRemoteTaskClient_serverlessClient() throws Exception { 128 setSelfAsServerlessClient(); 129 130 RemoteTaskClientCallbackImpl callback = new RemoteTaskClientCallbackImpl(); 131 132 mCarRemoteAccessManager.setRemoteTaskClient(mExecutor, callback); 133 134 PollingCheck.waitFor(CALLBACK_WAIT_TIME_MS, 135 () -> callback.isServerlessClientRegistered()); 136 } 137 138 /** 139 * Tests that calling {@code setRemoteTaskClient} twice from the same client is not allowed. 140 */ 141 @Test 142 @ApiTest(apis = { 143 "android.car.remoteaccess.CarRemoteAccessManager#setRemoteTaskClient" 144 }) testSetRemoteTaskClient_withAlreadyRegisteredClient()145 public void testSetRemoteTaskClient_withAlreadyRegisteredClient() { 146 RemoteTaskClientCallbackImpl callbackOne = new RemoteTaskClientCallbackImpl(); 147 RemoteTaskClientCallbackImpl callbackTwo = new RemoteTaskClientCallbackImpl(); 148 149 mCarRemoteAccessManager.setRemoteTaskClient(mExecutor, callbackOne); 150 151 assertThrows(IllegalStateException.class, 152 () -> mCarRemoteAccessManager.setRemoteTaskClient(mExecutor, 153 callbackTwo)); 154 } 155 156 @Test 157 @ApiTest(apis = { 158 "android.car.remoteaccess.CarRemoteAccessManager#setRemoteTaskClient", 159 "android.car.remoteaccess.CarRemoteAccessManager#clearRemoteTaskClient" 160 }) testClearRemoteTaskClient()161 public void testClearRemoteTaskClient() { 162 RemoteTaskClientCallbackImpl callback = new RemoteTaskClientCallbackImpl(); 163 mCarRemoteAccessManager.setRemoteTaskClient(mExecutor, callback); 164 165 mCarRemoteAccessManager.clearRemoteTaskClient(); 166 167 // Calling clearRemoteTaskClient again to ensure that multiple calls do not cause errors. 168 mCarRemoteAccessManager.clearRemoteTaskClient(); 169 } 170 171 @Test 172 @ApiTest(apis = {"android.car.remoteaccess.CarRemoteAccessManager#clearRemoteTaskClient"}) testClearRemoteTaskClient_unregisteredClient()173 public void testClearRemoteTaskClient_unregisteredClient() { 174 mCarRemoteAccessManager.clearRemoteTaskClient(); 175 } 176 177 @Test 178 @ApiTest(apis = { 179 "android.car.remoteaccess.CarRemoteAccessManager#reportRemoteTaskDone" 180 }) testReportRemoteTaskDone_unregisteredClient()181 public void testReportRemoteTaskDone_unregisteredClient() { 182 assertThrows(IllegalStateException.class, 183 () -> mCarRemoteAccessManager.reportRemoteTaskDone(INVALID_TASK_ID)); 184 } 185 186 @Test 187 @ApiTest(apis = { 188 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 189 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 190 }) 191 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 192 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testGetInVehicleTaskScheduler_notSupported()193 public void testGetInVehicleTaskScheduler_notSupported() { 194 setSelfAsServerlessClient(); 195 196 assumeFalse("Task scheduling is supported, skipping the test", 197 mCarRemoteAccessManager.isTaskScheduleSupported()); 198 199 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 200 assertWithMessage("InVehicleTaskScheduler must be null when task schedule is not supported") 201 .that(taskScheduler).isNull(); 202 } 203 204 @Test 205 @ApiTest(apis = { 206 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 207 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 208 }) 209 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 210 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testGetInVehicleTaskScheduler_isSupported()211 public void testGetInVehicleTaskScheduler_isSupported() { 212 setSelfAsServerlessClient(); 213 assumeTaskSchedulingSupported(); 214 215 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 216 assertWithMessage("InVehicleTaskScheduler must not be null when task schedule is supported") 217 .that(taskScheduler).isNotNull(); 218 } 219 220 @Test 221 @ApiTest(apis = { 222 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 223 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 224 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 225 + "getSupportedTaskTypes", 226 }) 227 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 228 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testGetSupportedTaskTypes_TaskTypeMustIncludeCustom()229 public void testGetSupportedTaskTypes_TaskTypeMustIncludeCustom() throws Exception { 230 setSelfAsServerlessClient(); 231 assumeTaskSchedulingSupported(); 232 233 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 234 int[] supportedTaskTypes = new int[0]; 235 try { 236 supportedTaskTypes = taskScheduler.getSupportedTaskTypes(); 237 } catch (InVehicleTaskSchedulerException e) { 238 assumeNoException("Assume getInVehicleTaskScheduler to succeed", e); 239 } 240 241 assertWithMessage("Supported task types must contain TASK_TYPE_CUSTOM") 242 .that(supportedTaskTypes).asList().contains(TASK_TYPE_CUSTOM); 243 } 244 245 @Test 246 @ApiTest(apis = { 247 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 248 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 249 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 250 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 251 + "unscheduleAllTasks", 252 }) 253 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 254 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testScheduleTask_TaskTypeCustom()255 public void testScheduleTask_TaskTypeCustom() throws Exception { 256 setSelfAsServerlessClient(); 257 assumeTaskSchedulingSupported(); 258 259 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 260 // Schedule the task to be executed 30s later. 261 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 262 ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 263 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 264 .setTaskData(TEST_TASK_DATA).build(); 265 try { 266 taskScheduler.scheduleTask(scheduleInfo); 267 } catch (InVehicleTaskSchedulerException e) { 268 assumeNoException("Assume task schedule to succeed", e); 269 } 270 271 taskScheduler.unscheduleAllTasks(); 272 } 273 274 @Test 275 @ApiTest(apis = { 276 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 277 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 278 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 279 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 280 + "getSupportedTaskTypes", 281 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 282 + "isTaskScheduled", 283 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 284 + "getAllPendingScheduledTasks", 285 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 286 + "unscheduleAllTasks", 287 }) 288 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 289 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testScheduleTask_TaskTypeEnterGarageMode()290 public void testScheduleTask_TaskTypeEnterGarageMode() throws Exception { 291 setSelfAsServerlessClient(); 292 assumeTaskSchedulingSupported(); 293 294 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 295 int[] supportedTaskTypes = taskScheduler.getSupportedTaskTypes(); 296 boolean supportTaskTypeEnterGarageMode = false; 297 for (int i = 0; i < supportedTaskTypes.length; i++) { 298 if (supportedTaskTypes[i] == TASK_TYPE_ENTER_GARAGE_MODE) { 299 supportTaskTypeEnterGarageMode = true; 300 } 301 } 302 303 assumeTrue("Task type: ENTER_GARAGE_MODE is not supported, skip the test", 304 supportTaskTypeEnterGarageMode); 305 306 // Schedule the task to be executed 30s later. 307 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 308 ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 309 TASK_TYPE_ENTER_GARAGE_MODE, startTimeInEpochSeconds).build(); 310 try { 311 taskScheduler.scheduleTask(scheduleInfo); 312 } catch (InVehicleTaskSchedulerException e) { 313 assumeNoException("Assume task schedule to succeed", e); 314 } 315 316 try { 317 expectWithMessage("isTaskScheduled for scheduled task").that( 318 taskScheduler.isTaskScheduled(TEST_SCHEDULE_ID_1)).isTrue(); 319 320 List<ScheduleInfo> gotScheduleInfo = taskScheduler.getAllPendingScheduledTasks(); 321 322 assertWithMessage("Must return one scheduled tasks").that(gotScheduleInfo).hasSize(1); 323 ScheduleInfo info = gotScheduleInfo.get(0); 324 expectWithMessage("Got expected scheduleId").that(info.getScheduleId()) 325 .isEqualTo(TEST_SCHEDULE_ID_1); 326 expectWithMessage("Got expected task data").that(info.getTaskType()).isEqualTo( 327 TASK_TYPE_ENTER_GARAGE_MODE); 328 expectWithMessage("Got expected task data").that(info.getTaskData()).isEmpty(); 329 expectWithMessage("Got expected count").that(info.getCount()).isEqualTo(1); 330 expectWithMessage("Got expected periodic").that(info.getPeriodic()).isEqualTo( 331 Duration.ZERO); 332 } finally { 333 taskScheduler.unscheduleAllTasks(); 334 } 335 } 336 337 @Test 338 @ApiTest(apis = { 339 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 340 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 341 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 342 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 343 + "unscheduleAllTasks", 344 }) 345 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 346 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testScheduleTask_duplicateScheduleIdMustThrowException()347 public void testScheduleTask_duplicateScheduleIdMustThrowException() throws Exception { 348 setSelfAsServerlessClient(); 349 assumeTaskSchedulingSupported(); 350 351 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 352 // Schedule the task to be executed 30s later. 353 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 354 ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 355 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 356 .setTaskData(TEST_TASK_DATA).build(); 357 try { 358 taskScheduler.scheduleTask(scheduleInfo); 359 } catch (InVehicleTaskSchedulerException e) { 360 assumeNoException("Assume task schedule to succeed", e); 361 } 362 363 try { 364 // Schedule the same task twice must cause IllegalArgumentException. 365 assertThrows(IllegalArgumentException.class, () -> taskScheduler.scheduleTask( 366 scheduleInfo)); 367 } finally { 368 taskScheduler.unscheduleAllTasks(); 369 } 370 } 371 372 @Test 373 @ApiTest(apis = { 374 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 375 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 376 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 377 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 378 + "unscheduleAllTasks", 379 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 380 + "isTaskScheduled", 381 }) 382 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 383 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testIsTaskScheduled()384 public void testIsTaskScheduled() throws Exception { 385 setSelfAsServerlessClient(); 386 assumeTaskSchedulingSupported(); 387 388 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 389 // Schedule the task to be executed 30s later. 390 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 391 ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 392 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 393 .setTaskData(TEST_TASK_DATA).build(); 394 try { 395 taskScheduler.scheduleTask(scheduleInfo); 396 } catch (InVehicleTaskSchedulerException e) { 397 assumeNoException("Assume task schedule to succeed", e); 398 } 399 400 try { 401 expectWithMessage("isTaskScheduled for scheduled task").that( 402 taskScheduler.isTaskScheduled(TEST_SCHEDULE_ID_1)).isTrue(); 403 expectWithMessage("isTaskScheduled for unscheduled task").that( 404 taskScheduler.isTaskScheduled(TEST_SCHEDULE_ID_2)).isFalse(); 405 } finally { 406 taskScheduler.unscheduleAllTasks(); 407 } 408 } 409 410 @Test 411 @ApiTest(apis = { 412 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 413 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 414 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 415 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 416 + "unscheduleAllTasks", 417 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 418 + "getAllPendingScheduledTasks", 419 }) 420 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 421 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testGetAllPendingScheduledTasks()422 public void testGetAllPendingScheduledTasks() throws Exception { 423 setSelfAsServerlessClient(); 424 assumeTaskSchedulingSupported(); 425 426 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 427 // Schedule the task to be executed 30s later. 428 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 429 ScheduleInfo scheduleInfo1 = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 430 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 431 .setTaskData(TEST_TASK_DATA).build(); 432 ScheduleInfo scheduleInfo2 = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_2, 433 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 434 .setTaskData(TEST_TASK_DATA).setCount(2).setPeriodic(Duration.ofSeconds(30)) 435 .build(); 436 437 try { 438 taskScheduler.unscheduleAllTasks(); 439 taskScheduler.scheduleTask(scheduleInfo1); 440 taskScheduler.scheduleTask(scheduleInfo2); 441 } catch (InVehicleTaskSchedulerException e) { 442 assumeNoException("Assume task schedule to succeed", e); 443 } 444 445 try { 446 List<ScheduleInfo> scheduleInfo = taskScheduler.getAllPendingScheduledTasks(); 447 448 assertWithMessage("Must return two scheduled tasks").that(scheduleInfo).hasSize(2); 449 List<String> gotScheduleIds = new ArrayList<>(); 450 for (int i = 0; i < scheduleInfo.size(); i++) { 451 ScheduleInfo info = scheduleInfo.get(i); 452 expectWithMessage("Got expected scheduleId").that(info.getScheduleId()) 453 .isAnyOf(TEST_SCHEDULE_ID_1, TEST_SCHEDULE_ID_2); 454 gotScheduleIds.add(info.getScheduleId()); 455 expectWithMessage("Got expected task data").that(info.getTaskData()).isEqualTo( 456 TEST_TASK_DATA); 457 if (info.getScheduleId().equals(TEST_SCHEDULE_ID_1)) { 458 expectWithMessage("Got expected count").that(info.getCount()).isEqualTo(1); 459 expectWithMessage("Got expected periodic").that(info.getPeriodic()).isEqualTo( 460 Duration.ZERO); 461 } else { 462 expectWithMessage("Got expected count").that(info.getCount()).isEqualTo(2); 463 expectWithMessage("Got expected periodic").that(info.getPeriodic()).isEqualTo( 464 Duration.ofSeconds(30)); 465 } 466 } 467 expectWithMessage("Got all expected schedule Ids").that(gotScheduleIds).containsExactly( 468 TEST_SCHEDULE_ID_1, TEST_SCHEDULE_ID_2); 469 } finally { 470 taskScheduler.unscheduleAllTasks(); 471 } 472 } 473 474 @Test 475 @ApiTest(apis = { 476 "android.car.remoteaccess.CarRemoteAccessManager#isTaskScheduleSupported", 477 "android.car.remoteaccess.CarRemoteAccessManager#getInVehicleTaskScheduler", 478 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#scheduleTask", 479 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 480 + "unscheduleAllTasks", 481 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 482 + "getAllPendingScheduledTasks", 483 "android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskScheduler#" 484 + "unscheduleTask", 485 }) 486 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 487 @RequiresFlagsEnabled({Flags.FLAG_SERVERLESS_REMOTE_ACCESS, Flags.FLAG_CAR_DUMP_TO_PROTO}) testUnscheduleTask()488 public void testUnscheduleTask() throws Exception { 489 setSelfAsServerlessClient(); 490 assumeTaskSchedulingSupported(); 491 492 InVehicleTaskScheduler taskScheduler = mCarRemoteAccessManager.getInVehicleTaskScheduler(); 493 // Schedule the task to be executed 30s later. 494 long startTimeInEpochSeconds = System.currentTimeMillis() / 1000 + 30; 495 ScheduleInfo scheduleInfo1 = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_1, 496 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 497 .setTaskData(TEST_TASK_DATA).build(); 498 ScheduleInfo scheduleInfo2 = new ScheduleInfo.Builder(TEST_SCHEDULE_ID_2, 499 TASK_TYPE_CUSTOM, startTimeInEpochSeconds) 500 .setTaskData(TEST_TASK_DATA).setCount(2).setPeriodic(Duration.ofSeconds(30)) 501 .build(); 502 503 try { 504 taskScheduler.unscheduleAllTasks(); 505 taskScheduler.scheduleTask(scheduleInfo1); 506 taskScheduler.scheduleTask(scheduleInfo2); 507 } catch (InVehicleTaskSchedulerException e) { 508 assumeNoException("Assume task schedule to succeed", e); 509 } 510 511 taskScheduler.unscheduleTask(TEST_SCHEDULE_ID_2); 512 513 try { 514 List<ScheduleInfo> scheduleInfo = taskScheduler.getAllPendingScheduledTasks(); 515 516 assertWithMessage("Must return one scheduled tasks").that(scheduleInfo).hasSize(1); 517 ScheduleInfo info = scheduleInfo.get(0); 518 expectWithMessage("Got expected scheduleId").that(info.getScheduleId()) 519 .isEqualTo(TEST_SCHEDULE_ID_1); 520 expectWithMessage("Got expected task data").that(info.getTaskType()).isEqualTo( 521 TASK_TYPE_CUSTOM); 522 expectWithMessage("Got expected task data").that(info.getTaskData()).isEqualTo( 523 TEST_TASK_DATA); 524 expectWithMessage("Got expected count").that(info.getCount()).isEqualTo(1); 525 expectWithMessage("Got expected periodic").that(info.getPeriodic()).isEqualTo( 526 Duration.ZERO); 527 } finally { 528 taskScheduler.unscheduleAllTasks(); 529 } 530 } 531 532 @Test 533 @ApiTest(apis = {"android.car.remoteaccess.CarRemoteAccessManager#isVehicleInUseSupported"}) 534 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 535 @RequiresFlagsEnabled(Flags.FLAG_CAR_DUMP_TO_PROTO) testVehicleInUseMustBeSupported()536 public void testVehicleInUseMustBeSupported() { 537 assertWithMessage("VHAL property: VEHICLE_IN_USE must be supported if remote access feature" 538 + " is enabled").that(mCarRemoteAccessManager.isVehicleInUseSupported()).isTrue(); 539 } 540 541 @Test 542 @ApiTest(apis = {"android.car.remoteaccess.CarRemoteAccessManager#isShutdownRequestSupported"}) 543 @EnsureHasPermission(PERMISSION_CONTROL_REMOTE_ACCESS) 544 @RequiresFlagsEnabled(Flags.FLAG_CAR_DUMP_TO_PROTO) testShutdownRequestMustBeSupported()545 public void testShutdownRequestMustBeSupported() { 546 assertWithMessage("VHAL property: SHUTDOWN_REQUEST must be supported if remote access " 547 + "feature is enabled").that(mCarRemoteAccessManager.isShutdownRequestSupported()) 548 .isTrue(); 549 } 550 551 private static final class RemoteTaskClientCallbackImpl implements RemoteTaskClientCallback { 552 private final Object mLock = new Object(); 553 554 @GuardedBy("mLock") 555 private RemoteTaskClientRegistrationInfo mInfo; 556 @GuardedBy("mLock") 557 private boolean mServerlessClientRegistered; 558 559 @Override onRegistrationUpdated(@onNull RemoteTaskClientRegistrationInfo info)560 public void onRegistrationUpdated(@NonNull RemoteTaskClientRegistrationInfo info) { 561 synchronized (mLock) { 562 mInfo = info; 563 } 564 } 565 566 @Override onServerlessClientRegistered()567 public void onServerlessClientRegistered() { 568 synchronized (mLock) { 569 mServerlessClientRegistered = true; 570 } 571 } 572 573 @Override onRegistrationFailed()574 public void onRegistrationFailed() { 575 } 576 577 @Override onRemoteTaskRequested(@onNull String taskId, @Nullable byte[] data, int taskMaxDurationInSec)578 public void onRemoteTaskRequested(@NonNull String taskId, @Nullable byte[] data, 579 int taskMaxDurationInSec) { 580 } 581 582 @Override onShutdownStarting(@onNull CompletableRemoteTaskFuture future)583 public void onShutdownStarting(@NonNull CompletableRemoteTaskFuture future) { 584 } 585 getServiceId()586 public String getServiceId() { 587 synchronized (mLock) { 588 if (mInfo == null) { 589 return null; 590 } 591 return mInfo.getVehicleId(); 592 } 593 } 594 getVehicleId()595 public String getVehicleId() { 596 597 synchronized (mLock) { 598 if (mInfo == null) { 599 return null; 600 } 601 return mInfo.getVehicleId(); 602 } 603 } 604 getProcessorId()605 public String getProcessorId() { 606 synchronized (mLock) { 607 if (mInfo == null) { 608 return null; 609 } 610 return mInfo.getProcessorId(); 611 } 612 } 613 getClientId()614 public String getClientId() { 615 synchronized (mLock) { 616 if (mInfo == null) { 617 return null; 618 } 619 return mInfo.getClientId(); 620 } 621 } 622 isServerlessClientRegistered()623 public boolean isServerlessClientRegistered() { 624 synchronized (mLock) { 625 return mServerlessClientRegistered; 626 } 627 } 628 } 629 isTestPkgServerlessClient()630 private boolean isTestPkgServerlessClient() { 631 try { 632 CarRemoteAccessDumpProto dump = ProtoUtils.getProto( 633 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 634 CarRemoteAccessDumpProto.class, DUMP_COMMAND); 635 636 for (int i = 0; i < dump.getServerlessClientsCount(); i++) { 637 ServerlessClientInfo serverlessClientInfo = dump.getServerlessClients(i); 638 if (serverlessClientInfo.getPackageName().equals(mContext.getPackageName())) { 639 return true; 640 } 641 } 642 } catch (Exception e) { 643 Log.w(TAG, "Failed to check whether this test is a serverless remote task client" 644 + ", default to false", e); 645 } 646 647 return false; 648 } 649 setSelfAsServerlessClient()650 private void setSelfAsServerlessClient() { 651 try { 652 mCarRemoteAccessManager.addServerlessRemoteTaskClient(mPackageName, 653 SERVERLESS_CLIENT_ID); 654 mServerlessRemoteTaskClientSet = true; 655 } catch (IllegalArgumentException e) { 656 Log.w(TAG, "failed to call addServerlessRemoteTaskClient, maybe the test pkg is " 657 + "already a serverless remote task client?", e); 658 } 659 660 assertWithMessage( 661 "This test requires the test package to be a serverless remote task client").that( 662 isTestPkgServerlessClient()).isTrue(); 663 } 664 assumeTaskSchedulingSupported()665 private void assumeTaskSchedulingSupported() { 666 assumeTrue("Task scheduling is not supported, skipping the test", 667 mCarRemoteAccessManager.isTaskScheduleSupported()); 668 } 669 } 670