• 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 android.car.vms;
18 
19 import static android.system.OsConstants.PROT_READ;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemApi;
25 import android.car.Car;
26 import android.car.annotation.AddedInOrBefore;
27 import android.car.vms.VmsClientManager.VmsClientCallback;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.SharedMemory;
32 import android.system.ErrnoException;
33 import android.util.Log;
34 
35 import com.android.internal.annotations.GuardedBy;
36 
37 import java.lang.ref.WeakReference;
38 import java.nio.ByteBuffer;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.Objects;
42 import java.util.Set;
43 import java.util.concurrent.Executor;
44 import java.util.function.BiConsumer;
45 import java.util.function.Consumer;
46 
47 /**
48  * API implementation for use by Vehicle Map Service clients.
49  *
50  * This API can be obtained by registering a callback with {@link VmsClientManager}.
51  *
52  * @hide
53  */
54 @SystemApi
55 public final class VmsClient {
56     private static final boolean DBG = false;
57     private static final String TAG = VmsClient.class.getSimpleName();
58 
59     private static final VmsAvailableLayers DEFAULT_AVAILABLE_LAYERS =
60             new VmsAvailableLayers(Collections.emptySet(), 0);
61     private static final VmsSubscriptionState DEFAULT_SUBSCRIPTIONS =
62             new VmsSubscriptionState(0, Collections.emptySet(), Collections.emptySet());
63     private static final int LARGE_PACKET_THRESHOLD = 16 * 1024; // 16 KB
64 
65     private final IVmsBrokerService mService;
66     private final Executor mExecutor;
67     private final VmsClientCallback mCallback;
68     private final boolean mLegacyClient;
69     private final IVmsClientCallback mClientCallback;
70     private final Consumer<RemoteException> mExceptionHandler;
71     private final IBinder mClientToken;
72 
73     private final Object mLock = new Object();
74     @GuardedBy("mLock")
75     private VmsAvailableLayers mAvailableLayers = DEFAULT_AVAILABLE_LAYERS;
76     @GuardedBy("mLock")
77     private VmsSubscriptionState mSubscriptionState = DEFAULT_SUBSCRIPTIONS;
78     @GuardedBy("mLock")
79     private boolean mMonitoringEnabled;
80 
81     /**
82      * @hide
83      */
VmsClient(IVmsBrokerService service, Executor executor, VmsClientCallback callback, boolean legacyClient, boolean autoCloseMemory, Consumer<RemoteException> exceptionHandler)84     public VmsClient(IVmsBrokerService service, Executor executor, VmsClientCallback callback,
85             boolean legacyClient, boolean autoCloseMemory,
86             Consumer<RemoteException> exceptionHandler) {
87         mService = service;
88         mExecutor = executor;
89         mCallback = callback;
90         mLegacyClient = legacyClient;
91         mClientCallback = new IVmsClientCallbackImpl(this, autoCloseMemory);
92         mExceptionHandler = exceptionHandler;
93         mClientToken = new Binder();
94     }
95 
96     /**
97      * Retrieves registered information about a Vehicle Map Service data provider.
98      *
99      * <p>Data layers may be associated with multiple providers in updates received via
100      * {@link VmsClientCallback#onLayerAvailabilityChanged(VmsAvailableLayers)}, and clients can use
101      * this query to determine a provider or subset of providers to subscribe to.
102      *
103      * @param providerId Provider ID
104      * @return Provider's registration information, or null if unavailable
105      */
106     @Nullable
107     @RequiresPermission(anyOf = {Car.PERMISSION_VMS_PUBLISHER, Car.PERMISSION_VMS_SUBSCRIBER})
108     @AddedInOrBefore(majorVersion = 33)
getProviderDescription(int providerId)109     public byte[] getProviderDescription(int providerId) {
110         if (DBG) Log.d(TAG, "Getting provider information for " + providerId);
111         try {
112             return mService.getProviderInfo(mClientToken, providerId).getDescription();
113         } catch (RemoteException e) {
114             Log.e(TAG, "While getting publisher information for " + providerId, e);
115             mExceptionHandler.accept(e);
116             return null;
117         }
118     }
119 
120     /**
121      * Sets this client's data layer subscriptions
122      *
123      * <p>Existing subscriptions for the client will be replaced.
124      *
125      * @param layers Data layers to be subscribed
126      */
127     @RequiresPermission(Car.PERMISSION_VMS_SUBSCRIBER)
128     @AddedInOrBefore(majorVersion = 33)
setSubscriptions(@onNull Set<VmsAssociatedLayer> layers)129     public void setSubscriptions(@NonNull Set<VmsAssociatedLayer> layers) {
130         if (DBG) Log.d(TAG, "Setting subscriptions to " + layers);
131         try {
132             mService.setSubscriptions(mClientToken, new ArrayList<>(layers));
133         } catch (RemoteException e) {
134             Log.e(TAG, "While setting subscriptions", e);
135             mExceptionHandler.accept(e);
136         }
137     }
138 
139     /**
140      * Enables the monitoring of Vehicle Map Service packets by this client.
141      *
142      * <p>If monitoring is enabled, the client will receive all packets, regardless of any
143      * subscriptions. Enabling monitoring does not affect the client's existing subscriptions.
144      */
145     @RequiresPermission(Car.PERMISSION_VMS_SUBSCRIBER)
146     @AddedInOrBefore(majorVersion = 33)
setMonitoringEnabled(boolean enabled)147     public void setMonitoringEnabled(boolean enabled) {
148         if (DBG) Log.d(TAG, "Setting monitoring state to " + enabled);
149         try {
150             mService.setMonitoringEnabled(mClientToken, enabled);
151             synchronized (mLock) {
152                 mMonitoringEnabled = enabled;
153             }
154         } catch (RemoteException e) {
155             Log.e(TAG, "While setting monitoring state to " + enabled, e);
156             mExceptionHandler.accept(e);
157         }
158     }
159 
160     /**
161      * Returns the current monitoring state of the client.
162      */
163     @RequiresPermission(Car.PERMISSION_VMS_SUBSCRIBER)
164     @AddedInOrBefore(majorVersion = 33)
isMonitoringEnabled()165     public boolean isMonitoringEnabled() {
166         synchronized (mLock) {
167             return mMonitoringEnabled;
168         }
169     }
170 
171     /**
172      * Returns the most recently received data layer availability.
173      */
174     @NonNull
175     @RequiresPermission(anyOf = {Car.PERMISSION_VMS_PUBLISHER, Car.PERMISSION_VMS_SUBSCRIBER})
176     @AddedInOrBefore(majorVersion = 33)
getAvailableLayers()177     public VmsAvailableLayers getAvailableLayers() {
178         synchronized (mLock) {
179             return mAvailableLayers;
180         }
181     }
182 
183     /**
184      * Registers a data provider with the Vehicle Map Service.
185      *
186      * @param providerDescription Identifying information about the data provider
187      * @return Provider ID to use for setting offerings and publishing packets, or -1 on
188      * connection error
189      */
190     @RequiresPermission(Car.PERMISSION_VMS_PUBLISHER)
191     @AddedInOrBefore(majorVersion = 33)
registerProvider(@onNull byte[] providerDescription)192     public int registerProvider(@NonNull byte[] providerDescription) {
193         if (DBG) Log.d(TAG, "Registering provider");
194         Objects.requireNonNull(providerDescription, "providerDescription cannot be null");
195         try {
196             return mService.registerProvider(mClientToken,
197                     new VmsProviderInfo(providerDescription));
198         } catch (RemoteException e) {
199             Log.e(TAG, "While registering provider", e);
200             mExceptionHandler.accept(e);
201             return -1;
202         }
203     }
204 
205     /**
206      * Unregisters a data provider with the Vehicle Map Service.
207      *
208      * @param providerId Provider ID
209      */
210     @RequiresPermission(Car.PERMISSION_VMS_PUBLISHER)
211     @AddedInOrBefore(majorVersion = 33)
unregisterProvider(int providerId)212     public void unregisterProvider(int providerId) {
213         if (DBG) Log.d(TAG, "Unregistering provider");
214         try {
215             setProviderOfferings(providerId, Collections.emptySet());
216         } catch (IllegalArgumentException e) {
217             Log.e(TAG, "While unregistering provider " + providerId, e);
218         }
219     }
220 
221     /**
222      * Sets the data layer offerings for a provider registered through
223      * {@link #registerProvider(byte[])}.
224      *
225      * <p>Existing offerings for the provider ID will be replaced.
226      *
227      * @param providerId Provider ID
228      * @param offerings  Data layer offerings
229      * @throws IllegalArgumentException if the client has not registered the provider
230      */
231     @RequiresPermission(Car.PERMISSION_VMS_PUBLISHER)
232     @AddedInOrBefore(majorVersion = 33)
setProviderOfferings(int providerId, @NonNull Set<VmsLayerDependency> offerings)233     public void setProviderOfferings(int providerId, @NonNull Set<VmsLayerDependency> offerings) {
234         if (DBG) Log.d(TAG, "Setting provider offerings for " + providerId);
235         Objects.requireNonNull(offerings, "offerings cannot be null");
236         try {
237             mService.setProviderOfferings(mClientToken, providerId, new ArrayList<>(offerings));
238         } catch (RemoteException e) {
239             Log.e(TAG, "While setting provider offerings for " + providerId, e);
240             mExceptionHandler.accept(e);
241         }
242     }
243 
244     /**
245      * Publishes a Vehicle Maps Service packet.
246      *
247      * @param providerId Packet provider
248      * @param layer      Packet layer
249      * @param packet     Packet data
250      * @throws IllegalArgumentException if the client does not offer the layer as the provider
251      */
252     @RequiresPermission(Car.PERMISSION_VMS_PUBLISHER)
253     @AddedInOrBefore(majorVersion = 33)
publishPacket(int providerId, @NonNull VmsLayer layer, @NonNull byte[] packet)254     public void publishPacket(int providerId, @NonNull VmsLayer layer, @NonNull byte[] packet) {
255         Objects.requireNonNull(layer, "layer cannot be null");
256         Objects.requireNonNull(packet, "packet cannot be null");
257         if (DBG) {
258             Log.d(TAG, "Publishing packet as " + providerId + " (" + packet.length + " bytes)");
259         }
260         try {
261             if (packet.length < LARGE_PACKET_THRESHOLD) {
262                 mService.publishPacket(mClientToken, providerId, layer, packet);
263             } else {
264                 try (SharedMemory largePacket = packetToSharedMemory(packet)) {
265                     mService.publishLargePacket(mClientToken, providerId, layer,
266                             largePacket);
267                 }
268             }
269         } catch (RemoteException e) {
270             Log.e(TAG, "While publishing packet as " + providerId);
271             mExceptionHandler.accept(e);
272         }
273     }
274 
275     /**
276      * Returns the most recently received data layer subscription state.
277      */
278     @NonNull
279     @RequiresPermission(anyOf = {Car.PERMISSION_VMS_PUBLISHER, Car.PERMISSION_VMS_SUBSCRIBER})
280     @AddedInOrBefore(majorVersion = 33)
getSubscriptionState()281     public VmsSubscriptionState getSubscriptionState() {
282         synchronized (mLock) {
283             return mSubscriptionState;
284         }
285     }
286 
287     /**
288      * Registers this client with the Vehicle Map Service.
289      *
290      * @hide
291      */
292     @AddedInOrBefore(majorVersion = 33)
register()293     public void register() throws RemoteException {
294         VmsRegistrationInfo registrationInfo = mService.registerClient(
295                 mClientToken, mClientCallback, mLegacyClient);
296         synchronized (mLock) {
297             mAvailableLayers = registrationInfo.getAvailableLayers();
298             mSubscriptionState = registrationInfo.getSubscriptionState();
299         }
300     }
301 
302     /**
303      * Unregisters this client from the Vehicle Map Service.
304      *
305      * @hide
306      */
307     @AddedInOrBefore(majorVersion = 33)
unregister()308     public void unregister() throws RemoteException {
309         mService.unregisterClient(mClientToken);
310     }
311 
312     private static class IVmsClientCallbackImpl extends IVmsClientCallback.Stub {
313         private final WeakReference<VmsClient> mClient;
314         private final boolean mAutoCloseMemory;
315 
IVmsClientCallbackImpl(VmsClient client, boolean autoCloseMemory)316         private IVmsClientCallbackImpl(VmsClient client, boolean autoCloseMemory) {
317             mClient = new WeakReference<>(client);
318             mAutoCloseMemory = autoCloseMemory;
319         }
320 
321         @Override
onLayerAvailabilityChanged(VmsAvailableLayers availableLayers)322         public void onLayerAvailabilityChanged(VmsAvailableLayers availableLayers) {
323             if (DBG) Log.d(TAG, "Received new layer availability: " + availableLayers);
324             executeCallback((client, callback) -> {
325                 synchronized (client.mLock) {
326                     client.mAvailableLayers = availableLayers;
327                 }
328                 callback.onLayerAvailabilityChanged(availableLayers);
329             });
330         }
331 
332         @Override
onSubscriptionStateChanged(VmsSubscriptionState subscriptionState)333         public void onSubscriptionStateChanged(VmsSubscriptionState subscriptionState) {
334             if (DBG) Log.d(TAG, "Received new subscription state: " + subscriptionState);
335             executeCallback((client, callback) -> {
336                 synchronized (client.mLock) {
337                     client.mSubscriptionState = subscriptionState;
338                 }
339                 callback.onSubscriptionStateChanged(subscriptionState);
340             });
341         }
342 
343         @Override
onPacketReceived(int providerId, VmsLayer layer, byte[] packet)344         public void onPacketReceived(int providerId, VmsLayer layer, byte[] packet) {
345             if (DBG) {
346                 Log.d(TAG, "Received packet from " + providerId + " for: " + layer
347                         + " (" + packet.length + " bytes)");
348             }
349             executeCallback((client, callback) ->
350                     callback.onPacketReceived(providerId, layer, packet));
351         }
352 
353         @Override
onLargePacketReceived(int providerId, VmsLayer layer, SharedMemory packet)354         public void onLargePacketReceived(int providerId, VmsLayer layer, SharedMemory packet) {
355             if (DBG) {
356                 Log.d(TAG, "Received large packet from " + providerId + " for: " + layer
357                         + " (" + packet.getSize() + " bytes)");
358             }
359             byte[] largePacket;
360             if (mAutoCloseMemory) {
361                 try (SharedMemory autoClosedPacket = packet) {
362                     largePacket = sharedMemoryToPacket(autoClosedPacket);
363                 }
364             } else {
365                 largePacket = sharedMemoryToPacket(packet);
366             }
367             executeCallback((client, callback) ->
368                     callback.onPacketReceived(providerId, layer, largePacket));
369         }
370 
executeCallback(BiConsumer<VmsClient, VmsClientCallback> callbackOperation)371         private void executeCallback(BiConsumer<VmsClient, VmsClientCallback> callbackOperation) {
372             final VmsClient client = mClient.get();
373             if (client == null) {
374                 Log.w(TAG, "VmsClient unavailable");
375                 return;
376             }
377 
378             long token = Binder.clearCallingIdentity();
379             try {
380                 client.mExecutor.execute(() -> callbackOperation.accept(client, client.mCallback));
381             } finally {
382                 Binder.restoreCallingIdentity(token);
383             }
384         }
385     }
386 
packetToSharedMemory(byte[] packet)387     private static SharedMemory packetToSharedMemory(byte[] packet) {
388         SharedMemory shm;
389         try {
390             shm = SharedMemory.create("VmsClient", packet.length);
391         } catch (ErrnoException e) {
392             throw new IllegalStateException("Failed to allocate shared memory", e);
393         }
394 
395         ByteBuffer buffer = null;
396         try {
397             buffer = shm.mapReadWrite();
398             buffer.put(packet);
399         } catch (ErrnoException e) {
400             shm.close();
401             throw new IllegalStateException("Failed to create write buffer", e);
402         } finally {
403             if (buffer != null) {
404                 SharedMemory.unmap(buffer);
405             }
406         }
407 
408         if (!shm.setProtect(PROT_READ)) {
409             shm.close();
410             throw new SecurityException("Failed to set read-only protection on shared memory");
411         }
412 
413         return shm;
414     }
415 
sharedMemoryToPacket(SharedMemory shm)416     private static byte[] sharedMemoryToPacket(SharedMemory shm) {
417         ByteBuffer buffer;
418         try {
419             buffer = shm.mapReadOnly();
420         } catch (ErrnoException e) {
421             throw new IllegalStateException("Failed to create read buffer", e);
422         }
423 
424         byte[] packet;
425         try {
426             packet = new byte[buffer.capacity()];
427             buffer.get(packet);
428         } finally {
429             SharedMemory.unmap(buffer);
430         }
431         return packet;
432     }
433 }
434