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