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