/* * Copyright (C) 2018 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.hal; import static com.android.car.CarServiceUtils.toByteArray; import static com.android.car.CarServiceUtils.toIntArray; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.car.hardware.property.VehicleHalStatusCode; import android.car.vms.VmsAssociatedLayer; import android.car.vms.VmsAvailableLayers; import android.car.vms.VmsClient; import android.car.vms.VmsClientManager.VmsClientCallback; import android.car.vms.VmsLayer; import android.car.vms.VmsLayerDependency; import android.car.vms.VmsSubscriptionState; import android.content.Context; import android.content.res.Resources; import android.hardware.automotive.vehicle.VehiclePropConfig; import android.hardware.automotive.vehicle.VehicleProperty; import android.hardware.automotive.vehicle.VehiclePropertyGroup; import android.hardware.automotive.vehicle.VmsMessageType; import android.os.Handler; import android.os.ServiceSpecificException; import com.android.car.R; import com.android.car.test.utils.TemporaryFile; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @RunWith(MockitoJUnitRunner.class) public class VmsHalServiceTest { private static final int LAYER_TYPE = 1; private static final int LAYER_SUBTYPE = 2; private static final int LAYER_VERSION = 3; private static final VmsLayer LAYER = new VmsLayer(LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION); private static final int PUBLISHER_ID = 12345; private static final byte[] PAYLOAD = new byte[]{1, 2, 3, 4}; private static final List PAYLOAD_AS_LIST = Arrays.asList(new Byte[]{1, 2, 3, 4}); private static final int CORE_ID = 54321; private static final int CLIENT_ID = 98765; private final HalPropValueBuilder mPropValueBuilder = new HalPropValueBuilder(/*isAidl=*/true); @Mock private Context mContext; @Mock private Resources mResources; @Mock private VehicleHal mVehicleHal; @Mock private VmsClient mVmsClient; private VmsHalService mHalService; private VmsClientCallback mEventCallback; private int mVmsInitCount; @Before public void setUp() throws Exception { initHalService(true); } private void initHalService(boolean propagatePropertyException) throws Exception { mVmsInitCount = 0; when(mContext.getResources()).thenReturn(mResources); when(mVehicleHal.getHalPropValueBuilder()).thenReturn(mPropValueBuilder); mHalService = new VmsHalService(mContext, mVehicleHal, () -> (long) CORE_ID, this::initVmsClient, propagatePropertyException); VehiclePropConfig propConfig = new VehiclePropConfig(); propConfig.prop = VehicleProperty.VEHICLE_MAP_SERVICE; HalPropConfig halPropConfig = new AidlHalPropConfig(propConfig); mHalService.takeProperties(Collections.singleton(halPropConfig)); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.emptySet(), 0)); mHalService.init(); waitForHandlerCompletion(); // Verify START_SESSION message was sent InOrder initOrder = Mockito.inOrder(mVehicleHal); initOrder.verify(mVehicleHal).subscribeProperty(mHalService, VehicleProperty.VEHICLE_MAP_SERVICE); initOrder.verify(mVehicleHal).set(createHalMessage( VmsMessageType.START_SESSION, // Message type CORE_ID, // Core ID -1)); // Client ID (unknown) // Verify no more interections until handshake received initOrder.verifyNoMoreInteractions(); // Send START_SESSION response from client sendHalMessage(createHalMessage( VmsMessageType.START_SESSION, // Message type CORE_ID, // Core ID CLIENT_ID // Client ID )); waitForHandlerCompletion(); initOrder.verify(mVehicleHal).set(createHalMessage( VmsMessageType.AVAILABILITY_CHANGE, // Message type 0, // Sequence number 0)); // # of associated layers waitForHandlerCompletion(); initOrder.verifyNoMoreInteractions(); assertEquals(1, mVmsInitCount); reset(mVmsClient, mVehicleHal); } private VmsClient initVmsClient(Handler handler, VmsClientCallback callback) { mEventCallback = callback; mVmsInitCount++; return mVmsClient; } @Test public void testCoreId_IntegerOverflow() throws Exception { when(mVehicleHal.getHalPropValueBuilder()).thenReturn(mPropValueBuilder); mHalService = new VmsHalService(mContext, mVehicleHal, () -> (long) Integer.MAX_VALUE + CORE_ID, this::initVmsClient, true); VehiclePropConfig propConfig = new VehiclePropConfig(); propConfig.prop = VehicleProperty.VEHICLE_MAP_SERVICE; HalPropConfig halPropConfig = new AidlHalPropConfig(propConfig); mHalService.takeProperties(Collections.singleton(halPropConfig)); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.emptySet(), 0)); mHalService.init(); waitForHandlerCompletion(); verify(mVehicleHal).set(createHalMessage( VmsMessageType.START_SESSION, // Message type CORE_ID, // Core ID -1)); // Client ID (unknown) } @Test public void testTakeSupportedProperties() { VehiclePropConfig vmsPropConfig = new VehiclePropConfig(); vmsPropConfig.prop = VehicleProperty.VEHICLE_MAP_SERVICE; HalPropConfig vmsHalPropConfig = new AidlHalPropConfig(vmsPropConfig); VehiclePropConfig otherPropConfig = new VehiclePropConfig(); otherPropConfig.prop = VehicleProperty.CURRENT_GEAR; HalPropConfig otherHalPropConfig = new AidlHalPropConfig(otherPropConfig); mHalService.takeProperties(Arrays.asList(vmsHalPropConfig, otherHalPropConfig)); } /** * DATA message format: * */ @Test public void testHandleDataEvent() { HalPropValue message = createHalMessage( toByteArray(PAYLOAD_AS_LIST), VmsMessageType.DATA, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // VmsLayer PUBLISHER_ID // PublisherId ); sendHalMessage(message); verify(mVmsClient).publishPacket(PUBLISHER_ID, LAYER, PAYLOAD); } @Test public void testHandleInvalidEvent() { // Invalid message with no message type. HalPropValue message = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, "1234"); sendHalMessage(message); verify(mVmsClient, Mockito.never()).publishPacket( Mockito.anyInt(), Mockito.any(), Mockito.any()); // Incomplete VmsLayer. ArrayList int32Values = new ArrayList( Arrays.asList(VmsMessageType.DATA, LAYER_TYPE, LAYER_SUBTYPE)); message = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, /*timestamp=*/0, /*status=*/0, /*int32Values=*/toIntArray(int32Values), /*floatValues=*/new float[0], /*int64Values=*/new long[0], "1234", /*byteValues=*/new byte[0]); sendHalMessage(message); verify(mVmsClient, Mockito.never()).publishPacket( Mockito.anyInt(), Mockito.any(), Mockito.any()); // No publisher ID. int32Values.add(LAYER_VERSION); message = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, /*timestamp=*/0, /*status=*/0, /*int32Values=*/toIntArray(int32Values), /*floatValues=*/new float[0], /*int64Values=*/new long[0], "1234", /*byteValues=*/new byte[0]); sendHalMessage(message); verify(mVmsClient, Mockito.never()).publishPacket( Mockito.anyInt(), Mockito.any(), Mockito.any()); // No payload. int32Values.add(PUBLISHER_ID); message = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, /*timestamp=*/0, /*status=*/0, /*int32Values=*/toIntArray(int32Values), /*floatValues=*/new float[0], /*int64Values=*/new long[0], "1234", /*byteValues=*/new byte[0]); sendHalMessage(message); verify(mVmsClient, Mockito.never()).publishPacket( Mockito.anyInt(), Mockito.any(), Mockito.any()); // No payload for publisherID event. int32Values.set(0, VmsMessageType.PUBLISHER_ID_REQUEST); message = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, /*timestamp=*/0, /*status=*/0, /*int32Values=*/toIntArray(int32Values), /*floatValues=*/new float[0], /*int64Values=*/new long[0], "1234", /*byteValues=*/new byte[0]); sendHalMessage(message); verify(mVmsClient, Mockito.never()).publishPacket( Mockito.anyInt(), Mockito.any(), Mockito.any()); } @Test public void testOnPacketReceivedEvent() throws Exception { HalPropValue message = createHalMessage( toByteArray(PAYLOAD_AS_LIST), VmsMessageType.DATA, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // VmsLayer PUBLISHER_ID // PublisherId ); mEventCallback.onPacketReceived(PUBLISHER_ID, LAYER, PAYLOAD); verify(mVehicleHal).set(message); } /** * SUBSCRIBE message format: *
    *
  • Message type *
  • Layer ID *
  • Layer subtype *
  • Layer version *
