/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car.remoteaccess;
import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_CUSTOM;
import static android.car.remoteaccess.CarRemoteAccessManager.TASK_TYPE_ENTER_GARAGE_MODE;
import static android.car.remoteaccess.ICarRemoteAccessService.SERVICE_ERROR_CODE_GENERAL;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.car.feature.FakeFeatureFlagsImpl;
import android.car.feature.Flags;
import android.car.remoteaccess.CarRemoteAccessManager.CompletableRemoteTaskFuture;
import android.car.remoteaccess.CarRemoteAccessManager.InVehicleTaskSchedulerException;
import android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback;
import android.car.remoteaccess.CarRemoteAccessManager.ScheduleInfo;
import android.car.remoteaccess.CarRemoteAccessManager.TaskType;
import android.car.test.AbstractExpectableTestCase;
import android.car.test.mocks.JavaMockitoHelper;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.platform.test.ravenwood.RavenwoodRule;
import com.android.car.internal.ICarBase;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
/**
*
This class contains unit tests for the {@link CarRemoteAccessManager}.
*/
@RunWith(MockitoJUnitRunner.class)
public final class CarRemoteAccessManagerUnitTest extends AbstractExpectableTestCase {
private static final int DEFAULT_TIMEOUT = 3000;
private static final String TEST_SCHEDULE_ID = "test schedule id";
private static final byte[] TEST_TASK_DATA = new byte[]{
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef};
private static final @TaskType int TEST_TASK_TYPE = TASK_TYPE_CUSTOM;
private static final long TEST_START_TIME = 1234;
private static final int TEST_TASK_COUNT = 10;
private static final Duration TEST_PERIODIC = Duration.ofSeconds(1);
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProcessApp().build();
@Mock
private ICarBase mCar;
@Mock
private IBinder mBinder;
@Mock
private ICarRemoteAccessService mService;
@Captor
private ArgumentCaptor mFutureCaptor;
private CarRemoteAccessManager mRemoteAccessManager;
private final Executor mExecutor = Runnable::run;
@Before
public void setUp() throws Exception {
when(mBinder.queryLocalInterface(anyString())).thenReturn(mService);
when(mCar.handleRemoteExceptionFromCarService(any(RemoteException.class), any()))
.thenAnswer((inv) -> {
return inv.getArgument(1);
});
mRemoteAccessManager = new CarRemoteAccessManager(mCar, mBinder);
}
@Test
public void testSetRemoteTaskClient() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
mRemoteAccessManager.setRemoteTaskClient(mExecutor, remoteTaskClient);
verify(mService).addCarRemoteTaskClient(any(ICarRemoteAccessCallback.class));
}
@Test
public void testSetRemoteTaskClient_invalidArguments() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
assertThrows(IllegalArgumentException.class, () -> mRemoteAccessManager.setRemoteTaskClient(
/* executor= */ null, remoteTaskClient));
assertThrows(IllegalArgumentException.class, () -> mRemoteAccessManager.setRemoteTaskClient(
mExecutor, /* callback= */ null));
}
@Test
public void testSetRemoteTaskClient_doubleRegistration() throws Exception {
RemoteTaskClient remoteTaskClientOne = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
RemoteTaskClient remoteTaskClientTwo = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
mRemoteAccessManager.setRemoteTaskClient(mExecutor, remoteTaskClientOne);
assertThrows(IllegalStateException.class, () -> mRemoteAccessManager.setRemoteTaskClient(
mExecutor, remoteTaskClientTwo));
}
@Test
public void testSetRmoteTaskClient_remoteException() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
doThrow(RemoteException.class).when(mService)
.addCarRemoteTaskClient(any(ICarRemoteAccessCallback.class));
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing",
/* data= */ null, /* taskMaxDurationInSec= */ 10);
assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull();
}
@Test
public void testClearRemoteTaskClient() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
mRemoteAccessManager.clearRemoteTaskClient();
internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing",
/* data= */ null, /* taskMaxDurationInSec= */ 10);
assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull();
}
@Test
public void testClearRemoteTaskClient_remoteException() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 0);
doThrow(RemoteException.class).when(mService)
.removeCarRemoteTaskClient(any(ICarRemoteAccessCallback.class));
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
mRemoteAccessManager.clearRemoteTaskClient();
internalCallback.onRemoteTaskRequested("clientId_testing", "taskId_testing",
/* data= */ null, /* taskMaxDurationInSec= */ 10);
assertWithMessage("Remote task").that(remoteTaskClient.getTaskId()).isNull();
}
@Test
public void testClientRegistration() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1);
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
String serviceId = "serviceId_testing";
String vehicleId = "vehicleId_testing";
String processorId = "processorId_testing";
String clientId = "clientId_testing";
internalCallback.onClientRegistrationUpdated(
new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId));
assertWithMessage("Service ID").that(remoteTaskClient.getServiceId()).isEqualTo(serviceId);
assertWithMessage("Vehicle ID").that(remoteTaskClient.getVehicleId()).isEqualTo(vehicleId);
assertWithMessage("Processor ID").that(remoteTaskClient.getProcessorId())
.isEqualTo(processorId);
assertWithMessage("Client ID").that(remoteTaskClient.getClientId()).isEqualTo(clientId);
}
@Test
public void testServerlessClientRegistration() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1);
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
String clientId = "clientId_testing";
FakeFeatureFlagsImpl fakeFlagsImpl = new FakeFeatureFlagsImpl();
fakeFlagsImpl.setFlag(Flags.FLAG_SERVERLESS_REMOTE_ACCESS, true);
mRemoteAccessManager.setFeatureFlags(fakeFlagsImpl);
internalCallback.onServerlessClientRegistered(clientId);
assertThat(remoteTaskClient.isServerlessClientRegistered()).isTrue();
}
@Test
public void testClientRegistrationFail() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1);
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
internalCallback.onClientRegistrationFailed();
assertWithMessage("Registration fail").that(remoteTaskClient.isRegistrationFail()).isTrue();
}
@Test
public void testRemoteTaskRequested() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2);
String clientId = "clientId_testing";
String taskId = "taskId_testing";
prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null);
assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isEqualTo(taskId);
assertWithMessage("Data").that(remoteTaskClient.getData()).isNull();
}
@Test
public void testRemoteTaskRequested_withData() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2);
String clientId = "clientId_testing";
String taskId = "taskId_testing";
byte[] data = new byte[]{1, 2, 3, 4};
prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, data);
assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isEqualTo(taskId);
assertWithMessage("Data").that(remoteTaskClient.getData()).asList()
.containsExactlyElementsIn(new Byte[]{1, 2, 3, 4});
}
@Test
public void testRemoteTaskRequested_mismatchedClientId() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 1);
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
String serviceId = "serviceId_testing";
String vehicleId = "vehicleId_testing";
String processorId = "processorId_testing";
String clientId = "clientId_testing";
String misMatchedClientId = "clientId_mismatch";
String taskId = "taskId_testing";
byte[] data = new byte[]{1, 2, 3, 4};
internalCallback.onClientRegistrationUpdated(
new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId));
internalCallback.onRemoteTaskRequested(misMatchedClientId, taskId, data,
/* taskMaximumDurationInSec= */ 10);
assertWithMessage("Task ID").that(remoteTaskClient.getTaskId()).isNull();
assertWithMessage("Data").that(remoteTaskClient.getData()).isNull();
}
@Test
public void testReportRemoteTaskDone() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2);
String clientId = "clientId_testing";
String taskId = "taskId_testing";
prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null);
mRemoteAccessManager.reportRemoteTaskDone(taskId);
verify(mService).reportRemoteTaskDone(clientId, taskId);
}
@Test
public void testReportRemoteTaskDone_nullTaskId() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mRemoteAccessManager.reportRemoteTaskDone(/* taskId= */ null));
}
@Test
public void testReportRemoteTaskDone_noRegisteredClient() throws Exception {
assertThrows(IllegalStateException.class,
() -> mRemoteAccessManager.reportRemoteTaskDone("taskId_testing"));
}
@Test
public void testReportRemoteTaskDone_invalidTaskId() throws Exception {
RemoteTaskClient remoteTaskClient = new RemoteTaskClient(/* expectedCallbackCount= */ 2);
String clientId = "clientId_testing";
String taskId = "taskId_testing";
prepareRemoteTaskRequested(remoteTaskClient, clientId, taskId, /* data= */ null);
doThrow(IllegalStateException.class).when(mService)
.reportRemoteTaskDone(clientId, taskId);
assertThrows(IllegalStateException.class,
() -> mRemoteAccessManager.reportRemoteTaskDone(taskId));
}
@Test
public void testSetPowerStatePostTaskExecution() throws Exception {
int nextPowerState = CarRemoteAccessManager.NEXT_POWER_STATE_SUSPEND_TO_RAM;
boolean runGarageMode = true;
mRemoteAccessManager.setPowerStatePostTaskExecution(nextPowerState, runGarageMode);
verify(mService).setPowerStatePostTaskExecution(nextPowerState, runGarageMode);
}
@Test
public void testOnShutdownStarting() throws Exception {
RemoteTaskClientCallback remoteTaskClient = mock(RemoteTaskClientCallback.class);
String clientId = "clientId_testing";
String serviceId = "serviceId_testing";
String vehicleId = "vehicleId_testing";
String processorId = "processorId_testing";
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(remoteTaskClient);
internalCallback.onClientRegistrationUpdated(
new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId));
verify(remoteTaskClient, timeout(DEFAULT_TIMEOUT)).onRegistrationUpdated(any());
internalCallback.onShutdownStarting();
verify(remoteTaskClient, timeout(DEFAULT_TIMEOUT)).onShutdownStarting(
mFutureCaptor.capture());
CompletableRemoteTaskFuture future = mFutureCaptor.getValue();
verify(mService, never()).confirmReadyForShutdown(any());
future.complete();
verify(mService).confirmReadyForShutdown(clientId);
}
@Test
public void testScheduleInfoBuilder() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE,
TEST_START_TIME);
builder.setTaskData(TEST_TASK_DATA);
ScheduleInfo scheduleInfo = builder.setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC)
.build();
expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId())
.isEqualTo(TEST_SCHEDULE_ID);
expectWithMessage("taskType from ScheduleInfo").that(scheduleInfo.getTaskType())
.isEqualTo(TEST_TASK_TYPE);
expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData())
.isEqualTo(TEST_TASK_DATA);
expectWithMessage("startTimeInEpochSeconds from ScheduleInfo")
.that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME);
expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount())
.isEqualTo(TEST_TASK_COUNT);
expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic())
.isEqualTo(TEST_PERIODIC);
}
@Test
public void testScheduleInfoBuilder_enterGarageMode() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID,
TASK_TYPE_ENTER_GARAGE_MODE, TEST_START_TIME);
ScheduleInfo scheduleInfo = builder.setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC)
.build();
expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId())
.isEqualTo(TEST_SCHEDULE_ID);
expectWithMessage("taskType from ScheduleInfo").that(scheduleInfo.getTaskType())
.isEqualTo(TASK_TYPE_ENTER_GARAGE_MODE);
expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData())
.isEmpty();
expectWithMessage("startTimeInEpochSeconds from ScheduleInfo")
.that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME);
expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount())
.isEqualTo(TEST_TASK_COUNT);
expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic())
.isEqualTo(TEST_PERIODIC);
}
@Test
public void testScheduleInfoBuilder_invalidTaskType() throws Exception {
assertThrows(IllegalArgumentException.class, () -> new ScheduleInfo.Builder(
TEST_SCHEDULE_ID, /*taskType=*/-1234, TEST_START_TIME));
}
@Test
public void testScheduleInfoBuilder_buildTwiceNotAllowed() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE,
TEST_START_TIME);
builder.setTaskData(TEST_TASK_DATA).setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC)
.build();
assertThrows(IllegalStateException.class, () -> builder.build());
}
@Test
public void testScheduleInfoBuilder_nullScheduleId() {
assertThrows(IllegalArgumentException.class, () -> new ScheduleInfo.Builder(
/* scheduleId= */ null, TEST_TASK_TYPE, TEST_START_TIME));
}
@Test
public void testScheduleInfoBuilder_nullTaskData() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE,
TEST_START_TIME);
assertThrows(IllegalArgumentException.class, () -> builder.setTaskData(null));
}
@Test
public void testScheduleInfoBuilder_negativeCount() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID,
TEST_TASK_TYPE, TEST_START_TIME);
assertThrows(IllegalArgumentException.class, () -> builder.setCount(-1));
}
@Test
public void testScheduleInfoBuilder_nullPeriodic() {
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE,
TEST_START_TIME);
assertThrows(IllegalArgumentException.class, () -> builder.setPeriodic(null));
}
@Test
public void testIsTaskScheduleSupported() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
assertThat(mRemoteAccessManager.isTaskScheduleSupported()).isTrue();
}
@Test
public void testGetInVehicleTaskScheduler() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
assertThat(mRemoteAccessManager.getInVehicleTaskScheduler()).isNotNull();
}
@Test
public void testGetInVehicleTaskScheduler_notSupported() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(false);
assertThat(mRemoteAccessManager.getInVehicleTaskScheduler()).isNull();
}
private TaskScheduleInfo getTestTaskScheduleInfo() {
TaskScheduleInfo taskScheduleInfo = new TaskScheduleInfo();
taskScheduleInfo.scheduleId = TEST_SCHEDULE_ID;
taskScheduleInfo.taskType = TEST_TASK_TYPE;
taskScheduleInfo.taskData = TEST_TASK_DATA;
taskScheduleInfo.startTimeInEpochSeconds = TEST_START_TIME;
taskScheduleInfo.count = TEST_TASK_COUNT;
taskScheduleInfo.periodicInSeconds = TEST_PERIODIC.getSeconds();
return taskScheduleInfo;
}
@Test
public void testScheduleTask() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
ScheduleInfo.Builder builder = new ScheduleInfo.Builder(TEST_SCHEDULE_ID, TEST_TASK_TYPE,
TEST_START_TIME);
ScheduleInfo scheduleInfo = builder.setTaskData(TEST_TASK_DATA).setCount(TEST_TASK_COUNT)
.setPeriodic(TEST_PERIODIC).build();
mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(scheduleInfo);
verify(mService).scheduleTask(getTestTaskScheduleInfo());
}
@Test
public void testScheduleTask_nullScheduleInfo() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
assertThrows(IllegalArgumentException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(null));
}
@Test
public void testScheduleTask_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService)
.scheduleTask(any());
ScheduleInfo scheduleInfo = new ScheduleInfo.Builder(TEST_SCHEDULE_ID,
TEST_TASK_TYPE, TEST_START_TIME).setTaskData(TEST_TASK_DATA)
.setCount(TEST_TASK_COUNT).setPeriodic(TEST_PERIODIC).build();
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().scheduleTask(scheduleInfo));
}
@Test
public void testUnscheduleTask() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(TEST_SCHEDULE_ID);
verify(mService).unscheduleTask(TEST_SCHEDULE_ID);
}
@Test
public void testUnscheduleTask_nullScheduleId() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
assertThrows(IllegalArgumentException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(null));
}
@Test
public void testUnscheduleTask_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService)
.unscheduleTask(any());
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleTask(TEST_SCHEDULE_ID));
}
@Test
public void testGetAllPendingScheduledTasks() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
when(mService.getAllPendingScheduledTasks()).thenReturn(List.of(getTestTaskScheduleInfo()));
List scheduleInfoList = mRemoteAccessManager.getInVehicleTaskScheduler()
.getAllPendingScheduledTasks();
assertThat(scheduleInfoList).hasSize(1);
ScheduleInfo scheduleInfo = scheduleInfoList.get(0);
expectWithMessage("scheduleId from ScheduleInfo").that(scheduleInfo.getScheduleId())
.isEqualTo(TEST_SCHEDULE_ID);
expectWithMessage("taskData from ScheduleInfo").that(scheduleInfo.getTaskData())
.isEqualTo(TEST_TASK_DATA);
expectWithMessage("startTimeInEpochSeconds from ScheduleInfo")
.that(scheduleInfo.getStartTimeInEpochSeconds()).isEqualTo(TEST_START_TIME);
expectWithMessage("count from ScheduleInfo").that(scheduleInfo.getCount())
.isEqualTo(TEST_TASK_COUNT);
expectWithMessage("periodic from ScheduleInfo").that(scheduleInfo.getPeriodic())
.isEqualTo(TEST_PERIODIC);
}
@Test
public void testGetAllPendingScheduledTasks_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService)
.getAllPendingScheduledTasks();
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().getAllPendingScheduledTasks());
}
@Test
public void testUnscheduleAllTasks() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleAllTasks();
verify(mService).unscheduleAllTasks();
}
@Test
public void testUnscheduleAllTasks_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
doThrow(new ServiceSpecificException(SERVICE_ERROR_CODE_GENERAL)).when(mService)
.unscheduleAllTasks();
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().unscheduleAllTasks());
}
@Test
public void testIsTaskScheduled() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
when(mService.isTaskScheduled(TEST_SCHEDULE_ID)).thenReturn(true);
assertThat(mRemoteAccessManager.getInVehicleTaskScheduler()
.isTaskScheduled(TEST_SCHEDULE_ID)).isTrue();
}
@Test
public void testIsTaskScheduled_nullScheduleId() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
assertThrows(IllegalArgumentException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().isTaskScheduled(null));
}
@Test
public void testIsTaskScheduled_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
when(mService.isTaskScheduled(any())).thenThrow(new ServiceSpecificException(
SERVICE_ERROR_CODE_GENERAL));
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().isTaskScheduled(TEST_SCHEDULE_ID));
}
@Test
public void testGetSupportedTaskTypes() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
int[] taskTypes = new int[]{TASK_TYPE_CUSTOM, TASK_TYPE_ENTER_GARAGE_MODE};
when(mService.getSupportedTaskTypesForScheduling()).thenReturn(taskTypes);
assertThat(mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes())
.isEqualTo(taskTypes);
}
@Test
public void testGetSupportedTaskTypes_RemoteException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
when(mService.getSupportedTaskTypesForScheduling()).thenThrow(new RemoteException());
assertThat(mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes())
.isEmpty();
}
@Test
public void testGetSupportedTaskTypes_ServiceSpecificException() throws Exception {
when(mService.isTaskScheduleSupported()).thenReturn(true);
when(mService.getSupportedTaskTypesForScheduling()).thenThrow(
new ServiceSpecificException(0));
assertThrows(InVehicleTaskSchedulerException.class, () ->
mRemoteAccessManager.getInVehicleTaskScheduler().getSupportedTaskTypes());
}
private ICarRemoteAccessCallback setClientAndGetCallback(RemoteTaskClientCallback client)
throws Exception {
ArgumentCaptor internalCallbackCaptor =
ArgumentCaptor.forClass(ICarRemoteAccessCallback.class);
mRemoteAccessManager.setRemoteTaskClient(mExecutor, client);
verify(mService).addCarRemoteTaskClient(internalCallbackCaptor.capture());
return internalCallbackCaptor.getValue();
}
private void prepareRemoteTaskRequested(RemoteTaskClient client, String clientId,
String taskId, byte[] data) throws Exception {
ICarRemoteAccessCallback internalCallback = setClientAndGetCallback(client);
String serviceId = "serviceId_testing";
String vehicleId = "vehicleId_testing";
String processorId = "processorId_testing";
internalCallback.onClientRegistrationUpdated(
new RemoteTaskClientRegistrationInfo(serviceId, vehicleId, processorId, clientId));
internalCallback.onRemoteTaskRequested(clientId, taskId, data,
/* taskMaximumDurationInSec= */ 10);
}
private static final class RemoteTaskClient implements RemoteTaskClientCallback {
private static final int DEFAULT_TIMEOUT = 3000;
private final CountDownLatch mLatch;
private String mServiceId;
private String mVehicleId;
private String mProcessorId;
private String mClientId;
private String mTaskId;
private boolean mRegistrationFailed;
private boolean mServerlessClientRegistered;
private byte[] mData;
private RemoteTaskClient(int expectedCallbackCount) {
mLatch = new CountDownLatch(expectedCallbackCount);
}
@Override
public void onRegistrationUpdated(RemoteTaskClientRegistrationInfo info) {
mServiceId = info.getServiceId();
mVehicleId = info.getVehicleId();
mProcessorId = info.getProcessorId();
mClientId = info.getClientId();
mLatch.countDown();
}
@Override
public void onServerlessClientRegistered() {
mServerlessClientRegistered = true;
mLatch.countDown();
}
@Override
public void onRegistrationFailed() {
mRegistrationFailed = true;
mLatch.countDown();
}
@Override
public void onRemoteTaskRequested(String taskId, byte[] data, int remainingTimeSec) {
mTaskId = taskId;
mData = data;
mLatch.countDown();
}
@Override
public void onShutdownStarting(CarRemoteAccessManager.CompletableRemoteTaskFuture future) {
mLatch.countDown();
}
public String getServiceId() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mServiceId;
}
public String getVehicleId() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mVehicleId;
}
public String getProcessorId() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mProcessorId;
}
public String getClientId() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mClientId;
}
public String getTaskId() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mTaskId;
}
public byte[] getData() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mData;
}
public boolean isRegistrationFail() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mRegistrationFailed;
}
public boolean isServerlessClientRegistered() throws Exception {
JavaMockitoHelper.await(mLatch, DEFAULT_TIMEOUT);
return mServerlessClientRegistered;
}
}
}