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.content.pm.PackageManager; 26 import android.os.Binder; 27 import android.os.IBinder; 28 import android.os.Process; 29 import android.util.Log; 30 31 import com.android.car.VmsLayersAvailability; 32 import com.android.car.VmsPublishersInfo; 33 import com.android.car.VmsRouting; 34 import com.android.internal.annotations.GuardedBy; 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.Map; 40 import java.util.Set; 41 import java.util.concurrent.CopyOnWriteArrayList; 42 import java.util.function.IntSupplier; 43 44 /** 45 * Broker service facilitating subscription handling and message passing between 46 * VmsPublisherService, VmsSubscriberService, and VmsHalService. 47 */ 48 public class VmsBrokerService { 49 private static final boolean DBG = true; 50 private static final String TAG = "VmsBrokerService"; 51 52 @VisibleForTesting 53 static final String HAL_CLIENT = "HalClient"; 54 55 @VisibleForTesting 56 static final String UNKNOWN_PACKAGE = "UnknownPackage"; 57 58 private CopyOnWriteArrayList<PublisherListener> mPublisherListeners = 59 new CopyOnWriteArrayList<>(); 60 private CopyOnWriteArrayList<SubscriberListener> mSubscriberListeners = 61 new CopyOnWriteArrayList<>(); 62 private PackageManager mPackageManager; 63 private IntSupplier mGetCallingPid; 64 private IntSupplier mGetCallingUid; 65 66 private final Object mLock = new Object(); 67 @GuardedBy("mLock") 68 private final VmsRouting mRouting = new VmsRouting(); 69 @GuardedBy("mLock") 70 private final Map<IBinder, String> mBinderPackage = new HashMap<>(); 71 @GuardedBy("mLock") 72 private final Map<IBinder, Map<Integer, VmsLayersOffering>> mOfferings = new HashMap<>(); 73 @GuardedBy("mLock") 74 private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability(); 75 @GuardedBy("mLock") 76 private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo(); 77 78 /** 79 * The VMS publisher service implements this interface to receive publisher callbacks. 80 */ 81 public interface PublisherListener { 82 /** 83 * Callback triggered when publisher subscription state changes. 84 * 85 * @param subscriptionState Current subscription state. 86 */ onSubscriptionChange(VmsSubscriptionState subscriptionState)87 void onSubscriptionChange(VmsSubscriptionState subscriptionState); 88 } 89 90 /** 91 * The VMS subscriber service implements this interface to receive subscriber callbacks. 92 */ 93 public interface SubscriberListener { 94 /** 95 * Callback triggered when data is published for a given layer. 96 * 97 * @param layer Layer data is being published for 98 * @param publisherId Publisher of data 99 * @param payload Layer data 100 */ onMessageReceived(VmsLayer layer, int publisherId, byte[] payload)101 void onMessageReceived(VmsLayer layer, int publisherId, byte[] payload); 102 103 /** 104 * Callback triggered when the layers available for subscription changes. 105 * 106 * @param availableLayers Current layer availability 107 */ onLayersAvailabilityChange(VmsAvailableLayers availableLayers)108 void onLayersAvailabilityChange(VmsAvailableLayers availableLayers); 109 } 110 111 /** 112 * Constructs new broker service. 113 */ VmsBrokerService(PackageManager packageManager)114 public VmsBrokerService(PackageManager packageManager) { 115 this(packageManager, Binder::getCallingPid, Binder::getCallingUid); 116 } 117 118 @VisibleForTesting VmsBrokerService(PackageManager packageManager, IntSupplier getCallingPid, IntSupplier getCallingUid)119 VmsBrokerService(PackageManager packageManager, IntSupplier getCallingPid, 120 IntSupplier getCallingUid) { 121 if (DBG) Log.d(TAG, "Started VmsBrokerService!"); 122 mPackageManager = packageManager; 123 mGetCallingPid = getCallingPid; 124 mGetCallingUid = getCallingUid; 125 } 126 127 /** 128 * Adds a listener for publisher callbacks. 129 * 130 * @param listener Publisher callback listener 131 */ addPublisherListener(PublisherListener listener)132 public void addPublisherListener(PublisherListener listener) { 133 mPublisherListeners.add(listener); 134 } 135 136 /** 137 * Adds a listener for subscriber callbacks. 138 * 139 * @param listener Subscriber callback listener 140 */ addSubscriberListener(SubscriberListener listener)141 public void addSubscriberListener(SubscriberListener listener) { 142 mSubscriberListeners.add(listener); 143 } 144 145 /** 146 * Removes a listener for publisher callbacks. 147 * 148 * @param listener Publisher callback listener 149 */ removePublisherListener(PublisherListener listener)150 public void removePublisherListener(PublisherListener listener) { 151 mPublisherListeners.remove(listener); 152 } 153 154 /** 155 * Removes a listener for subscriber callbacks. 156 * 157 * @param listener Subscriber callback listener 158 */ removeSubscriberListener(SubscriberListener listener)159 public void removeSubscriberListener(SubscriberListener listener) { 160 mSubscriberListeners.remove(listener); 161 } 162 163 /** 164 * Adds a subscription to all layers. 165 * 166 * @param subscriber Subscriber client to send layer data 167 */ addSubscription(IVmsSubscriberClient subscriber)168 public void addSubscription(IVmsSubscriberClient subscriber) { 169 synchronized (mLock) { 170 mRouting.addSubscription(subscriber); 171 // Add mapping from binder to package name of subscriber. 172 mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage()); 173 } 174 } 175 176 /** 177 * Removes a subscription to all layers. 178 * 179 * @param subscriber Subscriber client to remove subscription for 180 */ removeSubscription(IVmsSubscriberClient subscriber)181 public void removeSubscription(IVmsSubscriberClient subscriber) { 182 synchronized (mLock) { 183 mRouting.removeSubscription(subscriber); 184 } 185 } 186 187 /** 188 * Adds a layer subscription. 189 * 190 * @param subscriber Subscriber client to send layer data 191 * @param layer Layer to send 192 */ addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer)193 public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) { 194 boolean firstSubscriptionForLayer; 195 if (DBG) Log.d(TAG, "Checking for first subscription. Layer: " + layer); 196 synchronized (mLock) { 197 // Check if publishers need to be notified about this change in subscriptions. 198 firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); 199 200 // Add the listeners subscription to the layer 201 mRouting.addSubscription(subscriber, layer); 202 203 // Add mapping from binder to package name of subscriber. 204 mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage()); 205 } 206 if (firstSubscriptionForLayer) { 207 notifyOfSubscriptionChange(); 208 } 209 } 210 211 /** 212 * Removes a layer subscription. 213 * 214 * @param subscriber Subscriber client to remove subscription for 215 * @param layer Layer to remove 216 */ removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer)217 public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) { 218 boolean layerHasSubscribers; 219 synchronized (mLock) { 220 if (!mRouting.hasLayerSubscriptions(layer)) { 221 if (DBG) Log.d(TAG, "Trying to remove a layer with no subscription: " + layer); 222 return; 223 } 224 225 // Remove the listeners subscription to the layer 226 mRouting.removeSubscription(subscriber, layer); 227 228 // Check if publishers need to be notified about this change in subscriptions. 229 layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); 230 } 231 if (!layerHasSubscribers) { 232 notifyOfSubscriptionChange(); 233 } 234 } 235 236 /** 237 * Adds a publisher-specific layer subscription. 238 * 239 * @param subscriber Subscriber client to send layer data 240 * @param layer Layer to send 241 * @param publisherId Publisher of layer 242 */ addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId)243 public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId) { 244 boolean firstSubscriptionForLayer; 245 synchronized (mLock) { 246 // Check if publishers need to be notified about this change in subscriptions. 247 firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer) 248 || mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId)); 249 250 // Add the listeners subscription to the layer 251 mRouting.addSubscription(subscriber, layer, publisherId); 252 253 // Add mapping from binder to package name of subscriber. 254 mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage()); 255 } 256 if (firstSubscriptionForLayer) { 257 notifyOfSubscriptionChange(); 258 } 259 } 260 261 /** 262 * Removes a publisher-specific layer subscription. 263 * 264 * @param subscriber Subscriber client to remove subscription for 265 * @param layer Layer to remove 266 * @param publisherId Publisher of layer 267 */ removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId)268 public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, 269 int publisherId) { 270 boolean layerHasSubscribers; 271 synchronized (mLock) { 272 if (!mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId)) { 273 Log.i(TAG, "Trying to remove a layer with no subscription: " 274 + layer + ", publisher ID:" + publisherId); 275 return; 276 } 277 278 // Remove the listeners subscription to the layer 279 mRouting.removeSubscription(subscriber, layer, publisherId); 280 281 // Check if publishers need to be notified about this change in subscriptions. 282 layerHasSubscribers = mRouting.hasLayerSubscriptions(layer) 283 || mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId); 284 } 285 if (!layerHasSubscribers) { 286 notifyOfSubscriptionChange(); 287 } 288 } 289 290 /** 291 * Removes a disconnected subscriber's subscriptions 292 * 293 * @param subscriber Subscriber that was disconnected 294 */ removeDeadSubscriber(IVmsSubscriberClient subscriber)295 public void removeDeadSubscriber(IVmsSubscriberClient subscriber) { 296 boolean subscriptionStateChanged; 297 synchronized (mLock) { 298 subscriptionStateChanged = mRouting.removeDeadSubscriber(subscriber); 299 300 // Remove mapping from binder to package name of subscriber. 301 mBinderPackage.remove(subscriber.asBinder()); 302 } 303 if (subscriptionStateChanged) { 304 notifyOfSubscriptionChange(); 305 } 306 } 307 308 /** 309 * Gets all subscribers for a specific layer/publisher combination. 310 * 311 * @param layer Layer to query 312 * @param publisherId Publisher of layer 313 */ getSubscribersForLayerFromPublisher(VmsLayer layer, int publisherId)314 public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer, 315 int publisherId) { 316 synchronized (mLock) { 317 return mRouting.getSubscribersForLayerFromPublisher(layer, publisherId); 318 } 319 } 320 321 /** 322 * Gets the state of all layer subscriptions. 323 */ getSubscriptionState()324 public VmsSubscriptionState getSubscriptionState() { 325 synchronized (mLock) { 326 return mRouting.getSubscriptionState(); 327 } 328 } 329 330 /** 331 * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means 332 * that the same publisherInfo will always, within a trip of the vehicle, return the same ID. 333 * The publisherInfo should be static for a binary and should only change as part of a software 334 * update. The publisherInfo is a serialized proto message which VMS clients can interpret. 335 */ getPublisherId(byte[] publisherInfo)336 public int getPublisherId(byte[] publisherInfo) { 337 if (DBG) Log.i(TAG, "Getting publisher static ID"); 338 synchronized (mLock) { 339 return mPublishersInfo.getIdForInfo(publisherInfo); 340 } 341 } 342 343 /** 344 * Gets the publisher information data registered in {@link #getPublisherId(byte[])} 345 * 346 * @param publisherId Publisher ID to query 347 * @return Publisher information 348 */ getPublisherInfo(int publisherId)349 public byte[] getPublisherInfo(int publisherId) { 350 if (DBG) Log.i(TAG, "Getting information for publisher ID: " + publisherId); 351 synchronized (mLock) { 352 return mPublishersInfo.getPublisherInfo(publisherId); 353 } 354 } 355 356 /** 357 * Sets the layers offered by the publisher with the given publisher token. 358 * 359 * @param publisherToken Identifier token of publisher 360 * @param offering Layers offered by publisher 361 */ setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering)362 public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering) { 363 synchronized (mLock) { 364 Map<Integer, VmsLayersOffering> publisherOfferings = mOfferings.computeIfAbsent( 365 publisherToken, k -> new HashMap<>()); 366 publisherOfferings.put(offering.getPublisherId(), offering); 367 updateLayerAvailability(); 368 } 369 VmsOperationRecorder.get().setPublisherLayersOffering(offering); 370 notifyOfAvailabilityChange(); 371 } 372 373 /** 374 * Removes a disconnected publisher's offerings 375 * 376 * @param publisherToken Identifier token of publisher to be removed 377 */ removeDeadPublisher(IBinder publisherToken)378 public void removeDeadPublisher(IBinder publisherToken) { 379 synchronized (mLock) { 380 mOfferings.remove(publisherToken); 381 updateLayerAvailability(); 382 } 383 notifyOfAvailabilityChange(); 384 } 385 386 /** 387 * Gets all layers available for subscription. 388 * 389 * @return All available layers 390 */ getAvailableLayers()391 public VmsAvailableLayers getAvailableLayers() { 392 synchronized (mLock) { 393 return mAvailableLayers.getAvailableLayers(); 394 } 395 } 396 397 /** 398 * Gets the package name for a given IVmsSubscriberClient 399 */ getPackageName(IVmsSubscriberClient subscriber)400 public String getPackageName(IVmsSubscriberClient subscriber) { 401 synchronized (mLock) { 402 return mBinderPackage.get(subscriber.asBinder()); 403 } 404 } 405 updateLayerAvailability()406 private void updateLayerAvailability() { 407 Set<VmsLayersOffering> allPublisherOfferings = new HashSet<>(); 408 synchronized (mLock) { 409 for (Map<Integer, VmsLayersOffering> offerings : mOfferings.values()) { 410 allPublisherOfferings.addAll(offerings.values()); 411 } 412 if (DBG) Log.d(TAG, "New layer availability: " + allPublisherOfferings); 413 mAvailableLayers.setPublishersOffering(allPublisherOfferings); 414 } 415 } 416 notifyOfSubscriptionChange()417 private void notifyOfSubscriptionChange() { 418 if (DBG) Log.d(TAG, "Notifying publishers on subscriptions"); 419 420 VmsSubscriptionState subscriptionState = getSubscriptionState(); 421 // Notify the App publishers 422 for (PublisherListener listener : mPublisherListeners) { 423 listener.onSubscriptionChange(subscriptionState); 424 } 425 } 426 notifyOfAvailabilityChange()427 private void notifyOfAvailabilityChange() { 428 if (DBG) Log.d(TAG, "Notifying subscribers on layers availability"); 429 430 VmsAvailableLayers availableLayers = getAvailableLayers(); 431 // Notify the App subscribers 432 for (SubscriberListener listener : mSubscriberListeners) { 433 listener.onLayersAvailabilityChange(availableLayers); 434 } 435 } 436 437 // If we're in a binder call, returns back the package name of the caller of the binder call. getCallingPackage()438 private String getCallingPackage() { 439 int callingPid = mGetCallingPid.getAsInt(); 440 // Since the HAL lives in the same process, if the callingPid is equal to this process's 441 // PID, we know it's the HAL client. 442 if (callingPid == Process.myPid()) { 443 return HAL_CLIENT; 444 } 445 int callingUid = mGetCallingUid.getAsInt(); 446 String packageName = mPackageManager.getNameForUid(callingUid); 447 if (packageName == null) { 448 return UNKNOWN_PACKAGE; 449 } else { 450 return packageName; 451 } 452 } 453 } 454