blob: b2dbaa1f2fd4e2f4910932c49c450b35111c0bce [file] [log] [blame]
Mark Tabry6fa123d2020-01-10 19:52:59 -08001/*
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
17package com.android.car.vms;
18
19import static com.android.car.ICarImpl.assertAnyVmsPermission;
20import static com.android.car.ICarImpl.assertVmsPublisherPermission;
21import static com.android.car.ICarImpl.assertVmsSubscriberPermission;
22
23import android.car.vms.IVmsBrokerService;
24import android.car.vms.IVmsClientCallback;
25import android.car.vms.VmsAssociatedLayer;
26import android.car.vms.VmsAvailableLayers;
27import android.car.vms.VmsLayer;
28import android.car.vms.VmsLayerDependency;
29import android.car.vms.VmsLayersOffering;
30import android.car.vms.VmsProviderInfo;
31import android.car.vms.VmsRegistrationInfo;
32import android.car.vms.VmsSubscriptionState;
33import android.content.Context;
34import android.content.pm.PackageManager;
35import android.os.Binder;
36import android.os.IBinder;
37import android.os.RemoteException;
Mark Tabry987c8852020-03-12 10:44:28 -070038import android.os.SharedMemory;
Mark Tabry6fa123d2020-01-10 19:52:59 -080039import android.util.ArrayMap;
40import android.util.ArraySet;
41import android.util.Log;
42
43import com.android.car.CarServiceBase;
44import com.android.car.VmsLayersAvailability;
45import com.android.car.VmsPublishersInfo;
46import com.android.car.stats.CarStatsService;
47import com.android.car.stats.VmsClientLogger;
48import com.android.internal.annotations.GuardedBy;
49import com.android.internal.annotations.VisibleForTesting;
Mark Tabry987c8852020-03-12 10:44:28 -070050import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
Mark Tabry6fa123d2020-01-10 19:52:59 -080051
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.Collection;
55import java.util.Collections;
Mark Tabry1a66c6c2020-05-14 10:29:03 -070056import java.util.Comparator;
Mark Tabry6fa123d2020-01-10 19:52:59 -080057import java.util.List;
58import java.util.Map;
59import java.util.Set;
60import java.util.function.IntSupplier;
61import java.util.stream.Collectors;
62
63/**
64 * Message broker service for routing Vehicle Map Service messages between clients.
65 *
66 * This service is also responsible for tracking VMS client connections and broadcasting
67 * notifications to clients about layer offering or subscription state changes.
68 */
Mark Tabry58246c32020-03-20 09:38:36 -070069public class VmsBrokerService extends IVmsBrokerService.Stub implements CarServiceBase {
Mark Tabry6fa123d2020-01-10 19:52:59 -080070 private static final boolean DBG = false;
Mark Tabry58246c32020-03-20 09:38:36 -070071 private static final String TAG = VmsBrokerService.class.getSimpleName();
Mark Tabry6fa123d2020-01-10 19:52:59 -080072
73 private final Context mContext;
74 private final PackageManager mPackageManager;
75 private final CarStatsService mStatsService;
76 private final IntSupplier mGetCallingUid;
77
78 private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
79 private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
80
81 private final Object mLock = new Object();
82 @GuardedBy("mLock")
83 private final Map<IBinder /* clientToken */, VmsClientInfo> mClientMap = new ArrayMap<>();
84 @GuardedBy("mLock")
85 private Set<VmsLayersOffering> mAllOfferings = Collections.emptySet();
86 @GuardedBy("mLock")
87 private VmsSubscriptionState mSubscriptionState = new VmsSubscriptionState(0,
88 Collections.emptySet(), Collections.emptySet());
89
Mark Tabry58246c32020-03-20 09:38:36 -070090 public VmsBrokerService(Context context, CarStatsService statsService) {
Mark Tabry6fa123d2020-01-10 19:52:59 -080091 this(context, statsService, Binder::getCallingUid);
92 }
93
94 @VisibleForTesting
Mark Tabry58246c32020-03-20 09:38:36 -070095 VmsBrokerService(
Mark Tabry6fa123d2020-01-10 19:52:59 -080096 Context context,
97 CarStatsService statsService,
98 IntSupplier getCallingUid) {
99 mContext = context;
100 mPackageManager = context.getPackageManager();
101 mStatsService = statsService;
102 mGetCallingUid = getCallingUid;
103 }
104
105 @Override
106 public void init() {
107 }
108
109 @Override
110 public void release() {
111 }
112
113 @Override
114 public void dump(PrintWriter writer) {
Mark Tabry1a66c6c2020-05-14 10:29:03 -0700115 writer.println("*" + TAG + "*");
116 synchronized (mLock) {
117 writer.println("mAvailableLayers: " + mAvailableLayers.getAvailableLayers());
118 writer.println();
119 writer.println("mSubscriptionState: " + mSubscriptionState);
120 writer.println();
121 writer.println("mClientMap:");
122 mClientMap.values().stream()
123 .sorted(Comparator.comparingInt(VmsClientInfo::getUid))
124 .forEach(client -> client.dump(writer, " "));
125 }
Mark Tabry6fa123d2020-01-10 19:52:59 -0800126 }
127
128 @Override
Mark Tabry62e749c2020-01-10 19:52:59 -0800129 public VmsRegistrationInfo registerClient(IBinder clientToken, IVmsClientCallback callback,
130 boolean legacyClient) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800131 assertAnyVmsPermission(mContext);
132 int clientUid = mGetCallingUid.getAsInt();
133 String clientPackage = mPackageManager.getNameForUid(clientUid);
134 if (DBG) Log.d(TAG, "registerClient uid: " + clientUid + " package: " + clientPackage);
135
136 mStatsService.getVmsClientLogger(clientUid)
137 .logConnectionState(VmsClientLogger.ConnectionState.CONNECTED);
138
Mark Tabrya63623c2020-04-30 15:41:26 -0700139 IBinder.DeathRecipient deathRecipient;
140 try {
141 deathRecipient = () -> unregisterClient(clientToken,
142 VmsClientLogger.ConnectionState.DISCONNECTED);
143 callback.asBinder().linkToDeath(deathRecipient, 0);
144 } catch (RemoteException e) {
145 mStatsService.getVmsClientLogger(clientUid)
146 .logConnectionState(VmsClientLogger.ConnectionState.DISCONNECTED);
147 throw new IllegalStateException("Client callback is already dead");
148 }
149
Mark Tabry6fa123d2020-01-10 19:52:59 -0800150 synchronized (mLock) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700151 mClientMap.put(clientToken, new VmsClientInfo(clientUid, clientPackage, callback,
152 legacyClient, deathRecipient));
Mark Tabry6fa123d2020-01-10 19:52:59 -0800153 return new VmsRegistrationInfo(
154 mAvailableLayers.getAvailableLayers(),
155 mSubscriptionState);
156 }
157 }
158
159 @Override
160 public void unregisterClient(IBinder clientToken) {
161 assertAnyVmsPermission(mContext);
162 unregisterClient(clientToken, VmsClientLogger.ConnectionState.TERMINATED);
163 }
164
165 @Override
166 public VmsProviderInfo getProviderInfo(IBinder clientToken, int providerId) {
167 assertAnyVmsPermission(mContext);
168 getClient(clientToken); // Assert that the client is registered
169 return new VmsProviderInfo(mPublishersInfo.getPublisherInfoOrNull(providerId));
170 }
171
172 @Override
173 public void setSubscriptions(IBinder clientToken, List<VmsAssociatedLayer> layers) {
174 assertVmsSubscriberPermission(mContext);
175 getClient(clientToken).setSubscriptions(layers);
176 updateSubscriptionState();
177 }
178
179 @Override
180 public void setMonitoringEnabled(IBinder clientToken, boolean enabled) {
181 assertVmsSubscriberPermission(mContext);
182 getClient(clientToken).setMonitoringEnabled(enabled);
183 }
184
185 @Override
186 public int registerProvider(IBinder clientToken, VmsProviderInfo providerInfo) {
187 assertVmsPublisherPermission(mContext);
188 synchronized (mLock) {
189 VmsClientInfo client = getClient(clientToken);
190 int publisherId = mPublishersInfo.getIdForInfo(providerInfo.getDescription());
191 client.addProviderId(publisherId);
192 return publisherId;
193 }
194 }
195
196 @Override
197 public void setProviderOfferings(IBinder clientToken, int providerId,
198 List<VmsLayerDependency> offerings) {
199 assertVmsPublisherPermission(mContext);
200 VmsClientInfo client = getClient(clientToken);
Mark Tabry62e749c2020-01-10 19:52:59 -0800201 if (!client.hasProviderId(providerId) && !client.isLegacyClient()) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800202 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
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800211 public void publishPacket(IBinder clientToken, int providerId, VmsLayer layer, byte[] packet) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800212 assertVmsPublisherPermission(mContext);
Mark Tabry987c8852020-03-12 10:44:28 -0700213 deliverToSubscribers(clientToken, providerId, layer, packet.length,
214 callback -> callback.onPacketReceived(providerId, layer, packet));
215 }
216
217 @Override
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
227 private void deliverToSubscribers(IBinder clientToken, int providerId, VmsLayer layer,
228 int packetLength, ThrowingConsumer<IVmsClientCallback> callbackConsumer) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800229 VmsClientInfo client = getClient(clientToken);
Mark Tabry62e749c2020-01-10 19:52:59 -0800230 if (!client.hasOffering(providerId, layer) && !client.isLegacyClient()) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800231 throw new IllegalArgumentException("Client does not offer " + layer + " as "
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800232 + providerId);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800233 }
234
Mark Tabry6fa123d2020-01-10 19:52:59 -0800235 mStatsService.getVmsClientLogger(client.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800236 .logPacketSent(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800237
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800238 Collection<VmsClientInfo> subscribers;
239 synchronized (mLock) {
240 subscribers = mClientMap.values().stream()
241 .filter(subscriber -> subscriber.isSubscribed(providerId, layer))
242 .collect(Collectors.toList());
243 }
Mark Tabry6fa123d2020-01-10 19:52:59 -0800244
245 if (DBG) Log.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
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800249 mStatsService.getVmsClientLogger(-1).logPacketDropped(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800250 return;
251 }
252
253 for (VmsClientInfo subscriber : subscribers) {
254 try {
Mark Tabry987c8852020-03-12 10:44:28 -0700255 callbackConsumer.accept(subscriber.getCallback());
Mark Tabry6fa123d2020-01-10 19:52:59 -0800256 mStatsService.getVmsClientLogger(subscriber.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800257 .logPacketReceived(layer, packetLength);
Mark Tabry987c8852020-03-12 10:44:28 -0700258 } catch (RuntimeException e) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800259 mStatsService.getVmsClientLogger(subscriber.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800260 .logPacketDropped(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800261 Log.e(TAG, String.format("Unable to publish to listener: %s",
Mark Tabry987c8852020-03-12 10:44:28 -0700262 subscriber.getPackageName()), e);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800263 }
264 }
265 }
266
267 private void unregisterClient(IBinder clientToken, int connectionState) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700268 VmsClientInfo client;
Mark Tabry6fa123d2020-01-10 19:52:59 -0800269 synchronized (mLock) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700270 client = mClientMap.remove(clientToken);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800271 }
Mark Tabrya63623c2020-04-30 15:41:26 -0700272 if (client != null) {
273 client.getCallback().asBinder().unlinkToDeath(client.getDeathRecipient(), 0);
274 mStatsService.getVmsClientLogger(client.getUid())
275 .logConnectionState(connectionState);
276 updateAvailableLayers();
277 updateSubscriptionState();
278 }
Mark Tabry6fa123d2020-01-10 19:52:59 -0800279 }
280
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
291 private Collection<VmsClientInfo> getActiveClients() {
292 synchronized (mLock) {
293 return new ArrayList<>(mClientMap.values());
294 }
295 }
296
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
317 private void notifyOfAvailabilityChange(VmsAvailableLayers availableLayers) {
318 Log.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 Log.w(TAG, "onLayersAvailabilityChanged failed: " + client.getPackageName(),
324 e);
325 }
326 }
327 }
328
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());
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800337 client.getLayerAndProviderSubscriptions().forEach((layer, providerIds) -> {
338 Set<Integer> providerSubscriptions =
Mark Tabry6fa123d2020-01-10 19:52:59 -0800339 layerAndProviderSubscriptions.computeIfAbsent(
340 layer,
341 ignored -> new ArraySet<>());
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800342 providerSubscriptions.addAll(providerIds);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800343 });
344 }
345
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800346 // Remove global layer subscriptions from provider-specific subscription state
Mark Tabry6fa123d2020-01-10 19:52:59 -0800347 layerSubscriptions.forEach(layerAndProviderSubscriptions::remove);
348
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800349 // Transform provider-specific subscriptions into VmsAssociatedLayers
Mark Tabry6fa123d2020-01-10 19:52:59 -0800350 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
372 private void notifyOfSubscriptionChange(VmsSubscriptionState subscriptionState) {
373 Log.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 Log.w(TAG, "onSubscriptionStateChanged failed: " + client.getPackageName(),
379 e);
380 }
381 }
382 }
383}