1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car;
18 
19 import android.car.annotation.FutureFeature;
20 import android.car.vms.IVmsSubscriberClient;
21 import android.car.vms.VmsLayer;
22 import android.car.vms.VmsSubscriptionState;
23 
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 /**
34  * Manages all the VMS subscriptions:
35  * + Subscriptions to data messages of individual layer + version.
36  * + Subscriptions to all data messages.
37  * + HAL subscriptions to layer + version.
38  */
39 @FutureFeature
40 public class VmsRouting {
41     private final Object mLock = new Object();
42     // A map of Layer + Version to listeners.
43     @GuardedBy("mLock")
44     private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions =
45         new HashMap<>();
46     // A set of listeners that are interested in any layer + version.
47     @GuardedBy("mLock")
48     private Set<IVmsSubscriberClient> mPromiscuousSubscribers =
49         new HashSet<>();
50     // A set of all the layers + versions the HAL is subscribed to.
51     @GuardedBy("mLock")
52     private Set<VmsLayer> mHalSubscriptions = new HashSet<>();
53     // A sequence number that is increased every time the subscription state is modified. Note that
54     // modifying the list of promiscuous subscribers does not affect the subscription state.
55     @GuardedBy("mLock")
56     private int mSequenceNumber = 0;
57 
58     /**
59      * Add a listener subscription to a data messages from layer + version.
60      *
61      * @param listener a VMS subscriber.
62      * @param layer the layer subscribing to.
63      */
addSubscription(IVmsSubscriberClient listener, VmsLayer layer)64     public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
65         synchronized (mLock) {
66             ++mSequenceNumber;
67             // Get or create the list of listeners for layer and version.
68             Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
69 
70             if (listeners == null) {
71                 listeners = new HashSet<>();
72                 mLayerSubscriptions.put(layer, listeners);
73             }
74             // Add the listener to the list.
75             listeners.add(listener);
76         }
77     }
78 
79     /**
80      * Add a listener subscription to all data messages.
81      *
82      * @param listener a VMS subscriber.
83      */
addSubscription(IVmsSubscriberClient listener)84     public void addSubscription(IVmsSubscriberClient listener) {
85         synchronized (mLock) {
86             ++mSequenceNumber;
87             mPromiscuousSubscribers.add(listener);
88         }
89     }
90 
91     /**
92      * Remove a subscription for a layer + version and make sure to remove the key if there are no
93      * more subscribers.
94      *
95      * @param listener to remove.
96      * @param layer of the subscription.
97      */
removeSubscription(IVmsSubscriberClient listener, VmsLayer layer)98     public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
99         synchronized (mLock) {
100             ++mSequenceNumber;
101             Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
102 
103             // If there are no listeners we are done.
104             if (listeners == null) {
105                 return;
106             }
107             listeners.remove(listener);
108 
109             // If there are no more listeners then remove the list.
110             if (listeners.isEmpty()) {
111                 mLayerSubscriptions.remove(layer);
112             }
113         }
114     }
115 
116     /**
117      * Remove a listener subscription to all data messages.
118      *
119      * @param listener a VMS subscriber.
120      */
removeSubscription(IVmsSubscriberClient listener)121     public void removeSubscription(IVmsSubscriberClient listener) {
122         synchronized (mLock) {
123             ++mSequenceNumber;
124             mPromiscuousSubscribers.remove(listener);
125         }
126     }
127 
128     /**
129      * Remove a subscriber from all routes (optional operation).
130      *
131      * @param listener a VMS subscriber.
132      */
removeDeadListener(IVmsSubscriberClient listener)133     public void removeDeadListener(IVmsSubscriberClient listener) {
134         synchronized (mLock) {
135             // Remove the listener from all the routes.
136             for (VmsLayer layer : mLayerSubscriptions.keySet()) {
137                 removeSubscription(listener, layer);
138             }
139             // Remove the listener from the loggers.
140             removeSubscription(listener);
141         }
142     }
143 
144     /**
145      * Returns all the listeners for a layer and version. This include the subscribers which
146      * explicitly subscribed to this layer and version and the promiscuous subscribers.
147      *
148      * @param layer to get listeners to.
149      * @return a list of the listeners.
150      */
getListeners(VmsLayer layer)151     public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
152         Set<IVmsSubscriberClient> listeners = new HashSet<>();
153         synchronized (mLock) {
154             // Add the subscribers which explicitly subscribed to this layer and version
155             if (mLayerSubscriptions.containsKey(layer)) {
156                 listeners.addAll(mLayerSubscriptions.get(layer));
157             }
158             // Add the promiscuous subscribers.
159             listeners.addAll(mPromiscuousSubscribers);
160         }
161         return listeners;
162     }
163 
164     /**
165      * Checks if a listener is subscribed to any messages.
166      * @param listener that may have subscription.
167      * @return true if the listener uis subscribed to messages.
168      */
containsListener(IVmsSubscriberClient listener)169     public boolean containsListener(IVmsSubscriberClient listener) {
170         synchronized (mLock) {
171             // Check if listener is subscribed to a layer.
172             for (Set<IVmsSubscriberClient> layerListeners: mLayerSubscriptions.values()) {
173                 if (layerListeners.contains(listener)) {
174                     return true;
175                 }
176             }
177             // Check is listener is subscribed to all data messages.
178             return mPromiscuousSubscribers.contains(listener);
179         }
180     }
181 
182     /**
183      * Add a layer and version to the HAL subscriptions.
184      * @param layer the HAL subscribes to.
185      */
addHalSubscription(VmsLayer layer)186     public void addHalSubscription(VmsLayer layer) {
187         synchronized (mLock) {
188             ++mSequenceNumber;
189             mHalSubscriptions.add(layer);
190         }
191     }
192 
193     /**
194      * remove a layer and version to the HAL subscriptions.
195      * @param layer the HAL unsubscribes from.
196      */
removeHalSubscription(VmsLayer layer)197     public void removeHalSubscription(VmsLayer layer) {
198         synchronized (mLock) {
199             ++mSequenceNumber;
200             mHalSubscriptions.remove(layer);
201         }
202     }
203 
204     /**
205      * checks if the HAL is subscribed to a layer.
206      * @param layer
207      * @return true if the HAL is subscribed to layer.
208      */
isHalSubscribed(VmsLayer layer)209     public boolean isHalSubscribed(VmsLayer layer) {
210         synchronized (mLock) {
211             return mHalSubscriptions.contains(layer);
212         }
213     }
214 
215     /**
216      * checks if there are subscribers to a layer.
217      * @param layer
218      * @return true if there are subscribers to layer.
219      */
hasLayerSubscriptions(VmsLayer layer)220     public boolean hasLayerSubscriptions(VmsLayer layer) {
221         synchronized (mLock) {
222             return mLayerSubscriptions.containsKey(layer) || mHalSubscriptions.contains(layer);
223         }
224     }
225 
226     /**
227      * @return a Set of layers and versions which VMS clients are subscribed to.
228      */
getSubscriptionState()229     public VmsSubscriptionState getSubscriptionState() {
230         synchronized (mLock) {
231             List<VmsLayer> layers = new ArrayList<>();
232             layers.addAll(mLayerSubscriptions.keySet());
233             layers.addAll(mHalSubscriptions);
234             return new VmsSubscriptionState(mSequenceNumber, layers);
235         }
236     }
237 }