• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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