blob: 97a928ff82949978fb3276a7d42625af1fac4c29 [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.vms.IVmsSubscriberClient;
import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsOperationRecorder;
import android.car.vms.VmsSubscriptionState;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
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.
*/
public class VmsRouting {
private final Object mLock = new Object();
@GuardedBy("mLock")
private Map<IBinder, IVmsSubscriberClient> mSubscribers = new ArrayMap<>();
@GuardedBy("mLock")
private Set<IBinder> mPassiveSubscribers = new ArraySet<>();
@GuardedBy("mLock")
private Map<VmsLayer, Set<IBinder>> mLayerSubscriptions = new ArrayMap<>();
@GuardedBy("mLock")
private Map<VmsLayer, Map<Integer, Set<IBinder>>> mLayerSubscriptionsToPublishers =
new ArrayMap<>();
@GuardedBy("mLock")
private int mSequenceNumber = 0;
/**
* Add a passive subscription to all data messages.
*
* Passive subscribers receive all published data messages, but are not reflected in the
* subscription state sent to publishers.
*
* @param subscriber VMS subscriber to add
*/
public void addSubscription(IVmsSubscriberClient subscriber) {
int sequenceNumber;
synchronized (mLock) {
if (!mPassiveSubscribers.add(addSubscriber(subscriber))) {
return;
}
sequenceNumber = mSequenceNumber;
}
VmsOperationRecorder.get().addPromiscuousSubscription(sequenceNumber);
}
/**
* Remove a passive subscription to all data messages.
*
* @param subscriber VMS subscriber to remove
*/
public void removeSubscription(IVmsSubscriberClient subscriber) {
int sequenceNumber;
synchronized (mLock) {
if (!mPassiveSubscribers.remove(subscriber.asBinder())) {
return;
}
sequenceNumber = mSequenceNumber;
}
VmsOperationRecorder.get().removePromiscuousSubscription(sequenceNumber);
}
/**
* Add a subscription to data messages from a VMS layer.
*
* @param subscriber VMS subscriber to add
* @param layer the layer to subscribe to
*/
public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
int sequenceNumber;
synchronized (mLock) {
Set<IBinder> subscribers =
mLayerSubscriptions.computeIfAbsent(layer, k -> new ArraySet<>());
if (!subscribers.add(addSubscriber(subscriber))) {
return;
}
sequenceNumber = ++mSequenceNumber;
}
VmsOperationRecorder.get().addSubscription(sequenceNumber, layer);
}
/**
* Remove a subscription to data messages from a VMS layer.
*
* @param subscriber VMS subscriber to remove
* @param layer the subscribed layer
*/
public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
int sequenceNumber;
synchronized (mLock) {
Set<IBinder> subscribers =
mLayerSubscriptions.getOrDefault(layer, Collections.emptySet());
if (!subscribers.remove(subscriber.asBinder())) {
return;
}
sequenceNumber = ++mSequenceNumber;
if (subscribers.isEmpty()) {
// If a layer has no subscribers, remove it
mLayerSubscriptions.remove(layer);
}
}
VmsOperationRecorder.get().removeSubscription(sequenceNumber, layer);
}
/**
* Add a subscription to data messages from a VMS layer and a specific publisher.
*
* @param subscriber VMS subscriber to add
* @param layer the layer to subscribe to
* @param publisherId the publisher ID
*/
public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId) {
int sequenceNumber;
synchronized (mLock) {
Set<IBinder> subscribers =
mLayerSubscriptionsToPublishers.computeIfAbsent(layer, k -> new ArrayMap<>())
.computeIfAbsent(publisherId, k -> new ArraySet<>());
if (!subscribers.add(addSubscriber(subscriber))) {
return;
}
sequenceNumber = ++mSequenceNumber;
}
VmsOperationRecorder.get().addSubscription(sequenceNumber, layer);
}
/**
* Remove a subscription to data messages from a VMS layer and a specific publisher.
*
* @param subscriber VMS subscriber to remove
* @param layer the subscribed layer
* @param publisherId the publisher ID
*/
public void removeSubscription(IVmsSubscriberClient subscriber,
VmsLayer layer,
int publisherId) {
int sequenceNumber;
synchronized (mLock) {
Map<Integer, Set<IBinder>> subscribersToPublishers =
mLayerSubscriptionsToPublishers.getOrDefault(layer, Collections.emptyMap());
Set<IBinder> subscribers =
subscribersToPublishers.getOrDefault(publisherId, Collections.emptySet());
if (!subscribers.remove(subscriber.asBinder())) {
return;
}
sequenceNumber = ++mSequenceNumber;
// If a publisher has no subscribers, remove it
if (subscribers.isEmpty()) {
subscribersToPublishers.remove(publisherId);
}
// If a layer has no subscribers, remove it
if (subscribersToPublishers.isEmpty()) {
mLayerSubscriptionsToPublishers.remove(layer);
}
}
VmsOperationRecorder.get().removeSubscription(sequenceNumber, layer);
}
/**
* Remove all of a subscriber's subscriptions.
*
* @param subscriber VMS subscriber to remove
* @return {@code true} if the subscription state was modified
*/
public boolean removeDeadSubscriber(IVmsSubscriberClient subscriber) {
IBinder subscriberBinder = subscriber.asBinder();
synchronized (mLock) {
int startSequenceNumber = mSequenceNumber;
// Remove the subscriber from the loggers.
removeSubscription(subscriber);
// Remove the subscriber from all layer-based subscriptions.
mLayerSubscriptions.entrySet().stream()
.filter(e -> e.getValue().contains(subscriberBinder))
.map(Map.Entry::getKey)
.collect(Collectors.toSet())
.forEach(layer -> removeSubscription(subscriber, layer));
// Remove the subscriber from all publisher-based subscriptions.
mLayerSubscriptionsToPublishers.entrySet().stream()
.flatMap(layer -> layer.getValue().entrySet().stream()
.filter(publisher -> publisher.getValue().contains(subscriberBinder))
.map(publisher -> Pair.create(layer.getKey(), publisher.getKey())))
.collect(Collectors.toSet())
.forEach(layerAndPublisher -> removeSubscription(subscriber,
layerAndPublisher.first, layerAndPublisher.second));
// Remove the subscriber from the subscriber index
mSubscribers.remove(subscriberBinder);
// If the sequence number was updated, then the subscription state was modified
return startSequenceNumber != mSequenceNumber;
}
}
/**
* Returns a list of all the subscribers a data message should be delivered to. This includes
* subscribers that subscribed to this layer from all publishers, subscribed to this layer
* from a specific publisher, and passive 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<IBinder> subscribers = new HashSet<>();
synchronized (mLock) {
// Add the passive subscribers
subscribers.addAll(mPassiveSubscribers);
// Add the subscribers which explicitly subscribed to this layer
subscribers.addAll(mLayerSubscriptions.getOrDefault(layer, Collections.emptySet()));
// Add the subscribers which explicitly subscribed to this layer and publisher
subscribers.addAll(
mLayerSubscriptionsToPublishers.getOrDefault(layer, Collections.emptyMap())
.getOrDefault(publisherId, Collections.emptySet()));
}
return subscribers.stream()
.map(binder -> mSubscribers.get(binder))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
/**
* @return {@code true} if there is an explicit subscription to the layer
*/
public boolean hasLayerSubscriptions(VmsLayer layer) {
synchronized (mLock) {
return mLayerSubscriptions.containsKey(layer);
}
}
/**
* @return {@code true} if there is an explicit subscription to the layer and publisherId
*/
public boolean hasLayerFromPublisherSubscriptions(VmsLayer layer, int publisherId) {
synchronized (mLock) {
return mLayerSubscriptionsToPublishers.containsKey(layer)
&& mLayerSubscriptionsToPublishers.getOrDefault(layer, Collections.emptyMap())
.containsKey(publisherId);
}
}
/**
* @return a Set of layers and publishers which VMS clients are subscribed to.
*/
public VmsSubscriptionState getSubscriptionState() {
synchronized (mLock) {
return new VmsSubscriptionState(mSequenceNumber,
new ArraySet<>(mLayerSubscriptions.keySet()),
mLayerSubscriptionsToPublishers.entrySet()
.stream()
.map(e -> new VmsAssociatedLayer(e.getKey(), e.getValue().keySet()))
.collect(Collectors.toSet()));
}
}
private IBinder addSubscriber(IVmsSubscriberClient subscriber) {
IBinder subscriberBinder = subscriber.asBinder();
synchronized (mLock) {
mSubscribers.putIfAbsent(subscriberBinder, subscriber);
}
return subscriberBinder;
}
}