*/ @Test public void testHandleSubscribeEvent() { HalPropValue message = createHalMessage( VmsMessageType.SUBSCRIBE, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION // VmsLayer ); sendHalMessage(message); verify(mVmsClient).setSubscriptions(asSet(new VmsAssociatedLayer(LAYER, asSet()))); } /** * SUBSCRIBE_TO_PUBLISHER message format: *
    *
  • Message type *
  • Layer ID *
  • Layer subtype *
  • Layer version *
  • Publisher ID *
*/ @Test public void testHandleSubscribeToPublisherEvent() { HalPropValue message = createHalMessage( VmsMessageType.SUBSCRIBE_TO_PUBLISHER, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // VmsLayer PUBLISHER_ID // PublisherId ); sendHalMessage(message); verify(mVmsClient).setSubscriptions(asSet( new VmsAssociatedLayer(LAYER, asSet(PUBLISHER_ID)))); } /** * UNSUBSCRIBE message format: *
    *
  • Message type *
  • Layer ID *
  • Layer subtype *
  • Layer version *
*/ @Test public void testHandleUnsubscribeEvent() { testHandleSubscribeEvent(); HalPropValue message = createHalMessage( VmsMessageType.UNSUBSCRIBE, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION // VmsLayer ); sendHalMessage(message); verify(mVmsClient).setSubscriptions(asSet()); } /** * UNSUBSCRIBE_TO_PUBLISHER message format: *
    *
  • Message type *
  • Layer ID *
  • Layer subtype *
  • Layer version *
  • Publisher ID *
*/ @Test public void testHandleUnsubscribeFromPublisherEvent() { testHandleSubscribeToPublisherEvent(); HalPropValue message = createHalMessage( VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER, // Message type LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // VmsLayer PUBLISHER_ID // PublisherId ); sendHalMessage(message); verify(mVmsClient).setSubscriptions(asSet()); } /** * PUBLISHER_ID_REQUEST message format: *
    *
  • Message type *
  • Publisher info (bytes) *
* * PUBLISHER_ID_RESPONSE message format: *
    *
  • Message type *
  • Publisher ID *
*/ @Test public void testHandlePublisherIdRequestEvent() throws Exception { HalPropValue request = createHalMessage( toByteArray(PAYLOAD_AS_LIST), VmsMessageType.PUBLISHER_ID_REQUEST // Message type ); when(mVmsClient.registerProvider(PAYLOAD)).thenReturn(PUBLISHER_ID); HalPropValue response = createHalMessage( VmsMessageType.PUBLISHER_ID_RESPONSE, // Message type PUBLISHER_ID // Publisher ID ); sendHalMessage(request); verify(mVehicleHal).set(response); } /** * PUBLISHER_INFORMATION_REQUEST message format: *
    *
  • Message type *
  • Publisher ID *
* * PUBLISHER_INFORMATION_RESPONSE message format: *
    *
  • Message type *
  • Publisher info (bytes) *
*/ @Test public void testHandlePublisherInformationRequestEvent() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.PUBLISHER_INFORMATION_REQUEST, // Message type PUBLISHER_ID // Publisher ID ); when(mVmsClient.getProviderDescription(PUBLISHER_ID)).thenReturn(PAYLOAD); HalPropValue response = createHalMessage( toByteArray(PAYLOAD_AS_LIST), VmsMessageType.PUBLISHER_INFORMATION_RESPONSE // Message type ); sendHalMessage(request); verify(mVehicleHal).set(response); } /** * OFFERING message format: *
    *
  • Message type *
  • Publisher ID *
  • Number of offerings. *
  • Offerings (x number of offerings) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    • Number of layer dependencies. *
    • Layer dependencies (x number of layer dependencies) *
        *
      • Layer ID *
      • Layer subtype *
      • Layer version *
      *
    *
*/ @Test public void testHandleOfferingEvent_ZeroOfferings() { HalPropValue message = createHalMessage( VmsMessageType.OFFERING, // Message type PUBLISHER_ID, // PublisherId 0 // # of offerings ); sendHalMessage(message); verify(mVmsClient).setProviderOfferings(PUBLISHER_ID, asSet()); } @Test public void testHandleOfferingEvent_LayerOnly() { HalPropValue message = createHalMessage( VmsMessageType.OFFERING, // Message type PUBLISHER_ID, // PublisherId 1, // # of offerings // Offered layer LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, 0 // # of dependencies ); sendHalMessage(message); verify(mVmsClient).setProviderOfferings( PUBLISHER_ID, asSet(new VmsLayerDependency(LAYER))); } @Test public void testHandleOfferingEvent_LayerAndDependency() { HalPropValue message = createHalMessage( VmsMessageType.OFFERING, // Message type PUBLISHER_ID, // PublisherId 1, // # of offerings LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 1, // # of dependencies 4, 5, 6 // Dependency layer ); sendHalMessage(message); verify(mVmsClient).setProviderOfferings( PUBLISHER_ID, asSet( new VmsLayerDependency(LAYER, Collections.singleton( new VmsLayer(4, 5, 6))))); } @Test public void testHandleOfferingEvent_MultipleLayersAndDependencies() { HalPropValue message = createHalMessage( VmsMessageType.OFFERING, // Message type PUBLISHER_ID, // PublisherId 3, // # of offerings // Offered layer #1 LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 2, // # of dependencies 4, 5, 6, // Dependency layer 7, 8, 9, // Dependency layer // Offered layer #2 3, 2, 1, // Layer 0, // # of dependencies // Offered layer #3 6, 5, 4, // Layer 1, // # of dependencies 7, 8, 9 // Dependency layer ); sendHalMessage(message); verify(mVmsClient).setProviderOfferings( PUBLISHER_ID, asSet( new VmsLayerDependency(LAYER, new LinkedHashSet<>(Arrays.asList( new VmsLayer(4, 5, 6), new VmsLayer(7, 8, 9) ))), new VmsLayerDependency(new VmsLayer(3, 2, 1), Collections.emptySet()), new VmsLayerDependency(new VmsLayer(6, 5, 4), Collections.singleton( new VmsLayer(7, 8, 9) )))); } /** * AVAILABILITY_REQUEST message format: *
    *
  • Message type *
* * AVAILABILITY_RESPONSE message format: *
    *
  • Message type *
  • Sequence number. *
  • Number of associated layers. *
  • Associated layers (x number of associated layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    • Number of publishers *
    • Publisher ID (x number of publishers) *
    *
*/ @Test public void testHandleAvailabilityRequestEvent_ZeroLayers() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.AVAILABILITY_REQUEST // Message type ); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.emptySet(), 123)); HalPropValue response = createHalMessage( VmsMessageType.AVAILABILITY_RESPONSE, // Message type 123, // Sequence number 0 // # of associated layers ); sendHalMessage(request); verify(mVehicleHal).set(response); } @Test public void testHandleAvailabilityRequestEvent_OneLayer() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.AVAILABILITY_REQUEST // Message type ); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.singleton( new VmsAssociatedLayer(LAYER, Collections.singleton(PUBLISHER_ID))), 123)); HalPropValue response = createHalMessage( VmsMessageType.AVAILABILITY_RESPONSE, // Message type 123, // Sequence number 1, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 1, // # of publisher IDs PUBLISHER_ID // Publisher ID ); sendHalMessage(request); verify(mVehicleHal).set(response); } @Test public void testHandleAvailabilityRequestEvent_MultipleLayers() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.AVAILABILITY_REQUEST // Message type ); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(new LinkedHashSet<>(Arrays.asList( new VmsAssociatedLayer(LAYER, new LinkedHashSet<>(Arrays.asList(PUBLISHER_ID, 54321))), new VmsAssociatedLayer(new VmsLayer(3, 2, 1), Collections.emptySet()), new VmsAssociatedLayer(new VmsLayer(6, 5, 4), Collections.singleton(99999)))), 123)); HalPropValue response = createHalMessage( VmsMessageType.AVAILABILITY_RESPONSE, // Message type 123, // Sequence number 3, // # of associated layers // Associated layer #1 LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 2, // # of publisher IDs PUBLISHER_ID, // Publisher ID 54321, // Publisher ID #2 // Associated layer #2 3, 2, 1, // Layer 0, // # of publisher IDs // Associated layer #3 6, 5, 4, // Layer 1, // # of publisher IDs 99999 // Publisher ID ); sendHalMessage(request); verify(mVehicleHal).set(response); } /** * START_SESSION message format: *
    *
  • Message type *
  • Core ID *
  • Client ID *
*/ @Test public void testHandleStartSessionEvent() throws Exception { when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.emptySet(), 5)); HalPropValue request = createHalMessage( VmsMessageType.START_SESSION, // Message type -1, // Core ID (unknown) CLIENT_ID // Client ID ); HalPropValue response = createHalMessage( VmsMessageType.START_SESSION, // Message type CORE_ID, // Core ID CLIENT_ID // Client ID ); sendHalMessage(request); InOrder inOrder = Mockito.inOrder(mVehicleHal, mVmsClient); inOrder.verify(mVmsClient).unregister(); inOrder.verify(mVehicleHal).set(response); assertEquals(2, mVmsInitCount); waitForHandlerCompletion(); inOrder.verify(mVehicleHal).set(createHalMessage( VmsMessageType.AVAILABILITY_CHANGE, // Message type 5, // Sequence number 0)); // # of associated layers } /** * AVAILABILITY_CHANGE message format: *
    *
  • Message type *
  • Sequence number. *
  • Number of associated layers. *
  • Associated layers (x number of associated layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    • Number of publishers *
    • Publisher ID (x number of publishers) *
    *
*/ @Test public void testOnLayersAvailabilityChanged_ZeroLayers() throws Exception { mEventCallback.onLayerAvailabilityChanged( new VmsAvailableLayers(Collections.emptySet(), 123)); HalPropValue message = createHalMessage( VmsMessageType.AVAILABILITY_CHANGE, // Message type 123, // Sequence number 0 // # of associated layers ); waitForHandlerCompletion(); verify(mVehicleHal).set(message); } @Test public void testOnLayersAvailabilityChanged_OneLayer() throws Exception { mEventCallback.onLayerAvailabilityChanged( new VmsAvailableLayers(Collections.singleton( new VmsAssociatedLayer(LAYER, Collections.singleton(PUBLISHER_ID))), 123)); HalPropValue message = createHalMessage( VmsMessageType.AVAILABILITY_CHANGE, // Message type 123, // Sequence number 1, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 1, // # of publisher IDs PUBLISHER_ID // Publisher ID ); waitForHandlerCompletion(); verify(mVehicleHal).set(message); } @Test public void testOnLayersAvailabilityChanged_MultipleLayers() throws Exception { mEventCallback.onLayerAvailabilityChanged( new VmsAvailableLayers(new LinkedHashSet<>(Arrays.asList( new VmsAssociatedLayer(LAYER, new LinkedHashSet<>(Arrays.asList(PUBLISHER_ID, 54321))), new VmsAssociatedLayer(new VmsLayer(3, 2, 1), Collections.emptySet()), new VmsAssociatedLayer(new VmsLayer(6, 5, 4), Collections.singleton(99999)))), 123)); HalPropValue message = createHalMessage( VmsMessageType.AVAILABILITY_CHANGE, // Message type 123, // Sequence number 3, // # of associated layers // Associated layer #1 LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 2, // # of publisher IDs PUBLISHER_ID, // Publisher ID 54321, // Publisher ID #2 // Associated layer #2 3, 2, 1, // Layer 0, // # of publisher IDs // Associated layer #3 6, 5, 4, // Layer 1, // # of publisher IDs 99999 // Publisher ID ); waitForHandlerCompletion(); verify(mVehicleHal).set(message); } /** * SUBSCRIPTION_REQUEST message format: *
    *
  • Message type *
* * SUBSCRIPTION_RESPONSE message format: *
    *
  • Message type *
  • Sequence number *
  • Number of layers *
  • Number of associated layers *
  • Layers (x number of layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    *
  • Associated layers (x number of associated layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    • Number of publishers *
    • Publisher ID (x number of publishers) *
    *
*/ @Test public void testHandleSubscriptionsRequestEvent_ZeroLayers() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.SUBSCRIPTIONS_REQUEST // Message type ); when(mVmsClient.getSubscriptionState()).thenReturn( new VmsSubscriptionState(123, Collections.emptySet(), Collections.emptySet())); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_RESPONSE, // Message type 123, // Sequence number 0, // # of layers 0 // # of associated layers ); sendHalMessage(request); verify(mVehicleHal).set(response); } @Test public void testHandleSubscriptionsRequestEvent_OneLayer_ZeroAssociatedLayers() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.SUBSCRIPTIONS_REQUEST // Message type ); when(mVmsClient.getSubscriptionState()).thenReturn( new VmsSubscriptionState(123, Collections.singleton(LAYER), Collections.emptySet())); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_RESPONSE, // Message type 123, // Sequence number 1, // # of layers 0, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION // Layer ); sendHalMessage(request); verify(mVehicleHal).set(response); } @Test public void testHandleSubscriptionsRequestEvent_ZeroLayers_OneAssociatedLayer() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.SUBSCRIPTIONS_REQUEST // Message type ); when(mVmsClient.getSubscriptionState()).thenReturn( new VmsSubscriptionState(123, Collections.emptySet(), Collections.singleton( new VmsAssociatedLayer(LAYER, Collections.singleton(PUBLISHER_ID))))); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_RESPONSE, // Message type 123, // Sequence number 0, // # of layers 1, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 1, // # of publisher IDs PUBLISHER_ID // Publisher ID ); sendHalMessage(request); verify(mVehicleHal).set(response); } @Test public void testHandleSubscriptionsRequestEvent_MultipleLayersAndAssociatedLayers() throws Exception { HalPropValue request = createHalMessage( VmsMessageType.SUBSCRIPTIONS_REQUEST // Message type ); when(mVmsClient.getSubscriptionState()).thenReturn( new VmsSubscriptionState(123, new LinkedHashSet<>(Arrays.asList( LAYER, new VmsLayer(4, 5, 6), new VmsLayer(7, 8, 9) )), new LinkedHashSet<>(Arrays.asList( new VmsAssociatedLayer(LAYER, Collections.emptySet()), new VmsAssociatedLayer(new VmsLayer(6, 5, 4), new LinkedHashSet<>(Arrays.asList( PUBLISHER_ID, 54321)))))) ); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_RESPONSE, // Message type 123, // Sequence number 3, // # of layers 2, // # of associated layers // Layer #1 LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer // Layer #2 4, 5, 6, // Layer // Layer #3 7, 8, 9, // Layer LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 0, // # of publisher IDs 6, 5, 4, // Layer 2, // # of publisher IDs PUBLISHER_ID, // Publisher ID 54321 // Publisher ID #2 ); sendHalMessage(request); verify(mVehicleHal).set(response); } /** * SUBSCRIPTIONS_CHANGE message format: *
    *
  • Message type *
  • Sequence number *
  • Number of layers *
  • Number of associated layers *
  • Layers (x number of layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    *
  • Associated layers (x number of associated layers) *
      *
    • Layer ID *
    • Layer subtype *
    • Layer version *
    • Number of publishers *
    • Publisher ID (x number of publishers) *
    *
*/ @Test public void testOnVmsSubscriptionChange_ZeroLayers() throws Exception { mEventCallback.onSubscriptionStateChanged( new VmsSubscriptionState(123, Collections.emptySet(), Collections.emptySet())); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_CHANGE, // Message type 123, // Sequence number 0, // # of layers 0 // # of associated layers ); waitForHandlerCompletion(); verify(mVehicleHal).set(response); } @Test public void testOnVmsSubscriptionChange_OneLayer_ZeroAssociatedLayers() throws Exception { mEventCallback.onSubscriptionStateChanged( new VmsSubscriptionState(123, Collections.singleton(LAYER), Collections.emptySet())); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_CHANGE, // Message type 123, // Sequence number 1, // # of layers 0, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION // Layer ); waitForHandlerCompletion(); verify(mVehicleHal).set(response); } @Test public void testOnVmsSubscriptionChange_ZeroLayers_OneAssociatedLayer() throws Exception { mEventCallback.onSubscriptionStateChanged( new VmsSubscriptionState(123, Collections.emptySet(), Collections.singleton( new VmsAssociatedLayer(LAYER, Collections.singleton(PUBLISHER_ID))))); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_CHANGE, // Message type 123, // Sequence number 0, // # of layers 1, // # of associated layers LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 1, // # of publisher IDs PUBLISHER_ID // Publisher ID ); waitForHandlerCompletion(); verify(mVehicleHal).set(response); } @Test public void testOnVmsSubscriptionChange_MultipleLayersAndAssociatedLayers() throws Exception { mEventCallback.onSubscriptionStateChanged( new VmsSubscriptionState(123, new LinkedHashSet<>(Arrays.asList( LAYER, new VmsLayer(4, 5, 6), new VmsLayer(7, 8, 9) )), new LinkedHashSet<>(Arrays.asList( new VmsAssociatedLayer(LAYER, Collections.emptySet()), new VmsAssociatedLayer(new VmsLayer(6, 5, 4), new LinkedHashSet<>(Arrays.asList( PUBLISHER_ID, 54321)))))) ); HalPropValue response = createHalMessage( VmsMessageType.SUBSCRIPTIONS_CHANGE, // Message type 123, // Sequence number 3, // # of layers 2, // # of associated layers // Layer #1 LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer // Layer #2 4, 5, 6, // Layer // Layer #3 7, 8, 9, // Layer LAYER_TYPE, LAYER_SUBTYPE, LAYER_VERSION, // Layer 0, // # of publisher IDs 6, 5, 4, // Layer 2, // # of publisher IDs PUBLISHER_ID, // Publisher ID 54321 // Publisher ID #2 ); waitForHandlerCompletion(); verify(mVehicleHal).set(response); } @Test public void testPropertySetExceptionNotPropagated_CoreStartSession() throws Exception { doThrow(new RuntimeException()).when(mVehicleHal).set(any()); initHalService(false); waitForHandlerCompletion(); } @Test public void testPropertySetExceptionNotPropagated_ClientStartSession() throws Exception { initHalService(false); when(mVmsClient.getAvailableLayers()).thenReturn( new VmsAvailableLayers(Collections.emptySet(), 0)); doThrow(new RuntimeException()).when(mVehicleHal).set(any()); HalPropValue request = createHalMessage( VmsMessageType.START_SESSION, // Message type -1, // Core ID (unknown) CLIENT_ID // Client ID ); sendHalMessage(request); waitForHandlerCompletion(); } @Test public void testDumpMetrics_DefaultConfig() { mHalService.dumpMetrics(new FileDescriptor()); verifyZeroInteractions(mVehicleHal); } @Test public void testDumpMetrics_NonVendorProperty() throws Exception { HalPropValue vehicleProp = mPropValueBuilder.build(/*propId=*/0, /*areaId=*/0, toByteArray(PAYLOAD_AS_LIST)); when(mVehicleHal.get(anyInt())).thenReturn(vehicleProp); when(mResources.getInteger( R.integer.vmsHalClientMetricsProperty)).thenReturn( VehicleProperty.VEHICLE_MAP_SERVICE); setUp(); mHalService.dumpMetrics(new FileDescriptor()); verifyZeroInteractions(mVehicleHal); } @Test public void testDumpMetrics_VendorProperty() throws Exception { int metricsPropertyId = VehiclePropertyGroup.VENDOR | 1; when(mResources.getInteger( R.integer.vmsHalClientMetricsProperty)).thenReturn( metricsPropertyId); setUp(); HalPropValue metricsProperty = mPropValueBuilder.build(/*propId=*/0, /*areaId=*/0, toByteArray(PAYLOAD_AS_LIST)); when(mVehicleHal.get(metricsPropertyId)).thenReturn(metricsProperty); try (TemporaryFile dumpsysFile = new TemporaryFile("VmsHalServiceTest")) { try (FileOutputStream outputStream = new FileOutputStream(dumpsysFile.getFile())) { mHalService.dumpMetrics(outputStream.getFD()); verify(mVehicleHal).get(metricsPropertyId); } try (FileInputStream inputStream = new FileInputStream(dumpsysFile.getFile())) { byte[] dumpsysOutput = new byte[PAYLOAD.length]; assertEquals(PAYLOAD.length, inputStream.read(dumpsysOutput)); assertArrayEquals(PAYLOAD, dumpsysOutput); } } } @Test public void testDumpMetrics_VendorProperty_Timeout() throws Exception { int metricsPropertyId = VehiclePropertyGroup.VENDOR | 1; when(mResources.getInteger( R.integer.vmsHalClientMetricsProperty)).thenReturn( metricsPropertyId); setUp(); when(mVehicleHal.get(metricsPropertyId)) .thenThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN, "propertyId: 0x" + Integer.toHexString(metricsPropertyId))); mHalService.dumpMetrics(new FileDescriptor()); verify(mVehicleHal).get(metricsPropertyId); } @Test public void testDumpMetrics_VendorProperty_Unavailable() throws Exception { int metricsPropertyId = VehiclePropertyGroup.VENDOR | 1; when(mResources.getInteger( R.integer.vmsHalClientMetricsProperty)).thenReturn( metricsPropertyId); setUp(); when(mVehicleHal.get(metricsPropertyId)).thenReturn(null); mHalService.dumpMetrics(new FileDescriptor()); verify(mVehicleHal).get(metricsPropertyId); } private HalPropValue createHalMessage(Integer... message) { HalPropValue result = mPropValueBuilder.build(VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, toIntArray(Arrays.asList(message))); return result; } private HalPropValue createHalMessage(byte[] payload, Integer... message) { HalPropValue result = mPropValueBuilder.build( VehicleProperty.VEHICLE_MAP_SERVICE, /*areaId=*/0, /*timestamp=*/0, /*status=*/0, /*int32Values=*/toIntArray(Arrays.asList(message)), /*floatValues=*/new float[0], /*int64Values=*/new long[0], /*stringValue=*/new String(), /*byteValues=*/payload); return result; } private void sendHalMessage(HalPropValue message) { mHalService.onHalEvents(Collections.singletonList(message)); } private void waitForHandlerCompletion() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mHalService.getHandler().post(latch::countDown); latch.await(5, TimeUnit.SECONDS); } @SafeVarargs private static Set asSet(T... values) { return new HashSet<>(Arrays.asList(values)); } }