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