blob: aed7e691f21bc0bc81e15884cb33364d6bf4c09e [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;
56import java.util.List;
57import java.util.Map;
58import java.util.Set;
59import java.util.function.IntSupplier;
60import 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 */
Mark Tabry58246c32020-03-20 09:38:36 -070068public class VmsBrokerService extends IVmsBrokerService.Stub implements CarServiceBase {
Mark Tabry6fa123d2020-01-10 19:52:59 -080069 private static final boolean DBG = false;
Mark Tabry58246c32020-03-20 09:38:36 -070070 private static final String TAG = VmsBrokerService.class.getSimpleName();
Mark Tabry6fa123d2020-01-10 19:52:59 -080071
72 private final Context mContext;
73 private final PackageManager mPackageManager;
74 private final CarStatsService mStatsService;
75 private final IntSupplier mGetCallingUid;
76
77 private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
78 private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
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
Mark Tabry58246c32020-03-20 09:38:36 -070089 public VmsBrokerService(Context context, CarStatsService statsService) {
Mark Tabry6fa123d2020-01-10 19:52:59 -080090 this(context, statsService, Binder::getCallingUid);
91 }
92
93 @VisibleForTesting
Mark Tabry58246c32020-03-20 09:38:36 -070094 VmsBrokerService(
Mark Tabry6fa123d2020-01-10 19:52:59 -080095 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
105 public void init() {
106 }
107
108 @Override
109 public void release() {
110 }
111
112 @Override
113 public void dump(PrintWriter writer) {
114 // TODO(b/149125079): Implement dumpsys
115 }
116
117 @Override
Mark Tabry62e749c2020-01-10 19:52:59 -0800118 public VmsRegistrationInfo registerClient(IBinder clientToken, IVmsClientCallback callback,
119 boolean legacyClient) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800120 assertAnyVmsPermission(mContext);
121 int clientUid = mGetCallingUid.getAsInt();
122 String clientPackage = mPackageManager.getNameForUid(clientUid);
123 if (DBG) Log.d(TAG, "registerClient uid: " + clientUid + " package: " + clientPackage);
124
125 mStatsService.getVmsClientLogger(clientUid)
126 .logConnectionState(VmsClientLogger.ConnectionState.CONNECTED);
127
Mark Tabrya63623c2020-04-30 15:41:26 -0700128 IBinder.DeathRecipient deathRecipient;
129 try {
130 deathRecipient = () -> unregisterClient(clientToken,
131 VmsClientLogger.ConnectionState.DISCONNECTED);
132 callback.asBinder().linkToDeath(deathRecipient, 0);
133 } catch (RemoteException e) {
134 mStatsService.getVmsClientLogger(clientUid)
135 .logConnectionState(VmsClientLogger.ConnectionState.DISCONNECTED);
136 throw new IllegalStateException("Client callback is already dead");
137 }
138
Mark Tabry6fa123d2020-01-10 19:52:59 -0800139 synchronized (mLock) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700140 mClientMap.put(clientToken, new VmsClientInfo(clientUid, clientPackage, callback,
141 legacyClient, deathRecipient));
Mark Tabry6fa123d2020-01-10 19:52:59 -0800142 return new VmsRegistrationInfo(
143 mAvailableLayers.getAvailableLayers(),
144 mSubscriptionState);
145 }
146 }
147
148 @Override
149 public void unregisterClient(IBinder clientToken) {
150 assertAnyVmsPermission(mContext);
151 unregisterClient(clientToken, VmsClientLogger.ConnectionState.TERMINATED);
152 }
153
154 @Override
155 public VmsProviderInfo getProviderInfo(IBinder clientToken, int providerId) {
156 assertAnyVmsPermission(mContext);
157 getClient(clientToken); // Assert that the client is registered
158 return new VmsProviderInfo(mPublishersInfo.getPublisherInfoOrNull(providerId));
159 }
160
161 @Override
162 public void setSubscriptions(IBinder clientToken, List<VmsAssociatedLayer> layers) {
163 assertVmsSubscriberPermission(mContext);
164 getClient(clientToken).setSubscriptions(layers);
165 updateSubscriptionState();
166 }
167
168 @Override
169 public void setMonitoringEnabled(IBinder clientToken, boolean enabled) {
170 assertVmsSubscriberPermission(mContext);
171 getClient(clientToken).setMonitoringEnabled(enabled);
172 }
173
174 @Override
175 public int registerProvider(IBinder clientToken, VmsProviderInfo providerInfo) {
176 assertVmsPublisherPermission(mContext);
177 synchronized (mLock) {
178 VmsClientInfo client = getClient(clientToken);
179 int publisherId = mPublishersInfo.getIdForInfo(providerInfo.getDescription());
180 client.addProviderId(publisherId);
181 return publisherId;
182 }
183 }
184
185 @Override
186 public void setProviderOfferings(IBinder clientToken, int providerId,
187 List<VmsLayerDependency> offerings) {
188 assertVmsPublisherPermission(mContext);
189 VmsClientInfo client = getClient(clientToken);
Mark Tabry62e749c2020-01-10 19:52:59 -0800190 if (!client.hasProviderId(providerId) && !client.isLegacyClient()) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800191 throw new IllegalArgumentException("Client not registered to offer layers as "
192 + providerId);
193 }
194 if (client.setProviderOfferings(providerId, offerings)) {
195 updateAvailableLayers();
196 }
197 }
198
199 @Override
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800200 public void publishPacket(IBinder clientToken, int providerId, VmsLayer layer, byte[] packet) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800201 assertVmsPublisherPermission(mContext);
Mark Tabry987c8852020-03-12 10:44:28 -0700202 deliverToSubscribers(clientToken, providerId, layer, packet.length,
203 callback -> callback.onPacketReceived(providerId, layer, packet));
204 }
205
206 @Override
207 public void publishLargePacket(IBinder clientToken, int providerId, VmsLayer layer,
208 SharedMemory packet) {
209 try (SharedMemory largePacket = packet) {
210 assertVmsPublisherPermission(mContext);
211 deliverToSubscribers(clientToken, providerId, layer, packet.getSize(),
212 callback -> callback.onLargePacketReceived(providerId, layer, largePacket));
213 }
214 }
215
216 private void deliverToSubscribers(IBinder clientToken, int providerId, VmsLayer layer,
217 int packetLength, ThrowingConsumer<IVmsClientCallback> callbackConsumer) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800218 VmsClientInfo client = getClient(clientToken);
Mark Tabry62e749c2020-01-10 19:52:59 -0800219 if (!client.hasOffering(providerId, layer) && !client.isLegacyClient()) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800220 throw new IllegalArgumentException("Client does not offer " + layer + " as "
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800221 + providerId);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800222 }
223
Mark Tabry6fa123d2020-01-10 19:52:59 -0800224 mStatsService.getVmsClientLogger(client.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800225 .logPacketSent(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800226
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800227 Collection<VmsClientInfo> subscribers;
228 synchronized (mLock) {
229 subscribers = mClientMap.values().stream()
230 .filter(subscriber -> subscriber.isSubscribed(providerId, layer))
231 .collect(Collectors.toList());
232 }
Mark Tabry6fa123d2020-01-10 19:52:59 -0800233
234 if (DBG) Log.d(TAG, String.format("Number of subscribers: %d", subscribers.size()));
235
236 if (subscribers.isEmpty()) {
237 // A negative UID signals that the packet had zero subscribers
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800238 mStatsService.getVmsClientLogger(-1).logPacketDropped(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800239 return;
240 }
241
242 for (VmsClientInfo subscriber : subscribers) {
243 try {
Mark Tabry987c8852020-03-12 10:44:28 -0700244 callbackConsumer.accept(subscriber.getCallback());
Mark Tabry6fa123d2020-01-10 19:52:59 -0800245 mStatsService.getVmsClientLogger(subscriber.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800246 .logPacketReceived(layer, packetLength);
Mark Tabry987c8852020-03-12 10:44:28 -0700247 } catch (RuntimeException e) {
Mark Tabry6fa123d2020-01-10 19:52:59 -0800248 mStatsService.getVmsClientLogger(subscriber.getUid())
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800249 .logPacketDropped(layer, packetLength);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800250 Log.e(TAG, String.format("Unable to publish to listener: %s",
Mark Tabry987c8852020-03-12 10:44:28 -0700251 subscriber.getPackageName()), e);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800252 }
253 }
254 }
255
256 private void unregisterClient(IBinder clientToken, int connectionState) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700257 VmsClientInfo client;
Mark Tabry6fa123d2020-01-10 19:52:59 -0800258 synchronized (mLock) {
Mark Tabrya63623c2020-04-30 15:41:26 -0700259 client = mClientMap.remove(clientToken);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800260 }
Mark Tabrya63623c2020-04-30 15:41:26 -0700261 if (client != null) {
262 client.getCallback().asBinder().unlinkToDeath(client.getDeathRecipient(), 0);
263 mStatsService.getVmsClientLogger(client.getUid())
264 .logConnectionState(connectionState);
265 updateAvailableLayers();
266 updateSubscriptionState();
267 }
Mark Tabry6fa123d2020-01-10 19:52:59 -0800268 }
269
270 private VmsClientInfo getClient(IBinder clientToken) {
271 synchronized (mLock) {
272 VmsClientInfo client = mClientMap.get(clientToken);
273 if (client == null) {
274 throw new IllegalStateException("Unknown client token");
275 }
276 return client;
277 }
278 }
279
280 private Collection<VmsClientInfo> getActiveClients() {
281 synchronized (mLock) {
282 return new ArrayList<>(mClientMap.values());
283 }
284 }
285
286 private void updateAvailableLayers() {
287 synchronized (mLock) {
288 // Fuse layer offerings
289 Set<VmsLayersOffering> allOfferings = mClientMap.values().stream()
290 .map(VmsClientInfo::getAllOfferings)
291 .flatMap(Collection::stream)
292 .collect(Collectors.toCollection(ArraySet::new));
293
294 // Ignore update if offerings are unchanged
295 if (mAllOfferings.equals(allOfferings)) {
296 return;
297 }
298
299 // Update offerings and compute available layers
300 mAllOfferings = allOfferings;
301 mAvailableLayers.setPublishersOffering(allOfferings);
302 }
303 notifyOfAvailabilityChange(mAvailableLayers.getAvailableLayers());
304 }
305
306 private void notifyOfAvailabilityChange(VmsAvailableLayers availableLayers) {
307 Log.i(TAG, "Notifying clients of layer availability change: " + availableLayers);
308 for (VmsClientInfo client : getActiveClients()) {
309 try {
310 client.getCallback().onLayerAvailabilityChanged(availableLayers);
311 } catch (RemoteException e) {
312 Log.w(TAG, "onLayersAvailabilityChanged failed: " + client.getPackageName(),
313 e);
314 }
315 }
316 }
317
318 private void updateSubscriptionState() {
319 VmsSubscriptionState subscriptionState;
320 synchronized (mLock) {
321 Set<VmsLayer> layerSubscriptions = new ArraySet<>();
322 Map<VmsLayer, Set<Integer>> layerAndProviderSubscriptions = new ArrayMap<>();
323 // Fuse subscriptions
324 for (VmsClientInfo client : mClientMap.values()) {
325 layerSubscriptions.addAll(client.getLayerSubscriptions());
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800326 client.getLayerAndProviderSubscriptions().forEach((layer, providerIds) -> {
327 Set<Integer> providerSubscriptions =
Mark Tabry6fa123d2020-01-10 19:52:59 -0800328 layerAndProviderSubscriptions.computeIfAbsent(
329 layer,
330 ignored -> new ArraySet<>());
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800331 providerSubscriptions.addAll(providerIds);
Mark Tabry6fa123d2020-01-10 19:52:59 -0800332 });
333 }
334
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800335 // Remove global layer subscriptions from provider-specific subscription state
Mark Tabry6fa123d2020-01-10 19:52:59 -0800336 layerSubscriptions.forEach(layerAndProviderSubscriptions::remove);
337
Mark Tabry4b3a64a2020-02-18 00:23:41 -0800338 // Transform provider-specific subscriptions into VmsAssociatedLayers
Mark Tabry6fa123d2020-01-10 19:52:59 -0800339 Set<VmsAssociatedLayer> associatedLayers =
340 layerAndProviderSubscriptions.entrySet().stream()
341 .map(entry -> new VmsAssociatedLayer(entry.getKey(), entry.getValue()))
342 .collect(Collectors.toCollection(ArraySet::new));
343
344 // Ignore update if subscriptions are unchanged
345 if (mSubscriptionState.getLayers().equals(layerSubscriptions)
346 && mSubscriptionState.getAssociatedLayers().equals(associatedLayers)) {
347 return;
348 }
349
350 // Update subscription state
351 subscriptionState = new VmsSubscriptionState(
352 mSubscriptionState.getSequenceNumber() + 1,
353 layerSubscriptions,
354 associatedLayers);
355 mSubscriptionState = subscriptionState;
356 }
357 // Notify clients of update
358 notifyOfSubscriptionChange(subscriptionState);
359 }
360
361 private void notifyOfSubscriptionChange(VmsSubscriptionState subscriptionState) {
362 Log.i(TAG, "Notifying clients of subscription state change: " + subscriptionState);
363 for (VmsClientInfo client : getActiveClients()) {
364 try {
365 client.getCallback().onSubscriptionStateChanged(subscriptionState);
366 } catch (RemoteException e) {
367 Log.w(TAG, "onSubscriptionStateChanged failed: " + client.getPackageName(),
368 e);
369 }
370 }
371 }
372}