diff options
author | Asaf Rosenfeld <asafro@google.com> | 2017-03-31 14:32:10 -0700 |
---|---|---|
committer | Asaf Rosenfeld <asafro@google.com> | 2017-04-05 14:46:53 -0700 |
commit | eb541d4fae5171ca480069ff821581226df3ddb0 (patch) | |
tree | 9acf9c02636ca2fe6e4fb787358d478c4252836a | |
parent | f50230d087f356300957af2317b4b0f4df6631e6 (diff) | |
download | Car-eb541d4fae5171ca480069ff821581226df3ddb0.tar.gz |
Adding availability HAL support
Test: Added tests that inject offering and check change in availability. Also verified the following pass:
runtest -x ../tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
runtest -x ../tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
runtest -x ../tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
runtest -x ../tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
runtest -x ../tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
Change-Id: Ia641499a60e79a2c8665257d94e1050a070da18d
14 files changed, 542 insertions, 132 deletions
diff --git a/car-lib/src/android/car/vms/VmsLayer.java b/car-lib/src/android/car/vms/VmsLayer.java index 702daec9ef..afd0ae7835 100644 --- a/car-lib/src/android/car/vms/VmsLayer.java +++ b/car-lib/src/android/car/vms/VmsLayer.java @@ -24,8 +24,10 @@ import java.util.Objects; /** * A VMS Layer which can be subscribed to by VMS clients. - * Consists of the layer ID and the layer version. + * Consists of the layer ID and the layer major version. * + * This class does not contain the minor version since all minor version are backward and forward + * compatible and should not be used for routing messages. * @hide */ @FutureFeature diff --git a/car-lib/src/android/car/vms/VmsLayerDependency.java b/car-lib/src/android/car/vms/VmsLayerDependency.java index bb588ebf7a..e14c7ecbc2 100644 --- a/car-lib/src/android/car/vms/VmsLayerDependency.java +++ b/car-lib/src/android/car/vms/VmsLayerDependency.java @@ -80,6 +80,10 @@ public final class VmsLayerDependency implements Parcelable { } }; + public String toString() { + return "VmsLayerDependency{ Layer: " + mLayer + " Dependency: " + mDependency + "}"; + } + @Override public void writeToParcel(Parcel out, int flags) { out.writeParcelable(mLayer, flags); diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.java b/car-lib/src/android/car/vms/VmsLayersOffering.java index 12b3dd30b2..51a0b995be 100644 --- a/car-lib/src/android/car/vms/VmsLayersOffering.java +++ b/car-lib/src/android/car/vms/VmsLayersOffering.java @@ -55,6 +55,11 @@ public final class VmsLayersOffering implements Parcelable { }; @Override + public String toString() { + return "VmsLayersOffering{" + mDependencies+ "}"; + } + + @Override public void writeToParcel(Parcel out, int flags) { out.writeParcelableList(mDependencies, flags); } diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java index 2743ff157b..85fd2c2b8d 100644 --- a/car-lib/src/android/car/vms/VmsPublisherClientService.java +++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java @@ -106,6 +106,41 @@ public abstract class VmsPublisherClientService extends Service { if (DBG) { Log.d(TAG, "Publishing for layer : " + layer); } + + IBinder token = getTokenForPublisherServiceThreadSafe(); + + try { + mVmsPublisherService.publish(token, layer, payload); + return true; + } catch (RemoteException e) { + Log.e(TAG, "unable to publish message: " + payload, e); + } + return false; + } + + /** + * Uses the VmsPublisherService binder to set the layers offering. + * + * @param offering the layers that the publisher may publish. + * @return if the call to VmsPublisherService.setLayersOffering was successful. + */ + public final boolean setLayersOffering(VmsLayersOffering offering) { + if (DBG) { + Log.d(TAG, "Setting layers offering : " + offering); + } + + IBinder token = getTokenForPublisherServiceThreadSafe(); + + try { + mVmsPublisherService.setLayersOffering(token, offering); + return true; + } catch (RemoteException e) { + Log.e(TAG, "unable to set layers offering: " + offering, e); + } + return false; + } + + private IBinder getTokenForPublisherServiceThreadSafe() { if (mVmsPublisherService == null) { throw new IllegalStateException("VmsPublisherService not set."); } @@ -117,13 +152,7 @@ public abstract class VmsPublisherClientService extends Service { if (token == null) { throw new IllegalStateException("VmsPublisherService does not have a valid token."); } - try { - mVmsPublisherService.publish(token, layer, payload); - return true; - } catch (RemoteException e) { - Log.e(TAG, "unable to publish message: " + payload, e); - } - return false; + return token; } /** diff --git a/service/src/com/android/car/VmsLayersAvailability.java b/service/src/com/android/car/VmsLayersAvailability.java index 5f5ac3085e..d6e89f2bc0 100644 --- a/service/src/com/android/car/VmsLayersAvailability.java +++ b/service/src/com/android/car/VmsLayersAvailability.java @@ -83,7 +83,7 @@ public class VmsLayersAvailability { /** * Returns a collection of all the layers which may be published. */ - public Collection<VmsLayer> getAvailableLayers() { + public Set<VmsLayer> getAvailableLayers() { synchronized (mLock) { return mAvailableLayers; } @@ -93,7 +93,7 @@ public class VmsLayersAvailability { * Returns a collection of all the layers which publishers could have published if the * dependencies were satisfied. */ - public Collection<VmsLayer> getUnavailableLayers() { + public Set<VmsLayer> getUnavailableLayers() { synchronized (mLock) { return mUnavailableLayers; } diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java index 8f7fba353b..8bb0167d8d 100644 --- a/service/src/com/android/car/VmsPublisherService.java +++ b/service/src/com/android/car/VmsPublisherService.java @@ -56,7 +56,6 @@ public class VmsPublisherService extends IVmsPublisherService.Stub private final Context mContext; private final VmsHalService mHal; private final VmsPublisherManager mPublisherManager; - private final Map<IBinder, VmsLayersOffering> mRawOffering = new HashMap<>(); public VmsPublisherService(Context context, VmsHalService hal) { mContext = context; @@ -96,12 +95,7 @@ public class VmsPublisherService extends IVmsPublisherService.Stub @Override public void setLayersOffering(IBinder token, VmsLayersOffering offering) { - // Store the raw dependencies - mRawOffering.put(token, offering); - - //TODO(asafro): Calculate the new available layers - - //TODO(asafro): Notify the subscribers that there is a change in availability + mHal.setPublisherLayersOffering(token, offering); } // Implements IVmsPublisherService interface. diff --git a/service/src/com/android/car/VmsRouting.java b/service/src/com/android/car/VmsRouting.java index fc2cbaca7b..2829cc0623 100644 --- a/service/src/com/android/car/VmsRouting.java +++ b/service/src/com/android/car/VmsRouting.java @@ -20,16 +20,14 @@ import android.car.annotation.FutureFeature; import android.car.vms.IVmsSubscriberClient; import android.car.vms.VmsLayer; import android.car.vms.VmsSubscriptionState; - +import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import com.android.internal.annotations.GuardedBy; - /** * Manages all the VMS subscriptions: * + Subscriptions to data messages of individual layer + version. @@ -62,6 +60,7 @@ public class VmsRouting { * @param layer the layer subscribing to. */ public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) { + //TODO(b/36902947): revise if need to sync, and return value. synchronized (mLock) { ++mSequenceNumber; // Get or create the list of listeners for layer and version. @@ -142,8 +141,8 @@ public class VmsRouting { } /** - * Returns all the listeners for a layer and version. This include the subscribers which - * explicitly subscribed to this layer and version and the promiscuous subscribers. + * Returns a list with all the listeners for a layer and version. This include the subscribers + * which explicitly subscribed to this layer and version and the promiscuous subscribers. * * @param layer to get listeners to. * @return a list of the listeners. @@ -162,6 +161,21 @@ public class VmsRouting { } /** + * Returns a list with all the listeners. + */ + public Set<IVmsSubscriberClient> getAllListeners() { + Set<IVmsSubscriberClient> listeners = new HashSet<>(); + synchronized (mLock) { + for (VmsLayer layer : mLayerSubscriptions.keySet()) { + listeners.addAll(mLayerSubscriptions.get(layer)); + } + // Add the promiscuous subscribers. + listeners.addAll(mPromiscuousSubscribers); + } + return listeners; + } + + /** * Checks if a listener is subscribed to any messages. * @param listener that may have subscription. * @return true if the listener uis subscribed to messages. diff --git a/service/src/com/android/car/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java index 97ed27ff8c..eabb0a6a1a 100644 --- a/service/src/com/android/car/VmsSubscriberService.java +++ b/service/src/com/android/car/VmsSubscriberService.java @@ -269,18 +269,13 @@ public class VmsSubscriberService extends IVmsSubscriberService.Stub // Implements VmsHalSubscriberListener interface @Override - public void onChange(VmsLayer layer, byte[] payload) { + public void onDataMessage(VmsLayer layer, byte[] payload) { if(DBG) { Log.d(TAG, "Publishing a message for layer: " + layer); } Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer); - // If there are no listeners we're done. - if ((listeners == null)) { - return; - } - for (IVmsSubscriberClient subscriber : listeners) { try { subscriber.onVmsMessageReceived(layer, payload); @@ -290,6 +285,24 @@ public class VmsSubscriberService extends IVmsSubscriberService.Stub Log.e(TAG, "onVmsMessageReceived calling failed: ", e); } } + } + + @Override + public void onLayersAvaiabilityChange(List<VmsLayer> availableLayers) { + if(DBG) { + Log.d(TAG, "Publishing layers availability change: " + availableLayers); + } + + Set<IVmsSubscriberClient> listeners = mHal.getAllListeners(); + for (IVmsSubscriberClient subscriber : listeners) { + try { + subscriber.onLayersAvailabilityChange(availableLayers); + } catch (RemoteException e) { + // If we could not send a record, its likely the connection snapped. Let the binder + // death handle the situation. + Log.e(TAG, "onLayersAvailabilityChange calling failed: ", e); + } + } } } diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java index c23f36a7e7..8a361178c7 100644 --- a/service/src/com/android/car/hal/VmsHalService.java +++ b/service/src/com/android/car/hal/VmsHalService.java @@ -22,24 +22,32 @@ import android.car.VehicleAreaType; import android.car.annotation.FutureFeature; import android.car.vms.IVmsSubscriberClient; import android.car.vms.VmsLayer; +import android.car.vms.VmsLayerDependency; +import android.car.vms.VmsLayersOffering; import android.car.vms.VmsSubscriptionState; import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; import android.hardware.automotive.vehicle.V2_1.VehicleProperty; -import android.hardware.automotive.vehicle.V2_1.VmsMessageIntegerValuesIndex; +import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex; import android.hardware.automotive.vehicle.V2_1.VmsMessageType; -import android.os.SystemClock; +import android.hardware.automotive.vehicle.V2_1.VmsOfferingMessageIntegerValuesIndex; +import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex; +import android.os.Binder; +import android.os.IBinder; import android.util.Log; import com.android.car.CarLog; +import com.android.car.VmsLayersAvailability; import com.android.car.VmsRouting; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; @@ -49,25 +57,27 @@ import java.util.concurrent.CopyOnWriteArrayList; */ @FutureFeature public class VmsHalService extends HalServiceBase { + private static final boolean DBG = true; private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE; private static final String TAG = "VmsHalService"; - private static final Set<Integer> SUPPORTED_MESSAGE_TYPES = - new HashSet<Integer>( - Arrays.asList( - VmsMessageType.SUBSCRIBE, - VmsMessageType.UNSUBSCRIBE, - VmsMessageType.DATA)); private boolean mIsSupported = false; private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners = new CopyOnWriteArrayList<>(); private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners = new CopyOnWriteArrayList<>(); + + private final IBinder mHalPublisherToken = new Binder(); private final VehicleHal mVehicleHal; - @GuardedBy("mLock") - private VmsRouting mRouting = new VmsRouting(); - private final Object mLock = new Object(); + + private final Object mRoutingLock = new Object(); + private final VmsRouting mRouting = new VmsRouting(); + private final Object mAvailabilityLock = new Object(); + @GuardedBy("mAvailabilityLock") + private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>(); + @GuardedBy("mAvailabilityLock") + private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability(); /** * The VmsPublisherService implements this interface to receive data from the HAL. @@ -80,7 +90,11 @@ public class VmsHalService extends HalServiceBase { * The VmsSubscriberService implements this interface to receive data from the HAL. */ public interface VmsHalSubscriberListener { - void onChange(VmsLayer layer, byte[] payload); + // Notify listener on a data Message. + void onDataMessage(VmsLayer layer, byte[] payload); + + // Notify listener on a change in available layers. + void onLayersAvaiabilityChange(List<VmsLayer> availableLayers); } /** @@ -110,22 +124,22 @@ public class VmsHalService extends HalServiceBase { } public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) { - synchronized (mLock) { + boolean firstSubscriptionForLayer = false; + synchronized (mRoutingLock) { // Check if publishers need to be notified about this change in subscriptions. - boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); + firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); // Add the listeners subscription to the layer mRouting.addSubscription(listener, layer); - - // Notify the publishers - if (firstSubscriptionForLayer) { - notifyPublishers(layer, true); - } + } + if (firstSubscriptionForLayer) { + notifyPublishers(layer, true); } } public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) { - synchronized (mLock) { + boolean layerHasSubscribers = true; + synchronized (mRoutingLock) { if (!mRouting.hasLayerSubscriptions(layer)) { Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); return; @@ -135,67 +149,72 @@ public class VmsHalService extends HalServiceBase { mRouting.removeSubscription(listener, layer); // Check if publishers need to be notified about this change in subscriptions. - boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); - - // Notify the publishers - if (!layerHasSubscribers) { - notifyPublishers(layer, false); - } + layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); + } + if (!layerHasSubscribers) { + notifyPublishers(layer, false); } } public void addSubscription(IVmsSubscriberClient listener) { - synchronized (mLock) { + synchronized (mRoutingLock) { mRouting.addSubscription(listener); } } public void removeSubscription(IVmsSubscriberClient listener) { - synchronized (mLock) { + synchronized (mRoutingLock) { mRouting.removeSubscription(listener); } } public void removeDeadListener(IVmsSubscriberClient listener) { - synchronized (mLock) { + synchronized (mRoutingLock) { mRouting.removeDeadListener(listener); } } public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) { - synchronized (mLock) { + synchronized (mRoutingLock) { return mRouting.getListeners(layer); } } + public Set<IVmsSubscriberClient> getAllListeners() { + synchronized (mRoutingLock) { + return mRouting.getAllListeners(); + } + } + public boolean isHalSubscribed(VmsLayer layer) { - synchronized (mLock) { + synchronized (mRoutingLock) { return mRouting.isHalSubscribed(layer); } } public VmsSubscriptionState getSubscriptionState() { - synchronized (mLock) { + synchronized (mRoutingLock) { return mRouting.getSubscriptionState(); } } public void addHalSubscription(VmsLayer layer) { - synchronized (mLock) { + boolean firstSubscriptionForLayer = true; + synchronized (mRoutingLock) { // Check if publishers need to be notified about this change in subscriptions. - boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); + firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); // Add the listeners subscription to the layer mRouting.addHalSubscription(layer); - - if (firstSubscriptionForLayer) { - notifyPublishers(layer, true); - } + } + if (firstSubscriptionForLayer) { + notifyPublishers(layer, true); } } public void removeHalSubscription(VmsLayer layer) { - synchronized (mLock) { + boolean layerHasSubscribers = true; + synchronized (mRoutingLock) { if (!mRouting.hasLayerSubscriptions(layer)) { Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); return; @@ -205,21 +224,35 @@ public class VmsHalService extends HalServiceBase { mRouting.removeHalSubscription(layer); // Check if publishers need to be notified about this change in subscriptions. - boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); - - // Notify the publishers - if (!layerHasSubscribers) { - notifyPublishers(layer, false); - } + layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); + } + if (!layerHasSubscribers) { + notifyPublishers(layer, false); } } public boolean containsListener(IVmsSubscriberClient listener) { - synchronized (mLock) { + synchronized (mRoutingLock) { return mRouting.containsListener(listener); } } + public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){ + Set<VmsLayer> availableLayers = Collections.EMPTY_SET; + synchronized (mAvailabilityLock) { + updateOffering(publisherToken, offering); + availableLayers = mAvailableLayers.getAvailableLayers(); + } + notifySubscribers(availableLayers); + } + + public Set<VmsLayer> getAvailableLayers() { + //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState. + synchronized (mAvailabilityLock) { + return mAvailableLayers.getAvailableLayers(); + } + } + /** * Notify all the publishers and the HAL on subscription changes regardless of who triggered * the change. @@ -227,18 +260,31 @@ public class VmsHalService extends HalServiceBase { * @param layer layer which is being subscribed to or unsubscribed from. * @param hasSubscribers indicates if the notification is for subscription or unsubscription. */ - public void notifyPublishers(VmsLayer layer, boolean hasSubscribers) { - synchronized (mLock) { - // notify the HAL - setSubscriptionRequest(layer, hasSubscribers); - - // Notify the App publishers - for (VmsHalPublisherListener listener : mPublisherListeners) { - // Besides the list of layers, also a timestamp is provided to the clients. - // They should ignore any notification with a timestamp that is older than the most - // recent timestamp they have seen. - listener.onChange(getSubscriptionState()); - } + private void notifyPublishers(VmsLayer layer, boolean hasSubscribers) { + // notify the HAL + setSubscriptionRequest(layer, hasSubscribers); + + // Notify the App publishers + for (VmsHalPublisherListener listener : mPublisherListeners) { + // Besides the list of layers, also a timestamp is provided to the clients. + // They should ignore any notification with a timestamp that is older than the most + // recent timestamp they have seen. + listener.onChange(getSubscriptionState()); + } + } + + /** + * Notify all the subscribers and the HAL on layers availability change. + * + * @param availableLayers the layers which publishers claim they made publish. + */ + private void notifySubscribers(Set<VmsLayer> availableLayers) { + // notify the HAL + setAvailableLayers(availableLayers); + + // Notify the App subscribers + for (VmsHalSubscriberListener listener : mSubscriberListeners) { + listener.onLayersAvaiabilityChange(new ArrayList<>(availableLayers)); } } @@ -288,40 +334,126 @@ public class VmsHalService extends HalServiceBase { } for (VehiclePropValue v : values) { ArrayList<Integer> vec = v.value.int32Values; - int messageType = vec.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE); - int layerId = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_ID); - int layerVersion = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_VERSION); - - // Check if message type is supported. - if (!SUPPORTED_MESSAGE_TYPES.contains(messageType)) { - throw new IllegalArgumentException("Unexpected message type. " + - "Expecting: " + SUPPORTED_MESSAGE_TYPES + - ". Got: " + messageType); - - } + int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE); if (DBG) { - Log.d(TAG, - "Received message for Type: " + messageType + - " Layer Id: " + layerId + - "Version: " + layerVersion); + Log.d(TAG, "Handling VMS message type: " + messageType); } - // This is a data message intended for subscribers. - if (messageType == VmsMessageType.DATA) { - // Get the payload. - byte[] payload = toByteArray(v.value.bytes); - - // Send the message. - for (VmsHalSubscriberListener listener : mSubscriberListeners) { - listener.onChange(new VmsLayer(layerId, layerVersion), payload); - } - } else if (messageType == VmsMessageType.SUBSCRIBE) { - addHalSubscription(new VmsLayer(layerId, layerVersion)); + switch(messageType) { + case VmsMessageType.DATA: + handleDataEvent(vec, toByteArray(v.value.bytes)); + break; + case VmsMessageType.SUBSCRIBE: + handleSubscribeEvent(vec); + break; + case VmsMessageType.UNSUBSCRIBE: + handleUnsubscribeEvent(vec); + break; + case VmsMessageType.OFFERING: + handleOfferingEvent(vec); + break; + case VmsMessageType.AVAILABILITY: + handleAvailabilityEvent(); + break; + default: + throw new IllegalArgumentException("Unexpected message type: " + messageType); + } + } + } + + private void handleDataEvent(List<Integer> integerValues, byte[] payload) { + int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID); + int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION); + if (DBG) { + Log.d(TAG, + "Handling a data event for Layer Id: " + layerId + + " Version: " + layerVersion); + } + + // Send the message. + for (VmsHalSubscriberListener listener : mSubscriberListeners) { + listener.onDataMessage(new VmsLayer(layerId, layerVersion), payload); + } + } + + private void handleSubscribeEvent(List<Integer> integerValues) { + int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID); + int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION); + if (DBG) { + Log.d(TAG, + "Handling a subscribe event for Layer Id: " + layerId + + " Version: " + layerVersion); + } + addHalSubscription(new VmsLayer(layerId, layerVersion)); + } + + private void handleUnsubscribeEvent(List<Integer> integerValues) { + int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID); + int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION); + if (DBG) { + Log.d(TAG, + "Handling an unsubscribe event for Layer Id: " + layerId + + " Version: " + layerVersion); + } + removeHalSubscription(new VmsLayer(layerId, layerVersion)); + } + + private void handleOfferingEvent(List<Integer> integerValues) { + int numLayersDependencies = + integerValues.get(VmsOfferingMessageIntegerValuesIndex.VMS_NUMBER_OF_LAYERS_DEPENDENCIES); + int idx = VmsOfferingMessageIntegerValuesIndex.FIRST_DEPENDENCIES_INDEX; + + List<VmsLayerDependency> offeredLayers = new ArrayList<>(); + + // An offering is layerId, LayerVersion, NumDeps, <LayerId, LayerVersion> X NumDeps. + for (int i = 0; i < numLayersDependencies; i++) { + int layerId = integerValues.get(idx++); + int layerVersion = integerValues.get(idx++); + VmsLayer offeredLayer = new VmsLayer(layerId, layerVersion); + + int numDependenciesForLayer = integerValues.get(idx++); + if (numDependenciesForLayer == 0) { + offeredLayers.add(new VmsLayerDependency(offeredLayer)); } else { - // messageType == VmsMessageType.UNSUBSCRIBE - removeHalSubscription(new VmsLayer(layerId, layerVersion)); + Set<VmsLayer> dependencies = new HashSet<>(); + + for (int j = 0; j < numDependenciesForLayer; j++) { + int dependantLayerId = integerValues.get(idx++); + int dependantLayerVersion = integerValues.get(idx++); + + VmsLayer dependantLayer = new VmsLayer(dependantLayerId, dependantLayerVersion); + dependencies.add(dependantLayer); + } + offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies)); } } + // Store the HAL offering. + VmsLayersOffering offering = new VmsLayersOffering(offeredLayers); + synchronized (mAvailabilityLock) { + updateOffering(mHalPublisherToken, offering); + } + } + + private void handleAvailabilityEvent() { + synchronized (mAvailabilityLock) { + Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers(); + VehiclePropValue vehiclePropertyValue = toVehiclePropValue( + VmsMessageType.AVAILABILITY, availableLayers); + setPropertyValue(vehiclePropertyValue); + } + } + + private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) { + Set<VmsLayer> availableLayers = Collections.EMPTY_SET; + synchronized (mAvailabilityLock) { + mOfferings.put(publisherToken, offering); + + // Update layers availability. + mAvailableLayers.setPublishersOffering(mOfferings.values()); + + availableLayers = mAvailableLayers.getAvailableLayers(); + } + notifySubscribers(availableLayers); } @Override @@ -339,14 +471,21 @@ public class VmsHalService extends HalServiceBase { */ public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) { VehiclePropValue vehiclePropertyValue = toVehiclePropValue( - hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer); + hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer); return setPropertyValue(vehiclePropertyValue); } public boolean setDataMessage(VmsLayer layer, byte[] payload) { VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA, - layer, - payload); + layer, + payload); + return setPropertyValue(vehiclePropertyValue); + } + + public boolean setAvailableLayers(Collection<VmsLayer> availableLayers) { + VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.AVAILABILITY, + availableLayers); + return setPropertyValue(vehiclePropertyValue); } @@ -361,13 +500,20 @@ public class VmsHalService extends HalServiceBase { } /** Creates a {@link VehiclePropValue} */ - private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) { + private static VehiclePropValue toVehiclePropValue(int messageType) { VehiclePropValue vehicleProp = new VehiclePropValue(); vehicleProp.prop = HAL_PROPERTY_ID; vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE; VehiclePropValue.RawValue v = vehicleProp.value; v.int32Values.add(messageType); + return vehicleProp; + } + + /** Creates a {@link VehiclePropValue} */ + private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) { + VehiclePropValue vehicleProp = toVehiclePropValue(messageType); + VehiclePropValue.RawValue v = vehicleProp.value; v.int32Values.add(layer.getId()); v.int32Values.add(layer.getVersion()); return vehicleProp; @@ -375,8 +521,8 @@ public class VmsHalService extends HalServiceBase { /** Creates a {@link VehiclePropValue} with payload */ private static VehiclePropValue toVehiclePropValue(int messageType, - VmsLayer layer, - byte[] payload) { + VmsLayer layer, + byte[] payload) { VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer); VehiclePropValue.RawValue v = vehicleProp.value; v.bytes.ensureCapacity(payload.length); @@ -385,4 +531,18 @@ public class VmsHalService extends HalServiceBase { } return vehicleProp; } -}
\ No newline at end of file + + /** Creates a {@link VehiclePropValue} with payload */ + private static VehiclePropValue toVehiclePropValue(int messageType, + Collection<VmsLayer> layers) { + VehiclePropValue vehicleProp = toVehiclePropValue(messageType); + VehiclePropValue.RawValue v = vehicleProp.value; + int numLayers = layers.size(); + v.int32Values.add(numLayers); + for (VmsLayer layer : layers) { + v.int32Values.add(layer.getId()); + v.int32Values.add(layer.getVersion()); + } + return vehicleProp; + } +} diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java index fbea6d27bb..cc7342154c 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java +++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java @@ -18,8 +18,12 @@ package com.android.car.test; import android.car.annotation.FutureFeature; import android.car.vms.VmsLayer; +import android.car.vms.VmsLayerDependency; +import android.car.vms.VmsLayersOffering; import android.car.vms.VmsPublisherClientService; import android.car.vms.VmsSubscriptionState; +import java.util.List; +import java.util.ArrayList; /** * This service is launched during the tests in VmsPublisherSubscriberTest. It publishes a property @@ -36,12 +40,14 @@ public class VmsPublisherClientMockService extends VmsPublisherClientService { public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) { // Case when the publisher finished initialization before the subscription request. publishIfNeeded(subscriptionState); + declareOffering(subscriptionState); } @Override public void onVmsPublisherServiceReady() { // Case when the subscription request was sent before the publisher was ready. - publishIfNeeded(getSubscriptions()); + VmsSubscriptionState subscriptionState = getSubscriptions(); + publishIfNeeded(subscriptionState); } private void publishIfNeeded(VmsSubscriptionState subscriptionState) { @@ -51,4 +57,13 @@ public class VmsPublisherClientMockService extends VmsPublisherClientService { } } } + + private void declareOffering(VmsSubscriptionState subscriptionState) { + List<VmsLayerDependency> dependencies = new ArrayList<>(); + for( VmsLayer layer : subscriptionState.getLayers()) { + dependencies.add(new VmsLayerDependency(layer)); + } + VmsLayersOffering offering = new VmsLayersOffering(dependencies); + setLayersOffering(offering); + } } diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java index aa1431e13d..c22f63abe6 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java +++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java @@ -28,7 +28,8 @@ import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; import android.hardware.automotive.vehicle.V2_1.VehicleProperty; -import android.hardware.automotive.vehicle.V2_1.VmsMessageIntegerValuesIndex; +import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex; +import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex; import android.hardware.automotive.vehicle.V2_1.VmsMessageType; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; @@ -139,9 +140,9 @@ public class VmsPublisherClientServiceTest extends MockedCarTestBase { // the semaphore will not be released. assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS)); VehiclePropValue.RawValue rawValue = mHalHandler.getValue().value; - int messageType = rawValue.int32Values.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE); - int layerId = rawValue.int32Values.get(VmsMessageIntegerValuesIndex.VMS_LAYER_ID); - int layerVersion = rawValue.int32Values.get(VmsMessageIntegerValuesIndex.VMS_LAYER_VERSION); + int messageType = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE); + int layerId = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID); + int layerVersion = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION); byte[] payload = new byte[rawValue.bytes.size()]; for (int i = 0; i < rawValue.bytes.size(); ++i) { payload[i] = rawValue.bytes.get(i); @@ -161,7 +162,7 @@ public class VmsPublisherClientServiceTest extends MockedCarTestBase { // If this is the data message release the semaphone so the test can continue. ArrayList<Integer> int32Values = value.value.int32Values; - if (int32Values.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) == + if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) == VmsMessageType.DATA) { mHalHandlerSemaphore.release(); } diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java index e86b3fa9ea..20fa0b6476 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java +++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java @@ -32,7 +32,9 @@ import android.hardware.automotive.vehicle.V2_1.VehicleProperty; import com.android.car.vehiclehal.test.MockedVehicleHal; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -45,10 +47,12 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_VERSION); public static final byte[] PAYLOAD = new byte[]{2, 3, 5, 7, 11, 13, 17}; + private static final List<VmsLayer> AVAILABLE_LAYERS = new ArrayList<>(Arrays.asList(LAYER)); private HalHandler mHalHandler; // Used to block until a value is propagated to the TestListener.onVmsMessageReceived. private Semaphore mSubscriberSemaphore; + private Semaphore mAvailabilitySemaphore; @Override protected synchronized void configureMockedHal() { @@ -92,6 +96,7 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { if (!VmsTestUtils.canRunTest(TAG)) return; super.setUp(); mSubscriberSemaphore = new Semaphore(0); + mAvailabilitySemaphore = new Semaphore(0); } @Override @@ -121,12 +126,30 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { assertTrue(Arrays.equals(PAYLOAD, listener.getPayload())); } + /** + * The Mock service offers all the subscribed layers as available layers, so in this + * test the listener subscribes to a layer and verifies that it gets the notification that it + * is available. + */ + public void testAvailability() throws Exception { + if (!VmsTestUtils.canRunTest(TAG)) return; + VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager( + Car.VMS_SUBSCRIBER_SERVICE); + TestListener listener = new TestListener(); + vmsSubscriberManager.setListener(listener); + vmsSubscriberManager.subscribe(LAYER); + + assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS)); + assertEquals(AVAILABLE_LAYERS, listener.getAvailalbeLayers()); + } + private class HalHandler implements MockedVehicleHal.VehicleHalPropertyHandler { } private class TestListener implements VmsSubscriberManager.VmsSubscriberClientListener { private VmsLayer mLayer; private byte[] mPayload; + private List<VmsLayer> mAvailableLayers; @Override public void onVmsMessageReceived(VmsLayer layer, byte[] payload) { @@ -139,9 +162,9 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { @Override public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) { - //TODO(asafro): test availability changes on publisher update when logic is implemented. - // for that need to add Offering support in VmsPublisherClientService - // and update VmsPublisherClientMockService + assertEquals(AVAILABLE_LAYERS, availableLayers); + mAvailableLayers = availableLayers; + mAvailabilitySemaphore.release(); } @Override @@ -156,5 +179,9 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { public byte[] getPayload() { return mPayload; } + + public List<VmsLayer> getAvailalbeLayers() { + return mAvailableLayers; + } } } diff --git a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java index 79cddad743..063d5cff32 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java +++ b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java @@ -32,6 +32,7 @@ import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import com.android.car.vehiclehal.VehiclePropValueBuilder; import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Semaphore; @@ -46,6 +47,20 @@ public class VmsSubscriberManagerTest extends MockedCarTestBase { private static final VmsLayer SUBSCRIPTION_LAYER = new VmsLayer(SUBSCRIPTION_LAYER_ID, SUBSCRIPTION_LAYER_VERSION); + private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_1 = 4; + private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1 = 5; + private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_1 = + new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_1, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1); + + private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_2 = 6; + private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2 = 7; + private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_2 = + new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_2, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2); + + private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_ID = 100; + private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION = 200; + + private HalHandler mHalHandler; // Used to block until the HAL property is updated in HalHandler.onPropertySet. private Semaphore mHalHandlerSemaphore; @@ -132,6 +147,120 @@ public class VmsSubscriberManagerTest extends MockedCarTestBase { assertTrue(Arrays.equals(expectedPayload, listener.getPayload())); } + + // Test injecting a value in the HAL and verifying it propagates to a subscriber. + public void testSimpleAvailableLayers() throws Exception { + if (!VmsTestUtils.canRunTest(TAG)) return; + VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager( + Car.VMS_SUBSCRIBER_SERVICE); + TestListener listener = new TestListener(); + vmsSubscriberManager.setListener(listener); + vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER); + + // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange. + VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE) + .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE) + .setTimestamp(SystemClock.elapsedRealtimeNanos()) + .build(); + /* + Offering: + Layer | Dependency + ==================== + (2, 3) | {} + + Expected availability: + {(2, 3)} + */ + v.value.int32Values.addAll( + Arrays.asList( + VmsMessageType.OFFERING, // MessageType + 1, // Number of offered layers + + SUBSCRIPTION_LAYER_ID, + SUBSCRIPTION_LAYER_VERSION, + 0 // number of dependencies for layer + ) + ); + + assertEquals(0, mSubscriberSemaphore.availablePermits()); + + List<VmsLayer> expectedAvailableLayers = new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER)); + + getMockedVehicleHal().injectEvent(v); + assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS)); + assertEquals(expectedAvailableLayers, listener.getAvailableLayers()); + } + + // Test injecting a value in the HAL and verifying it propagates to a subscriber. + public void testComplexAvailableLayers() throws Exception { + if (!VmsTestUtils.canRunTest(TAG)) return; + VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager( + Car.VMS_SUBSCRIBER_SERVICE); + TestListener listener = new TestListener(); + vmsSubscriberManager.setListener(listener); + vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER); + + // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange. + VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE) + .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE) + .setTimestamp(SystemClock.elapsedRealtimeNanos()) + .build(); + /* + Offering: + Layer | Dependency + ==================== + (2, 3) | {} + (4, 5) | {(2, 3)} + (6, 7) | {(2, 3), (4, 5)} + (6, 7) | {(100, 200)} + + Expected availability: + {(2, 3), (4, 5), (6, 7)} + */ + + v.value.int32Values.addAll( + Arrays.asList( + VmsMessageType.OFFERING, // MessageType + 4, // Number of offered layers + + SUBSCRIPTION_LAYER_ID, + SUBSCRIPTION_LAYER_VERSION, + 0, // number of dependencies for layer + + SUBSCRIPTION_DEPENDANT_LAYER_ID_1, + SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1, + 1, // number of dependencies for layer + SUBSCRIPTION_LAYER_ID, + SUBSCRIPTION_LAYER_VERSION, + + SUBSCRIPTION_DEPENDANT_LAYER_ID_2, + SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2, + 2, // number of dependencies for layer + SUBSCRIPTION_LAYER_ID, + SUBSCRIPTION_LAYER_VERSION, + SUBSCRIPTION_DEPENDANT_LAYER_ID_1, + SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1, + + SUBSCRIPTION_DEPENDANT_LAYER_ID_2, + SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2, + 1, // number of dependencies for layer + SUBSCRIPTION_UNSUPPORTED_LAYER_ID, + SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION + ) + ); + + assertEquals(0, mSubscriberSemaphore.availablePermits()); + + List<VmsLayer> expectedAvailableLayers = + new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER, + SUBSCRIPTION_DEPENDANT_LAYER_1, + SUBSCRIPTION_DEPENDANT_LAYER_2)); + + getMockedVehicleHal().injectEvent(v); + assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS)); + assertEquals(expectedAvailableLayers, listener.getAvailableLayers()); + } + private class HalHandler implements VehicleHalPropertyHandler { private VehiclePropValue mValue; @@ -165,6 +294,7 @@ public class VmsSubscriberManagerTest extends MockedCarTestBase { private class TestListener implements VmsSubscriberClientListener{ private VmsLayer mLayer; private byte[] mPayload; + private List<VmsLayer> mAvailableLayers = new ArrayList<>(); @Override public void onVmsMessageReceived(VmsLayer layer, byte[] payload) { @@ -177,6 +307,8 @@ public class VmsSubscriberManagerTest extends MockedCarTestBase { @Override public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) { Log.d(TAG, "onLayersAvailabilityChange: Layers: " + availableLayers); + mAvailableLayers.addAll(availableLayers); + mSubscriberSemaphore.release(); } @Override @@ -191,5 +323,9 @@ public class VmsSubscriberManagerTest extends MockedCarTestBase { public byte[] getPayload() { return mPayload; } + + public List<VmsLayer> getAvailableLayers() { + return mAvailableLayers; + } } } diff --git a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java index 07a0122dea..0a907e1076 100644 --- a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java +++ b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java @@ -21,6 +21,7 @@ import android.car.vms.VmsLayerDependency; import android.car.vms.VmsLayersOffering; import android.test.AndroidTestCase; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -62,6 +63,15 @@ public class VmsLayersAvailabilityTest extends AndroidTestCase { super.setUp(); } + public void testNoOffering() { + assertTrue(mLayersAvailability.getAvailableLayers().isEmpty()); + } + + public void testEmptyOffering() { + mLayersAvailability.setPublishersOffering(Collections.EMPTY_LIST); + assertTrue(mLayersAvailability.getAvailableLayers().isEmpty()); + } + public void testSingleLayerNoDeps() throws Exception { Set<VmsLayer> expectedAvailableLayers = new HashSet<>(); expectedAvailableLayers.add(LAYER_X); |