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