1 /*
2  * Copyright (C) 2019 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.vms;
18 
19 import android.car.vms.IVmsSubscriberClient;
20 import android.car.vms.VmsAvailableLayers;
21 import android.car.vms.VmsLayer;
22 import android.car.vms.VmsLayersOffering;
23 import android.car.vms.VmsOperationRecorder;
24 import android.car.vms.VmsSubscriptionState;
25 import android.os.IBinder;
26 import android.util.Log;
27 
28 import com.android.car.VmsLayersAvailability;
29 import com.android.car.VmsPublishersInfo;
30 import com.android.car.VmsRouting;
31 import com.android.internal.annotations.GuardedBy;
32 
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.concurrent.CopyOnWriteArrayList;
38 
39 /**
40  * Broker service facilitating subscription handling and message passing between
41  * VmsPublisherService, VmsSubscriberService, and VmsHalService.
42  */
43 public class VmsBrokerService {
44     private static final boolean DBG = false;
45     private static final String TAG = "VmsBrokerService";
46 
47     private CopyOnWriteArrayList<PublisherListener> mPublisherListeners =
48             new CopyOnWriteArrayList<>();
49     private CopyOnWriteArrayList<SubscriberListener> mSubscriberListeners =
50             new CopyOnWriteArrayList<>();
51 
52     private final Object mLock = new Object();
53     @GuardedBy("mLock")
54     private final VmsRouting mRouting = new VmsRouting();
55     @GuardedBy("mLock")
56     private final Map<IBinder, Map<Integer, VmsLayersOffering>> mOfferings = new HashMap<>();
57     @GuardedBy("mLock")
58     private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
59     @GuardedBy("mLock")
60     private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
61 
62     /**
63      * The VMS publisher service implements this interface to receive publisher callbacks.
64      */
65     public interface PublisherListener {
66         /**
67          * Callback triggered when publisher subscription state changes.
68          *
69          * @param subscriptionState Current subscription state.
70          */
onSubscriptionChange(VmsSubscriptionState subscriptionState)71         void onSubscriptionChange(VmsSubscriptionState subscriptionState);
72     }
73 
74     /**
75      * The VMS subscriber service implements this interface to receive subscriber callbacks.
76      */
77     public interface SubscriberListener {
78         /**
79          * Callback triggered when the layers available for subscription changes.
80          *
81          * @param availableLayers Current layer availability
82          */
onLayersAvailabilityChange(VmsAvailableLayers availableLayers)83         void onLayersAvailabilityChange(VmsAvailableLayers availableLayers);
84     }
85 
86     /**
87      * Adds a listener for publisher callbacks.
88      *
89      * @param listener Publisher callback listener
90      */
addPublisherListener(PublisherListener listener)91     public void addPublisherListener(PublisherListener listener) {
92         mPublisherListeners.add(listener);
93     }
94 
95     /**
96      * Adds a listener for subscriber callbacks.
97      *
98      * @param listener Subscriber callback listener
99      */
addSubscriberListener(SubscriberListener listener)100     public void addSubscriberListener(SubscriberListener listener) {
101         mSubscriberListeners.add(listener);
102     }
103 
104     /**
105      * Removes a listener for publisher callbacks.
106      *
107      * @param listener Publisher callback listener
108      */
removePublisherListener(PublisherListener listener)109     public void removePublisherListener(PublisherListener listener) {
110         mPublisherListeners.remove(listener);
111     }
112 
113     /**
114      * Removes a listener for subscriber callbacks.
115      *
116      * @param listener Subscriber callback listener
117      */
removeSubscriberListener(SubscriberListener listener)118     public void removeSubscriberListener(SubscriberListener listener) {
119         mSubscriberListeners.remove(listener);
120     }
121 
122     /**
123      * Adds a subscription to all layers.
124      *
125      * @param subscriber Subscriber client to send layer data
126      */
addSubscription(IVmsSubscriberClient subscriber)127     public void addSubscription(IVmsSubscriberClient subscriber) {
128         synchronized (mLock) {
129             mRouting.addSubscription(subscriber);
130         }
131     }
132 
133     /**
134      * Removes a subscription to all layers.
135      *
136      * @param subscriber Subscriber client to remove subscription for
137      */
removeSubscription(IVmsSubscriberClient subscriber)138     public void removeSubscription(IVmsSubscriberClient subscriber) {
139         synchronized (mLock) {
140             mRouting.removeSubscription(subscriber);
141         }
142     }
143 
144     /**
145      * Adds a layer subscription.
146      *
147      * @param subscriber Subscriber client to send layer data
148      * @param layer      Layer to send
149      */
addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer)150     public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
151         boolean firstSubscriptionForLayer;
152         if (DBG) Log.d(TAG, "Checking for first subscription. Layer: " + layer);
153         synchronized (mLock) {
154             // Check if publishers need to be notified about this change in subscriptions.
155             firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
156 
157             // Add the listeners subscription to the layer
158             mRouting.addSubscription(subscriber, layer);
159         }
160         if (firstSubscriptionForLayer) {
161             notifyOfSubscriptionChange();
162         }
163     }
164 
165     /**
166      * Removes a layer subscription.
167      *
168      * @param subscriber Subscriber client to remove subscription for
169      * @param layer      Layer to remove
170      */
removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer)171     public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
172         boolean layerHasSubscribers;
173         synchronized (mLock) {
174             if (!mRouting.hasLayerSubscriptions(layer)) {
175                 if (DBG) Log.d(TAG, "Trying to remove a layer with no subscription: " + layer);
176                 return;
177             }
178 
179             // Remove the listeners subscription to the layer
180             mRouting.removeSubscription(subscriber, layer);
181 
182             // Check if publishers need to be notified about this change in subscriptions.
183             layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
184         }
185         if (!layerHasSubscribers) {
186             notifyOfSubscriptionChange();
187         }
188     }
189 
190     /**
191      * Adds a publisher-specific layer subscription.
192      *
193      * @param subscriber  Subscriber client to send layer data
194      * @param layer       Layer to send
195      * @param publisherId Publisher of layer
196      */
addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId)197     public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId) {
198         boolean firstSubscriptionForLayer;
199         synchronized (mLock) {
200             // Check if publishers need to be notified about this change in subscriptions.
201             firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer)
202                     || mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId));
203 
204             // Add the listeners subscription to the layer
205             mRouting.addSubscription(subscriber, layer, publisherId);
206         }
207         if (firstSubscriptionForLayer) {
208             notifyOfSubscriptionChange();
209         }
210     }
211 
212     /**
213      * Removes a publisher-specific layer subscription.
214      *
215      * @param subscriber  Subscriber client to remove subscription for
216      * @param layer       Layer to remove
217      * @param publisherId Publisher of layer
218      */
removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId)219     public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer,
220             int publisherId) {
221         boolean layerHasSubscribers;
222         synchronized (mLock) {
223             if (!mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId)) {
224                 if (DBG) {
225                     Log.d(TAG, "Trying to remove a layer with no subscription: "
226                         + layer + ", publisher ID:" + publisherId);
227                 }
228                 return;
229             }
230 
231             // Remove the listeners subscription to the layer
232             mRouting.removeSubscription(subscriber, layer, publisherId);
233 
234             // Check if publishers need to be notified about this change in subscriptions.
235             layerHasSubscribers = mRouting.hasLayerSubscriptions(layer)
236                     || mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId);
237         }
238         if (!layerHasSubscribers) {
239             notifyOfSubscriptionChange();
240         }
241     }
242 
243     /**
244      * Removes a disconnected subscriber's subscriptions
245      *
246      * @param subscriber Subscriber that was disconnected
247      */
removeDeadSubscriber(IVmsSubscriberClient subscriber)248     public void removeDeadSubscriber(IVmsSubscriberClient subscriber) {
249         boolean subscriptionStateChanged;
250         synchronized (mLock) {
251             subscriptionStateChanged = mRouting.removeDeadSubscriber(subscriber);
252         }
253         if (subscriptionStateChanged) {
254             notifyOfSubscriptionChange();
255         }
256     }
257 
258     /**
259      * Gets all subscribers for a specific layer/publisher combination.
260      *
261      * @param layer       Layer to query
262      * @param publisherId Publisher of layer
263      */
getSubscribersForLayerFromPublisher(VmsLayer layer, int publisherId)264     public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer,
265             int publisherId) {
266         synchronized (mLock) {
267             return mRouting.getSubscribersForLayerFromPublisher(layer, publisherId);
268         }
269     }
270 
271     /**
272      * Gets the state of all layer subscriptions.
273      */
getSubscriptionState()274     public VmsSubscriptionState getSubscriptionState() {
275         synchronized (mLock) {
276             return mRouting.getSubscriptionState();
277         }
278     }
279 
280     /**
281      * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means
282      * that the same publisherInfo will always, within a trip of the vehicle, return the same ID.
283      * The publisherInfo should be static for a binary and should only change as part of a software
284      * update. The publisherInfo is a serialized proto message which VMS clients can interpret.
285      */
getPublisherId(byte[] publisherInfo)286     public int getPublisherId(byte[] publisherInfo) {
287         if (DBG) Log.i(TAG, "Getting publisher static ID");
288         synchronized (mLock) {
289             return mPublishersInfo.getIdForInfo(publisherInfo);
290         }
291     }
292 
293     /**
294      * Gets the publisher information data registered in {@link #getPublisherId(byte[])}
295      *
296      * @param publisherId Publisher ID to query
297      * @return Publisher information
298      */
getPublisherInfo(int publisherId)299     public byte[] getPublisherInfo(int publisherId) {
300         if (DBG) Log.i(TAG, "Getting information for publisher ID: " + publisherId);
301         synchronized (mLock) {
302             return mPublishersInfo.getPublisherInfo(publisherId);
303         }
304     }
305 
306     /**
307      * Sets the layers offered by the publisher with the given publisher token.
308      *
309      * @param publisherToken Identifier token of publisher
310      * @param offering       Layers offered by publisher
311      */
setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering)312     public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering) {
313         synchronized (mLock) {
314             Map<Integer, VmsLayersOffering> publisherOfferings = mOfferings.computeIfAbsent(
315                     publisherToken, k -> new HashMap<>());
316             publisherOfferings.put(offering.getPublisherId(), offering);
317             updateLayerAvailability();
318         }
319         VmsOperationRecorder.get().setPublisherLayersOffering(offering);
320         notifyOfAvailabilityChange();
321     }
322 
323     /**
324      * Removes a disconnected publisher's offerings
325      *
326      * @param publisherToken Identifier token of publisher to be removed
327      */
removeDeadPublisher(IBinder publisherToken)328     public void removeDeadPublisher(IBinder publisherToken) {
329         synchronized (mLock) {
330             mOfferings.remove(publisherToken);
331             updateLayerAvailability();
332         }
333         notifyOfAvailabilityChange();
334     }
335 
336     /**
337      * Gets all layers available for subscription.
338      *
339      * @return All available layers
340      */
getAvailableLayers()341     public VmsAvailableLayers getAvailableLayers() {
342         synchronized (mLock) {
343             return mAvailableLayers.getAvailableLayers();
344         }
345     }
346 
updateLayerAvailability()347     private void updateLayerAvailability() {
348         Set<VmsLayersOffering> allPublisherOfferings = new HashSet<>();
349         synchronized (mLock) {
350             for (Map<Integer, VmsLayersOffering> offerings : mOfferings.values()) {
351                 allPublisherOfferings.addAll(offerings.values());
352             }
353             if (DBG) Log.d(TAG, "New layer availability: " + allPublisherOfferings);
354             mAvailableLayers.setPublishersOffering(allPublisherOfferings);
355         }
356     }
357 
notifyOfSubscriptionChange()358     private void notifyOfSubscriptionChange() {
359         VmsSubscriptionState subscriptionState = getSubscriptionState();
360         Log.i(TAG, "Notifying publishers of subscriptions: " + subscriptionState);
361         // Notify the App publishers
362         for (PublisherListener listener : mPublisherListeners) {
363             listener.onSubscriptionChange(subscriptionState);
364         }
365     }
366 
notifyOfAvailabilityChange()367     private void notifyOfAvailabilityChange() {
368         VmsAvailableLayers availableLayers = getAvailableLayers();
369         Log.i(TAG, "Notifying subscribers of layers availability: " + availableLayers);
370         // Notify the App subscribers
371         for (SubscriberListener listener : mSubscriberListeners) {
372             listener.onLayersAvailabilityChange(availableLayers);
373         }
374     }
375 }
376