blob: 2e66781d39c82ae46aa066ae44ea1c86c59ceed9 [file] [log] [blame]
/*
* Copyright (C) 2017 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 android.car.annotation.FutureFeature;
import android.car.vms.IVmsSubscriberClient;
import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsOperationRecorder;
import android.car.vms.VmsSubscriptionState;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Manages all the VMS subscriptions:
* + Subscriptions to data messages of individual layer + version.
* + Subscriptions to all data messages.
* + HAL subscriptions to layer + version.
*/
@FutureFeature
public class VmsRouting {
private final Object mLock = new Object();
// A map of Layer + Version to subscribers.
@GuardedBy("mLock")
private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions = new HashMap<>();
@GuardedBy("mLock")
private Map<VmsLayer, Map<Integer, Set<IVmsSubscriberClient>>> mLayerSubscriptionsToPublishers =
new HashMap<>();
// A set of subscribers that are interested in any layer + version.
@GuardedBy("mLock")
private Set<IVmsSubscriberClient> mPromiscuousSubscribers = new HashSet<>();
// A set of all the layers + versions the HAL is subscribed to.
@GuardedBy("mLock")
private Set<VmsLayer> mHalSubscriptions = new HashSet<>();
@GuardedBy("mLock")
private Map<VmsLayer, Set<Integer>> mHalSubscriptionsToPublishers = new HashMap<>();
// A sequence number that is increased every time the subscription state is modified. Note that
// modifying the list of promiscuous subscribers does not affect the subscription state.
@GuardedBy("mLock")
private int mSequenceNumber = 0;
/**
* Add a subscriber subscription to data messages from a VMS layer.
*
* @param subscriber a VMS subscriber.
* @param layer the layer subscribing to.
*/
public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
//TODO(b/36902947): revise if need to sync, and return value.
synchronized (mLock) {
++mSequenceNumber;
// Get or create the list of subscribers for layer and version.
Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
if (subscribers == null) {
subscribers = new HashSet<>();
mLayerSubscriptions.put(layer, subscribers);
}
// Add the subscriber to the list.
subscribers.add(subscriber);
VmsOperationRecorder.get().addSubscription(mSequenceNumber, layer);
}
}
/**
* Add a subscriber subscription to all data messages.
*
* @param subscriber a VMS subscriber.
*/
public void addSubscription(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
++mSequenceNumber;
mPromiscuousSubscribers.add(subscriber);
VmsOperationRecorder.get().addPromiscuousSubscription(mSequenceNumber);
}
}
/**
* Add a subscriber subscription to data messages from a VMS layer from a specific publisher.
*
* @param subscriber a VMS subscriber.
* @param layer the layer to subscribing to.
* @param publisherId the publisher ID.
*/
public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId) {
synchronized (mLock) {
++mSequenceNumber;
Map<Integer, Set<IVmsSubscriberClient>> publisherIdsToSubscribersForLayer =
mLayerSubscriptionsToPublishers.get(layer);
if (publisherIdsToSubscribersForLayer == null) {
publisherIdsToSubscribersForLayer = new HashMap<>();
mLayerSubscriptionsToPublishers.put(layer, publisherIdsToSubscribersForLayer);
}
Set<IVmsSubscriberClient> subscribersForPublisher =
publisherIdsToSubscribersForLayer.get(publisherId);
if (subscribersForPublisher == null) {
subscribersForPublisher = new HashSet<>();
publisherIdsToSubscribersForLayer.put(publisherId, subscribersForPublisher);
}
// Add the subscriber to the list.
subscribersForPublisher.add(subscriber);
}
}
/**
* Remove a subscription for a layer + version and make sure to remove the key if there are no
* more subscribers.
*
* @param subscriber to remove.
* @param layer of the subscription.
*/
public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
// If there are no subscribers we are done.
if (subscribers == null) {
return;
}
subscribers.remove(subscriber);
VmsOperationRecorder.get().removeSubscription(mSequenceNumber, layer);
// If there are no more subscribers then remove the list.
if (subscribers.isEmpty()) {
mLayerSubscriptions.remove(layer);
}
}
}
/**
* Remove a subscriber subscription to all data messages.
*
* @param subscriber a VMS subscriber.
*/
public void removeSubscription(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
++mSequenceNumber;
mPromiscuousSubscribers.remove(subscriber);
VmsOperationRecorder.get().removePromiscuousSubscription(mSequenceNumber);
}
}
/**
* Remove a subscription to data messages from a VMS layer from a specific publisher.
*
* @param subscriber a VMS subscriber.
* @param layer the layer to unsubscribing from.
* @param publisherId the publisher ID.
*/
public void removeSubscription(IVmsSubscriberClient subscriber,
VmsLayer layer,
int publisherId) {
synchronized (mLock) {
++mSequenceNumber;
Map<Integer, Set<IVmsSubscriberClient>> subscribersToPublishers =
mLayerSubscriptionsToPublishers.get(layer);
if (subscribersToPublishers == null) {
return;
}
Set<IVmsSubscriberClient> subscribers = subscribersToPublishers.get(publisherId);
if (subscribers == null) {
return;
}
subscribers.remove(subscriber);
if (subscribers.isEmpty()) {
subscribersToPublishers.remove(publisherId);
}
if (subscribersToPublishers.isEmpty()) {
mLayerSubscriptionsToPublishers.remove(layer);
}
}
}
/**
* Remove a subscriber from all routes (optional operation).
*
* @param subscriber a VMS subscriber.
*/
public void removeDeadSubscriber(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
// Remove the subscriber from all the routes.
for (VmsLayer layer : mLayerSubscriptions.keySet()) {
removeSubscription(subscriber, layer);
}
// Remove the subscriber from the loggers.
removeSubscription(subscriber);
}
}
/**
* Returns a list of all the subscribers for a layer from a publisher. This includes
* subscribers that subscribed to this layer from all publishers, subscribed to this layer
* from a specific publisher, and the promiscuous subscribers.
*
* @param layer The layer of the message.
* @param publisherId the ID of the client that published the message to be routed.
* @return a list of the subscribers.
*/
public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer,
int publisherId) {
Set<IVmsSubscriberClient> subscribers = new HashSet<>();
synchronized (mLock) {
// Add the subscribers which explicitly subscribed to this layer
if (mLayerSubscriptions.containsKey(layer)) {
subscribers.addAll(mLayerSubscriptions.get(layer));
}
// Add the subscribers which explicitly subscribed to this layer and publisher
if (mLayerSubscriptionsToPublishers.containsKey(layer)) {
if (mLayerSubscriptionsToPublishers.get(layer).containsKey(publisherId)) {
subscribers.addAll(mLayerSubscriptionsToPublishers.get(layer).get(publisherId));
}
}
// Add the promiscuous subscribers.
subscribers.addAll(mPromiscuousSubscribers);
}
return subscribers;
}
/**
* Returns a list with all the subscribers.
*/
public Set<IVmsSubscriberClient> getAllSubscribers() {
Set<IVmsSubscriberClient> subscribers = new HashSet<>();
synchronized (mLock) {
for (VmsLayer layer : mLayerSubscriptions.keySet()) {
subscribers.addAll(mLayerSubscriptions.get(layer));
}
// Add the promiscuous subscribers.
subscribers.addAll(mPromiscuousSubscribers);
}
return subscribers;
}
/**
* Checks if a subscriber is subscribed to any messages.
*
* @param subscriber that may have subscription.
* @return true if the subscriber uis subscribed to messages.
*/
public boolean containsSubscriber(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
// Check if subscriber is subscribed to a layer.
for (Set<IVmsSubscriberClient> layerSubscribers : mLayerSubscriptions.values()) {
if (layerSubscribers.contains(subscriber)) {
return true;
}
}
// Check is subscriber is subscribed to all data messages.
return mPromiscuousSubscribers.contains(subscriber);
}
}
/**
* Add a layer and version to the HAL subscriptions.
*
* @param layer the HAL subscribes to.
*/
public void addHalSubscription(VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
mHalSubscriptions.add(layer);
VmsOperationRecorder.get().addHalSubscription(mSequenceNumber, layer);
}
}
public void addHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
synchronized (mLock) {
++mSequenceNumber;
Set<Integer> publisherIdsForLayer = mHalSubscriptionsToPublishers.get(layer);
if (publisherIdsForLayer == null) {
publisherIdsForLayer = new HashSet<>();
mHalSubscriptionsToPublishers.put(layer, publisherIdsForLayer);
}
publisherIdsForLayer.add(publisherId);
}
}
/**
* remove a layer and version to the HAL subscriptions.
*
* @param layer the HAL unsubscribes from.
*/
public void removeHalSubscription(VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
mHalSubscriptions.remove(layer);
VmsOperationRecorder.get().removeHalSubscription(mSequenceNumber, layer);
}
}
public void removeHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
synchronized (mLock) {
++mSequenceNumber;
Set<Integer> publisherIdsForLayer = mHalSubscriptionsToPublishers.get(layer);
if (publisherIdsForLayer == null) {
return;
}
publisherIdsForLayer.remove(publisherId);
if (publisherIdsForLayer.isEmpty()) {
mHalSubscriptionsToPublishers.remove(layer);
}
}
}
/**
* checks if the HAL is subscribed to a layer.
*
* @param layer
* @return true if the HAL is subscribed to layer.
*/
public boolean isHalSubscribed(VmsLayer layer) {
synchronized (mLock) {
return mHalSubscriptions.contains(layer);
}
}
/**
* checks if there are subscribers to a layer.
*
* @param layer
* @return true if there are subscribers to layer.
*/
public boolean hasLayerSubscriptions(VmsLayer layer) {
synchronized (mLock) {
return mLayerSubscriptions.containsKey(layer) || mHalSubscriptions.contains(layer);
}
}
/**
* returns true if there is already a subscription for the layer from publisherId.
*
* @param layer
* @param publisherId
* @return
*/
public boolean hasLayerFromPublisherSubscriptions(VmsLayer layer, int publisherId) {
synchronized (mLock) {
boolean hasClientSubscription =
mLayerSubscriptionsToPublishers.containsKey(layer) &&
mLayerSubscriptionsToPublishers.get(layer).containsKey(publisherId);
boolean hasHalSubscription = mHalSubscriptionsToPublishers.containsKey(layer) &&
mHalSubscriptionsToPublishers.get(layer).contains(publisherId);
return hasClientSubscription || hasHalSubscription;
}
}
/**
* @return a Set of layers and versions which VMS clients are subscribed to.
*/
public VmsSubscriptionState getSubscriptionState() {
synchronized (mLock) {
Set<VmsLayer> layers = new HashSet<>();
layers.addAll(mLayerSubscriptions.keySet());
layers.addAll(mHalSubscriptions);
Set<VmsAssociatedLayer> layersFromPublishers = new HashSet<>();
layersFromPublishers.addAll(mLayerSubscriptionsToPublishers.entrySet()
.stream()
.map(e -> new VmsAssociatedLayer(e.getKey(), e.getValue().keySet()))
.collect(Collectors.toSet()));
layersFromPublishers.addAll(mHalSubscriptionsToPublishers.entrySet()
.stream()
.map(e -> new VmsAssociatedLayer(e.getKey(), e.getValue()))
.collect(Collectors.toSet()));
return new VmsSubscriptionState(mSequenceNumber, layers, layersFromPublishers);
}
}
}