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