blob: a60201f45ca3bac98893d115939b44b6e425ce03 [file] [log] [blame]
/*
* Copyright (C) 2022 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 com.android.car;
import static android.car.hardware.property.CarPropertyManager.SENSOR_RATE_ONCHANGE;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
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.VehicleAreaType;
import android.car.VehiclePropertyIds;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.ICarPropertyEventListener;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.util.SparseArray;
import com.android.car.hal.PropertyHalService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public final class CarPropertyServiceUnitTest {
@Mock
private Context mContext;
@Mock
private PropertyHalService mHalService;
private String mReadPermission;
private CarPropertyService mService;
private static final int SPEED_ID = VehiclePropertyIds.PERF_VEHICLE_SPEED;
private static final int HVAC_TEMP = VehiclePropertyIds.HVAC_TEMPERATURE_SET;
@Before
public void setUp() {
mReadPermission = "READ_PERMISSION";
when(mContext.checkCallingOrSelfPermission(mReadPermission)).thenReturn(
PackageManager.PERMISSION_GRANTED);
SparseArray<CarPropertyConfig<?>> configs = new SparseArray<>();
configs.put(SPEED_ID, CarPropertyConfig.newBuilder(
Float.class, SPEED_ID,
VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL).build());
when(mHalService.getReadPermission(SPEED_ID)).thenReturn(mReadPermission);
// HVAC_TEMP is actually not a global property, but for simplicity, make it global here.
configs.put(HVAC_TEMP, CarPropertyConfig.newBuilder(
Float.class, HVAC_TEMP,
VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL).build());
when(mHalService.getReadPermission(HVAC_TEMP)).thenReturn(mReadPermission);
when(mHalService.getPropertyList()).thenReturn(configs);
mService = new CarPropertyService(mContext, mHalService);
mService.init();
}
@Test
public void testRegisterListenerNull() {
assertThrows(IllegalArgumentException.class,
() -> mService.registerListener(SPEED_ID, /* rate=*/ 10, /* listener= */ null));
}
@Test
public void testRegisterUnregisterForContinuousProperty() throws Exception {
ICarPropertyEventListener mMockHandler1 = mock(ICarPropertyEventListener.class);
ICarPropertyEventListener mMockHandler2 = mock(ICarPropertyEventListener.class);
// Must use two different binders because listener is uniquely identified by binder.
IBinder mBinder1 = mock(IBinder.class);
IBinder mBinder2 = mock(IBinder.class);
when(mMockHandler1.asBinder()).thenReturn(mBinder1);
when(mMockHandler2.asBinder()).thenReturn(mBinder2);
// Initially SPEED_ID is not subscribed, so should return -1.
when(mHalService.getSampleRate(SPEED_ID)).thenReturn(-1f);
CarPropertyValue mValue = mock(CarPropertyValue.class);
when(mHalService.getPropertySafe(SPEED_ID, 0)).thenReturn(mValue);
// Register the first listener.
mService.registerListener(SPEED_ID, /* rate= */ 10, mMockHandler1);
// Wait until we get the on property change event for the initial value.
verify(mMockHandler1, timeout(5000)).onEvent(any());
verify(mHalService).subscribeProperty(SPEED_ID, 10f);
verify(mHalService).getPropertySafe(SPEED_ID, 0);
// Clean up invocation state.
clearInvocations(mHalService);
when(mHalService.getSampleRate(SPEED_ID)).thenReturn(10f);
// Register the second listener.
mService.registerListener(SPEED_ID, /* rate= */ 20, mMockHandler2);
// Wait until we get the on property change event for the initial value.
verify(mMockHandler2, timeout(5000)).onEvent(any());
verify(mHalService).subscribeProperty(SPEED_ID, 20f);
verify(mHalService).getPropertySafe(SPEED_ID, 0);
// Clean up invocation state.
clearInvocations(mHalService);
when(mHalService.getSampleRate(SPEED_ID)).thenReturn(20f);
// Unregister the second listener, the first listener must still be registered.
mService.unregisterListener(SPEED_ID, mMockHandler2);
// The property must not be unsubscribed.
verify(mHalService, never()).unsubscribeProperty(anyInt());
// The subscription rate must be updated.
verify(mHalService).subscribeProperty(SPEED_ID, 10f);
when(mHalService.getSampleRate(SPEED_ID)).thenReturn(10f);
// Unregister the first listener. We have no more listeners, must cause unsubscription.
mService.unregisterListener(SPEED_ID, mMockHandler1);
verify(mHalService).unsubscribeProperty(SPEED_ID);
}
@Test
public void testRegisterUnregisterForOnChangeProperty() throws Exception {
ICarPropertyEventListener mMockHandler1 = mock(ICarPropertyEventListener.class);
ICarPropertyEventListener mMockHandler2 = mock(ICarPropertyEventListener.class);
// Must use two different binders because listener is uniquely identified by binder.
IBinder mBinder1 = mock(IBinder.class);
IBinder mBinder2 = mock(IBinder.class);
when(mMockHandler1.asBinder()).thenReturn(mBinder1);
when(mMockHandler2.asBinder()).thenReturn(mBinder2);
// Initially HVAC_TEMP is not subscribed, so should return -1.
when(mHalService.getSampleRate(HVAC_TEMP)).thenReturn(-1f);
CarPropertyValue mValue = mock(CarPropertyValue.class);
when(mHalService.getPropertySafe(HVAC_TEMP, 0)).thenReturn(mValue);
// Register the first listener.
mService.registerListener(HVAC_TEMP, /* rate= */ SENSOR_RATE_ONCHANGE, mMockHandler1);
// Wait until we get the on property change event for the initial value.
verify(mMockHandler1, timeout(5000)).onEvent(any());
verify(mHalService).subscribeProperty(HVAC_TEMP, 0f);
verify(mHalService).getPropertySafe(HVAC_TEMP, 0);
// Clean up invocation state.
clearInvocations(mHalService);
when(mHalService.getSampleRate(HVAC_TEMP)).thenReturn(0f);
// Register the second listener.
mService.registerListener(HVAC_TEMP, /* rate= */ SENSOR_RATE_ONCHANGE, mMockHandler2);
// Wait until we get the on property change event for the initial value.
verify(mMockHandler2, timeout(5000)).onEvent(any());
// Must not subscribe again.
verify(mHalService, never()).subscribeProperty(anyInt(), anyFloat());
verify(mHalService).getPropertySafe(HVAC_TEMP, 0);
// Clean up invocation state.
clearInvocations(mHalService);
// Unregister the second listener, the first listener must still be registered.
mService.unregisterListener(HVAC_TEMP, mMockHandler2);
// The property must not be unsubscribed.
verify(mHalService, never()).unsubscribeProperty(anyInt());
// Unregister the first listener. We have no more listeners, must cause unsubscription.
mService.unregisterListener(HVAC_TEMP, mMockHandler1);
verify(mHalService).unsubscribeProperty(HVAC_TEMP);
}
}