1 /* 2 * Copyright (C) 2020 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 static com.android.car.ICarImpl.assertAnyVmsPermission; 20 import static com.android.car.ICarImpl.assertVmsPublisherPermission; 21 import static com.android.car.ICarImpl.assertVmsSubscriberPermission; 22 23 import android.car.vms.IVmsBrokerService; 24 import android.car.vms.IVmsClientCallback; 25 import android.car.vms.VmsAssociatedLayer; 26 import android.car.vms.VmsAvailableLayers; 27 import android.car.vms.VmsLayer; 28 import android.car.vms.VmsLayerDependency; 29 import android.car.vms.VmsLayersOffering; 30 import android.car.vms.VmsProviderInfo; 31 import android.car.vms.VmsRegistrationInfo; 32 import android.car.vms.VmsSubscriptionState; 33 import android.content.Context; 34 import android.content.pm.PackageManager; 35 import android.os.Binder; 36 import android.os.IBinder; 37 import android.os.RemoteException; 38 import android.os.SharedMemory; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.IndentingPrintWriter; 42 import android.util.Slog; 43 44 import com.android.car.CarLog; 45 import com.android.car.CarServiceBase; 46 import com.android.car.stats.CarStatsService; 47 import com.android.car.stats.VmsClientLogger; 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.util.FunctionalUtils.ThrowingConsumer; 51 52 import java.util.ArrayList; 53 import java.util.Collection; 54 import java.util.Collections; 55 import java.util.Comparator; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Set; 59 import java.util.function.IntSupplier; 60 import java.util.stream.Collectors; 61 62 /** 63 * Message broker service for routing Vehicle Map Service messages between clients. 64 * 65 * This service is also responsible for tracking VMS client connections and broadcasting 66 * notifications to clients about layer offering or subscription state changes. 67 */ 68 public class VmsBrokerService extends IVmsBrokerService.Stub implements CarServiceBase { 69 private static final boolean DBG = false; 70 private static final String TAG = CarLog.tagFor(VmsBrokerService.class); 71 72 private final Context mContext; 73 private final PackageManager mPackageManager; 74 private final CarStatsService mStatsService; 75 private final IntSupplier mGetCallingUid; 76 77 private final VmsProviderInfoStore mProviderInfoStore = new VmsProviderInfoStore(); 78 private final VmsLayerAvailability mAvailableLayers = new VmsLayerAvailability(); 79 80 private final Object mLock = new Object(); 81 @GuardedBy("mLock") 82 private final Map<IBinder /* clientToken */, VmsClientInfo> mClientMap = new ArrayMap<>(); 83 @GuardedBy("mLock") 84 private Set<VmsLayersOffering> mAllOfferings = Collections.emptySet(); 85 @GuardedBy("mLock") 86 private VmsSubscriptionState mSubscriptionState = new VmsSubscriptionState(0, 87 Collections.emptySet(), Collections.emptySet()); 88 VmsBrokerService(Context context, CarStatsService statsService)89 public VmsBrokerService(Context context, CarStatsService statsService) { 90 this(context, statsService, Binder::getCallingUid); 91 } 92 93 @VisibleForTesting VmsBrokerService( Context context, CarStatsService statsService, IntSupplier getCallingUid)94 VmsBrokerService( 95 Context context, 96 CarStatsService statsService, 97 IntSupplier getCallingUid) { 98 mContext = context; 99 mPackageManager = context.getPackageManager(); 100 mStatsService = statsService; 101 mGetCallingUid = getCallingUid; 102 } 103 104 @Override init()105 public void init() { 106 } 107 108 @Override release()109 public void release() { 110 } 111 112 @Override dump(IndentingPrintWriter writer)113 public void dump(IndentingPrintWriter writer) { 114 writer.println("*" + TAG + "*"); 115 synchronized (mLock) { 116 writer.println("mAvailableLayers: " + mAvailableLayers.getAvailableLayers()); 117 writer.println(); 118 writer.println("mSubscriptionState: " + mSubscriptionState); 119 writer.println(); 120 writer.println("mClientMap:"); 121 mClientMap.values().stream() 122 .sorted(Comparator.comparingInt(VmsClientInfo::getUid)) 123 .forEach(client -> client.dump(writer, " ")); 124 } 125 } 126 127 @Override registerClient(IBinder clientToken, IVmsClientCallback callback, boolean legacyClient)128 public VmsRegistrationInfo registerClient(IBinder clientToken, IVmsClientCallback callback, 129 boolean legacyClient) { 130 assertAnyVmsPermission(mContext); 131 int clientUid = mGetCallingUid.getAsInt(); 132 String clientPackage = mPackageManager.getNameForUid(clientUid); 133 if (DBG) Slog.d(TAG, "registerClient uid: " + clientUid + " package: " + clientPackage); 134 135 mStatsService.getVmsClientLogger(clientUid) 136 .logConnectionState(VmsClientLogger.ConnectionState.CONNECTED); 137 138 IBinder.DeathRecipient deathRecipient; 139 try { 140 deathRecipient = () -> unregisterClient(clientToken, 141 VmsClientLogger.ConnectionState.DISCONNECTED); 142 callback.asBinder().linkToDeath(deathRecipient, 0); 143 } catch (RemoteException e) { 144 mStatsService.getVmsClientLogger(clientUid) 145 .logConnectionState(VmsClientLogger.ConnectionState.DISCONNECTED); 146 throw new IllegalStateException("Client callback is already dead"); 147 } 148 149 synchronized (mLock) { 150 mClientMap.put(clientToken, new VmsClientInfo(clientUid, clientPackage, callback, 151 legacyClient, deathRecipient)); 152 return new VmsRegistrationInfo( 153 mAvailableLayers.getAvailableLayers(), 154 mSubscriptionState); 155 } 156 } 157 158 @Override unregisterClient(IBinder clientToken)159 public void unregisterClient(IBinder clientToken) { 160 assertAnyVmsPermission(mContext); 161 unregisterClient(clientToken, VmsClientLogger.ConnectionState.TERMINATED); 162 } 163 164 @Override getProviderInfo(IBinder clientToken, int providerId)165 public VmsProviderInfo getProviderInfo(IBinder clientToken, int providerId) { 166 assertAnyVmsPermission(mContext); 167 getClient(clientToken); // Assert that the client is registered 168 return new VmsProviderInfo(mProviderInfoStore.getProviderInfo(providerId)); 169 } 170 171 @Override setSubscriptions(IBinder clientToken, List<VmsAssociatedLayer> layers)172 public void setSubscriptions(IBinder clientToken, List<VmsAssociatedLayer> layers) { 173 assertVmsSubscriberPermission(mContext); 174 getClient(clientToken).setSubscriptions(layers); 175 updateSubscriptionState(); 176 } 177 178 @Override setMonitoringEnabled(IBinder clientToken, boolean enabled)179 public void setMonitoringEnabled(IBinder clientToken, boolean enabled) { 180 assertVmsSubscriberPermission(mContext); 181 getClient(clientToken).setMonitoringEnabled(enabled); 182 } 183 184 @Override registerProvider(IBinder clientToken, VmsProviderInfo providerInfo)185 public int registerProvider(IBinder clientToken, VmsProviderInfo providerInfo) { 186 assertVmsPublisherPermission(mContext); 187 VmsClientInfo client = getClient(clientToken); 188 int providerId; 189 synchronized (mLock) { 190 providerId = mProviderInfoStore.getProviderId(providerInfo.getDescription()); 191 } 192 client.addProviderId(providerId); 193 return providerId; 194 } 195 196 @Override setProviderOfferings(IBinder clientToken, int providerId, List<VmsLayerDependency> offerings)197 public void setProviderOfferings(IBinder clientToken, int providerId, 198 List<VmsLayerDependency> offerings) { 199 assertVmsPublisherPermission(mContext); 200 VmsClientInfo client = getClient(clientToken); 201 if (!client.hasProviderId(providerId) && !client.isLegacyClient()) { 202 throw new IllegalArgumentException("Client not registered to offer layers as " 203 + providerId); 204 } 205 if (client.setProviderOfferings(providerId, offerings)) { 206 updateAvailableLayers(); 207 } 208 } 209 210 @Override publishPacket(IBinder clientToken, int providerId, VmsLayer layer, byte[] packet)211 public void publishPacket(IBinder clientToken, int providerId, VmsLayer layer, byte[] packet) { 212 assertVmsPublisherPermission(mContext); 213 deliverToSubscribers(clientToken, providerId, layer, packet.length, 214 callback -> callback.onPacketReceived(providerId, layer, packet)); 215 } 216 217 @Override publishLargePacket(IBinder clientToken, int providerId, VmsLayer layer, SharedMemory packet)218 public void publishLargePacket(IBinder clientToken, int providerId, VmsLayer layer, 219 SharedMemory packet) { 220 try (SharedMemory largePacket = packet) { 221 assertVmsPublisherPermission(mContext); 222 deliverToSubscribers(clientToken, providerId, layer, packet.getSize(), 223 callback -> callback.onLargePacketReceived(providerId, layer, largePacket)); 224 } 225 } 226 deliverToSubscribers(IBinder clientToken, int providerId, VmsLayer layer, int packetLength, ThrowingConsumer<IVmsClientCallback> callbackConsumer)227 private void deliverToSubscribers(IBinder clientToken, int providerId, VmsLayer layer, 228 int packetLength, ThrowingConsumer<IVmsClientCallback> callbackConsumer) { 229 VmsClientInfo client = getClient(clientToken); 230 if (!client.hasOffering(providerId, layer) && !client.isLegacyClient()) { 231 throw new IllegalArgumentException("Client does not offer " + layer + " as " 232 + providerId); 233 } 234 235 mStatsService.getVmsClientLogger(client.getUid()) 236 .logPacketSent(layer, packetLength); 237 238 Collection<VmsClientInfo> subscribers; 239 synchronized (mLock) { 240 subscribers = mClientMap.values().stream() 241 .filter(subscriber -> subscriber.isSubscribed(providerId, layer)) 242 .collect(Collectors.toList()); 243 } 244 245 if (DBG) Slog.d(TAG, String.format("Number of subscribers: %d", subscribers.size())); 246 247 if (subscribers.isEmpty()) { 248 // A negative UID signals that the packet had zero subscribers 249 mStatsService.getVmsClientLogger(-1).logPacketDropped(layer, packetLength); 250 return; 251 } 252 253 for (VmsClientInfo subscriber : subscribers) { 254 try { 255 callbackConsumer.accept(subscriber.getCallback()); 256 mStatsService.getVmsClientLogger(subscriber.getUid()) 257 .logPacketReceived(layer, packetLength); 258 } catch (RuntimeException e) { 259 mStatsService.getVmsClientLogger(subscriber.getUid()) 260 .logPacketDropped(layer, packetLength); 261 Slog.e(TAG, String.format("Unable to publish to listener: %s", 262 subscriber.getPackageName()), e); 263 } 264 } 265 } 266 unregisterClient(IBinder clientToken, int connectionState)267 private void unregisterClient(IBinder clientToken, int connectionState) { 268 VmsClientInfo client; 269 synchronized (mLock) { 270 client = mClientMap.remove(clientToken); 271 } 272 if (client != null) { 273 client.getCallback().asBinder().unlinkToDeath(client.getDeathRecipient(), 0); 274 mStatsService.getVmsClientLogger(client.getUid()) 275 .logConnectionState(connectionState); 276 updateAvailableLayers(); 277 updateSubscriptionState(); 278 } 279 } 280 getClient(IBinder clientToken)281 private VmsClientInfo getClient(IBinder clientToken) { 282 synchronized (mLock) { 283 VmsClientInfo client = mClientMap.get(clientToken); 284 if (client == null) { 285 throw new IllegalStateException("Unknown client token"); 286 } 287 return client; 288 } 289 } 290 getActiveClients()291 private Collection<VmsClientInfo> getActiveClients() { 292 synchronized (mLock) { 293 return new ArrayList<>(mClientMap.values()); 294 } 295 } 296 updateAvailableLayers()297 private void updateAvailableLayers() { 298 synchronized (mLock) { 299 // Fuse layer offerings 300 Set<VmsLayersOffering> allOfferings = mClientMap.values().stream() 301 .map(VmsClientInfo::getAllOfferings) 302 .flatMap(Collection::stream) 303 .collect(Collectors.toCollection(ArraySet::new)); 304 305 // Ignore update if offerings are unchanged 306 if (mAllOfferings.equals(allOfferings)) { 307 return; 308 } 309 310 // Update offerings and compute available layers 311 mAllOfferings = allOfferings; 312 mAvailableLayers.setPublishersOffering(allOfferings); 313 } 314 notifyOfAvailabilityChange(mAvailableLayers.getAvailableLayers()); 315 } 316 notifyOfAvailabilityChange(VmsAvailableLayers availableLayers)317 private void notifyOfAvailabilityChange(VmsAvailableLayers availableLayers) { 318 Slog.i(TAG, "Notifying clients of layer availability change: " + availableLayers); 319 for (VmsClientInfo client : getActiveClients()) { 320 try { 321 client.getCallback().onLayerAvailabilityChanged(availableLayers); 322 } catch (RemoteException e) { 323 Slog.w(TAG, "onLayersAvailabilityChanged failed: " + client.getPackageName(), 324 e); 325 } 326 } 327 } 328 updateSubscriptionState()329 private void updateSubscriptionState() { 330 VmsSubscriptionState subscriptionState; 331 synchronized (mLock) { 332 Set<VmsLayer> layerSubscriptions = new ArraySet<>(); 333 Map<VmsLayer, Set<Integer>> layerAndProviderSubscriptions = new ArrayMap<>(); 334 // Fuse subscriptions 335 for (VmsClientInfo client : mClientMap.values()) { 336 layerSubscriptions.addAll(client.getLayerSubscriptions()); 337 client.getLayerAndProviderSubscriptions().forEach((layer, providerIds) -> { 338 Set<Integer> providerSubscriptions = 339 layerAndProviderSubscriptions.computeIfAbsent( 340 layer, 341 ignored -> new ArraySet<>()); 342 providerSubscriptions.addAll(providerIds); 343 }); 344 } 345 346 // Remove global layer subscriptions from provider-specific subscription state 347 layerSubscriptions.forEach(layerAndProviderSubscriptions::remove); 348 349 // Transform provider-specific subscriptions into VmsAssociatedLayers 350 Set<VmsAssociatedLayer> associatedLayers = 351 layerAndProviderSubscriptions.entrySet().stream() 352 .map(entry -> new VmsAssociatedLayer(entry.getKey(), entry.getValue())) 353 .collect(Collectors.toCollection(ArraySet::new)); 354 355 // Ignore update if subscriptions are unchanged 356 if (mSubscriptionState.getLayers().equals(layerSubscriptions) 357 && mSubscriptionState.getAssociatedLayers().equals(associatedLayers)) { 358 return; 359 } 360 361 // Update subscription state 362 subscriptionState = new VmsSubscriptionState( 363 mSubscriptionState.getSequenceNumber() + 1, 364 layerSubscriptions, 365 associatedLayers); 366 mSubscriptionState = subscriptionState; 367 } 368 // Notify clients of update 369 notifyOfSubscriptionChange(subscriptionState); 370 } 371 notifyOfSubscriptionChange(VmsSubscriptionState subscriptionState)372 private void notifyOfSubscriptionChange(VmsSubscriptionState subscriptionState) { 373 Slog.i(TAG, "Notifying clients of subscription state change: " + subscriptionState); 374 for (VmsClientInfo client : getActiveClients()) { 375 try { 376 client.getCallback().onSubscriptionStateChanged(subscriptionState); 377 } catch (RemoteException e) { 378 Slog.w(TAG, "onSubscriptionStateChanged failed: " + client.getPackageName(), 379 e); 380 } 381 } 382 } 383 } 384