1 /* 2 * Copyright (C) 2017 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 package com.android.car.hal; 17 18 import static com.android.car.CarServiceUtils.toByteArray; 19 20 import android.car.VehicleAreaType; 21 import android.car.vms.VmsAssociatedLayer; 22 import android.car.vms.VmsAvailableLayers; 23 import android.car.vms.VmsClient; 24 import android.car.vms.VmsClientManager.VmsClientCallback; 25 import android.car.vms.VmsLayer; 26 import android.car.vms.VmsLayerDependency; 27 import android.car.vms.VmsSubscriptionHelper; 28 import android.car.vms.VmsSubscriptionState; 29 import android.content.Context; 30 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 31 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 32 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 33 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup; 34 import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex; 35 import android.hardware.automotive.vehicle.V2_0.VmsMessageType; 36 import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerAndPublisherIdIntegerValuesIndex; 37 import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex; 38 import android.hardware.automotive.vehicle.V2_0.VmsOfferingMessageIntegerValuesIndex; 39 import android.hardware.automotive.vehicle.V2_0.VmsPublisherInformationIntegerValuesIndex; 40 import android.hardware.automotive.vehicle.V2_0.VmsStartSessionMessageIntegerValuesIndex; 41 import android.os.Build; 42 import android.os.Handler; 43 import android.os.HandlerExecutor; 44 import android.os.HandlerThread; 45 import android.os.RemoteException; 46 import android.os.SystemClock; 47 import android.util.ArraySet; 48 import android.util.Log; 49 50 import androidx.annotation.GuardedBy; 51 import androidx.annotation.VisibleForTesting; 52 53 import com.android.car.CarLocalServices; 54 import com.android.car.CarServiceUtils; 55 import com.android.car.vms.VmsBrokerService; 56 57 import java.io.FileDescriptor; 58 import java.io.FileOutputStream; 59 import java.io.IOException; 60 import java.io.PrintWriter; 61 import java.util.ArrayList; 62 import java.util.Collection; 63 import java.util.HashSet; 64 import java.util.List; 65 import java.util.Set; 66 import java.util.function.BiFunction; 67 import java.util.function.Supplier; 68 69 /** 70 * VMS client implementation that proxies VmsPublisher/VmsSubscriber API calls to the Vehicle HAL 71 * using HAL-specific message encodings. 72 * 73 * @see android.hardware.automotive.vehicle.V2_0 74 */ 75 public class VmsHalService extends HalServiceBase { 76 private static final boolean DBG = false; 77 private static final String TAG = "VmsHalService"; 78 private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE; 79 private static final int[] SUPPORTED_PROPERTIES = new int[]{ 80 HAL_PROPERTY_ID 81 }; 82 private static final int NUM_INTEGERS_IN_VMS_LAYER = 3; 83 private static final int UNKNOWN_CLIENT_ID = -1; 84 private static final byte[] DEFAULT_PUBLISHER_INFO = new byte[0]; 85 86 private final VehicleHal mVehicleHal; 87 private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread( 88 getClass().getSimpleName()); 89 private final Handler mHandler = new Handler(mHandlerThread.getLooper()); 90 private final int mCoreId; 91 private final BiFunction<Handler, VmsClientCallback, VmsClient> mInitVmsClient; 92 private final int mClientMetricsProperty; 93 private final boolean mPropagatePropertyException; 94 private final VmsSubscriptionHelper mSubscriptionHelper = 95 new VmsSubscriptionHelper(this::setSubscriptions); 96 97 private final Object mLock = new Object(); 98 @GuardedBy("mLock") 99 private boolean mIsSupported; 100 @GuardedBy("mLock") 101 private VmsClient mClient; 102 103 private final VmsClientCallback mClientCallback = new VmsClientCallback() { 104 @Override 105 public void onClientConnected(VmsClient client) { 106 Log.wtf(TAG, "onClientConnnected triggered for local client"); 107 } 108 109 @Override 110 public void onSubscriptionStateChanged(VmsSubscriptionState subscriptionState) { 111 if (DBG) Log.d(TAG, "Handling a subscription state change"); 112 setPropertyValue(createSubscriptionStateMessage(VmsMessageType.SUBSCRIPTIONS_CHANGE, 113 subscriptionState)); 114 } 115 116 @Override 117 public void onLayerAvailabilityChanged(VmsAvailableLayers availableLayers) { 118 if (DBG) Log.d(TAG, "Handling a layer availability change"); 119 setPropertyValue(createAvailableLayersMessage(VmsMessageType.AVAILABILITY_CHANGE, 120 availableLayers)); 121 } 122 123 @Override 124 public void onPacketReceived(int providerId, VmsLayer layer, byte[] packet) { 125 if (DBG) Log.d(TAG, "Handling a data message for Layer: " + layer); 126 setPropertyValue(createDataMessage(layer, providerId, packet)); 127 } 128 }; 129 130 /** 131 * Constructor used by {@link VehicleHal} 132 */ VmsHalService(Context context, VehicleHal vehicleHal)133 VmsHalService(Context context, VehicleHal vehicleHal) { 134 this(context, vehicleHal, SystemClock::uptimeMillis, VmsHalService::initVmsClient, 135 Build.IS_DEBUGGABLE); 136 } 137 138 @VisibleForTesting VmsHalService(Context context, VehicleHal vehicleHal, Supplier<Long> getCoreId, BiFunction<Handler, VmsClientCallback, VmsClient> initVmsClient, boolean propagatePropertyException)139 VmsHalService(Context context, VehicleHal vehicleHal, Supplier<Long> getCoreId, 140 BiFunction<Handler, VmsClientCallback, VmsClient> initVmsClient, 141 boolean propagatePropertyException) { 142 mVehicleHal = vehicleHal; 143 mCoreId = (int) (getCoreId.get() % Integer.MAX_VALUE); 144 mInitVmsClient = initVmsClient; 145 mClientMetricsProperty = getClientMetricsProperty(context); 146 mPropagatePropertyException = propagatePropertyException; 147 } 148 getClientMetricsProperty(Context context)149 private static int getClientMetricsProperty(Context context) { 150 int propId = context.getResources().getInteger( 151 com.android.car.R.integer.vmsHalClientMetricsProperty); 152 if (propId == 0) { 153 Log.i(TAG, "Metrics collection disabled"); 154 return 0; 155 } 156 if ((propId & VehiclePropertyGroup.MASK) != VehiclePropertyGroup.VENDOR) { 157 Log.w(TAG, String.format("Metrics collection disabled, non-vendor property: 0x%x", 158 propId)); 159 return 0; 160 } 161 162 Log.i(TAG, String.format("Metrics collection property: 0x%x", propId)); 163 return propId; 164 } 165 166 /** 167 * Retrieves the callback message handler for use by unit tests. 168 */ 169 @VisibleForTesting getHandler()170 Handler getHandler() { 171 return mHandler; 172 } 173 174 @Override getAllSupportedProperties()175 public int[] getAllSupportedProperties() { 176 return SUPPORTED_PROPERTIES; 177 } 178 179 @Override takeProperties(Collection<VehiclePropConfig> properties)180 public void takeProperties(Collection<VehiclePropConfig> properties) { 181 if (properties.isEmpty()) { 182 return; 183 } 184 synchronized (mLock) { 185 mIsSupported = true; 186 } 187 } 188 189 @Override init()190 public void init() { 191 synchronized (mLock) { 192 if (!mIsSupported) { 193 Log.i(TAG, "VmsHalService VHAL property not supported"); 194 return; // Do not continue initialization 195 } 196 connectVmsClient(); 197 } 198 199 Log.i(TAG, "Initializing VmsHalService VHAL property"); 200 mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID); 201 202 mHandler.post(() -> 203 setPropertyValue(createStartSessionMessage(mCoreId, UNKNOWN_CLIENT_ID))); 204 } 205 206 @Override release()207 public void release() { 208 synchronized (mLock) { 209 disconnectVmsClient(); 210 if (!mIsSupported) { 211 return; 212 } 213 } 214 if (DBG) { 215 Log.d(TAG, "Releasing VmsHalService VHAL property"); 216 } 217 mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID); 218 } 219 220 @Override dump(PrintWriter writer)221 public void dump(PrintWriter writer) { 222 synchronized (mLock) { 223 writer.println("*VMS HAL*"); 224 writer.printf("VmsProperty: %s\n", mIsSupported ? "supported" : "unsupported"); 225 if (mClient == null) { 226 writer.println("VmsClient: disconnected"); 227 return; 228 } 229 writer.println("VmsClient: connected"); 230 writer.printf("Subscriptions: %s\n", mSubscriptionHelper.getSubscriptions()); 231 writer.printf("AvailableLayers: %s\n", mClient.getAvailableLayers()); 232 writer.printf("SubscriptionState: %s\n", mClient.getSubscriptionState()); 233 } 234 } 235 236 /** 237 * Dumps HAL client metrics obtained by reading the VMS HAL property. 238 * 239 * @param fd Dumpsys file descriptor to write client metrics to. 240 */ dumpMetrics(FileDescriptor fd)241 public void dumpMetrics(FileDescriptor fd) { 242 if (mClientMetricsProperty == 0) { 243 Log.w(TAG, "Metrics collection is disabled"); 244 return; 245 } 246 247 VehiclePropValue vehicleProp = null; 248 try { 249 vehicleProp = mVehicleHal.get(mClientMetricsProperty); 250 } catch (RuntimeException e) { 251 // Failures to retrieve metrics should be non-fatal 252 Log.e(TAG, "While reading metrics from client", e); 253 } 254 if (vehicleProp == null) { 255 if (DBG) Log.d(TAG, "Metrics unavailable"); 256 return; 257 } 258 259 try (FileOutputStream fout = new FileOutputStream(fd)) { 260 fout.write(toByteArray(vehicleProp.value.bytes)); 261 fout.flush(); 262 } catch (IOException e) { 263 Log.e(TAG, "Error writing metrics to output stream", e); 264 } 265 } 266 267 /** 268 * Consumes/produces HAL messages. 269 * 270 * The format of these messages is defined in: 271 * hardware/interfaces/automotive/vehicle/2.0/types.hal 272 */ 273 @Override onHalEvents(List<VehiclePropValue> values)274 public void onHalEvents(List<VehiclePropValue> values) { 275 if (DBG) Log.d(TAG, "Handling a VMS property change"); 276 for (VehiclePropValue v : values) { 277 ArrayList<Integer> vec = v.value.int32Values; 278 int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE); 279 280 if (DBG) Log.d(TAG, "Received " + VmsMessageType.toString(messageType) + " message"); 281 try { 282 switch (messageType) { 283 case VmsMessageType.DATA: 284 handleDataEvent(vec, toByteArray(v.value.bytes)); 285 break; 286 case VmsMessageType.SUBSCRIBE: 287 handleSubscribeEvent(vec); 288 break; 289 case VmsMessageType.UNSUBSCRIBE: 290 handleUnsubscribeEvent(vec); 291 break; 292 case VmsMessageType.SUBSCRIBE_TO_PUBLISHER: 293 handleSubscribeToPublisherEvent(vec); 294 break; 295 case VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER: 296 handleUnsubscribeFromPublisherEvent(vec); 297 break; 298 case VmsMessageType.PUBLISHER_ID_REQUEST: 299 handlePublisherIdRequest(toByteArray(v.value.bytes)); 300 break; 301 case VmsMessageType.PUBLISHER_INFORMATION_REQUEST: 302 handlePublisherInfoRequest(vec); 303 case VmsMessageType.OFFERING: 304 handleOfferingEvent(vec); 305 break; 306 case VmsMessageType.AVAILABILITY_REQUEST: 307 handleAvailabilityRequestEvent(); 308 break; 309 case VmsMessageType.SUBSCRIPTIONS_REQUEST: 310 handleSubscriptionsRequestEvent(); 311 break; 312 case VmsMessageType.START_SESSION: 313 handleStartSessionEvent(vec); 314 break; 315 default: 316 Log.e(TAG, "Unexpected message type: " + messageType); 317 } 318 } catch (IndexOutOfBoundsException e) { 319 Log.e(TAG, "While handling " + VmsMessageType.toString(messageType), e); 320 } 321 } 322 } 323 connectVmsClient()324 private void connectVmsClient() { 325 synchronized (mLock) { 326 mClient = mInitVmsClient.apply(mHandler, mClientCallback); 327 } 328 } 329 disconnectVmsClient()330 private void disconnectVmsClient() { 331 synchronized (mLock) { 332 if (mClient != null) { 333 try { 334 mClient.unregister(); 335 } catch (RemoteException e) { 336 Log.wtf(TAG, "Local broker should not throw RemoteException", e); 337 } 338 mClient = null; 339 } 340 } 341 } 342 initVmsClient(Handler handler, VmsClientCallback callback)343 private static VmsClient initVmsClient(Handler handler, VmsClientCallback callback) { 344 VmsBrokerService brokerService = CarLocalServices.getService(VmsBrokerService.class); 345 if (brokerService == null) { 346 Log.e(TAG, "Broker service is not enabled"); 347 return null; 348 } 349 VmsClient client = new VmsClient(brokerService, new HandlerExecutor(handler), callback, 350 /* legacyClient= */ true, /* autoCloseMemory */ false, 351 /* exceptionHandler= */ ignored -> { }); 352 try { 353 client.register(); 354 } catch (RemoteException e) { 355 Log.wtf(TAG, "Local broker should not throw RemoteException", e); 356 } 357 return client; 358 } 359 getVmsClient()360 private VmsClient getVmsClient() { 361 synchronized (mLock) { 362 if (mClient == null) { 363 throw new IllegalStateException("VmsClient is not connected"); 364 } 365 return mClient; 366 } 367 } 368 369 /** 370 * SESSION_START message format: 371 * <ul> 372 * <li>Message type 373 * <li>Core ID 374 * <li>Client ID 375 * </ul> 376 */ handleStartSessionEvent(List<Integer> message)377 private void handleStartSessionEvent(List<Integer> message) { 378 int coreId = message.get(VmsStartSessionMessageIntegerValuesIndex.SERVICE_ID); 379 int clientId = message.get(VmsStartSessionMessageIntegerValuesIndex.CLIENT_ID); 380 Log.i(TAG, "Starting new session with coreId: " + coreId + " client: " + clientId); 381 382 if (coreId != mCoreId) { 383 // Reset VmsClient 384 disconnectVmsClient(); 385 connectVmsClient(); 386 // Send acknowledgement message 387 setPropertyValue(createStartSessionMessage(mCoreId, clientId)); 388 } 389 mClientCallback.onLayerAvailabilityChanged(getVmsClient().getAvailableLayers()); 390 } 391 392 /** 393 * DATA message format: 394 * <ul> 395 * <li>Message type 396 * <li>Layer ID 397 * <li>Layer subtype 398 * <li>Layer version 399 * <li>Publisher ID 400 * <li>Payload 401 * </ul> 402 */ handleDataEvent(List<Integer> message, byte[] payload)403 private void handleDataEvent(List<Integer> message, byte[] payload) { 404 VmsLayer vmsLayer = parseVmsLayerFromMessage(message); 405 int publisherId = parsePublisherIdFromMessage(message); 406 if (DBG) { 407 Log.d(TAG, 408 "Handling a data event for Layer: " + vmsLayer + " Publisher: " + publisherId); 409 } 410 getVmsClient().publishPacket(publisherId, vmsLayer, payload); 411 } 412 413 /** 414 * SUBSCRIBE message format: 415 * <ul> 416 * <li>Message type 417 * <li>Layer ID 418 * <li>Layer subtype 419 * <li>Layer version 420 * </ul> 421 */ handleSubscribeEvent(List<Integer> message)422 private void handleSubscribeEvent(List<Integer> message) { 423 VmsLayer vmsLayer = parseVmsLayerFromMessage(message); 424 if (DBG) Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer); 425 mSubscriptionHelper.subscribe(vmsLayer); 426 } 427 428 /** 429 * SUBSCRIBE_TO_PUBLISHER message format: 430 * <ul> 431 * <li>Message type 432 * <li>Layer ID 433 * <li>Layer subtype 434 * <li>Layer version 435 * <li>Publisher ID 436 * </ul> 437 */ handleSubscribeToPublisherEvent(List<Integer> message)438 private void handleSubscribeToPublisherEvent(List<Integer> message) { 439 VmsLayer vmsLayer = parseVmsLayerFromMessage(message); 440 int publisherId = parsePublisherIdFromMessage(message); 441 if (DBG) { 442 Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer 443 + " Publisher: " + publisherId); 444 } 445 mSubscriptionHelper.subscribe(vmsLayer, publisherId); 446 } 447 448 /** 449 * UNSUBSCRIBE message format: 450 * <ul> 451 * <li>Message type 452 * <li>Layer ID 453 * <li>Layer subtype 454 * <li>Layer version 455 * </ul> 456 */ handleUnsubscribeEvent(List<Integer> message)457 private void handleUnsubscribeEvent(List<Integer> message) { 458 VmsLayer vmsLayer = parseVmsLayerFromMessage(message); 459 if (DBG) Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer); 460 mSubscriptionHelper.unsubscribe(vmsLayer); 461 } 462 463 /** 464 * UNSUBSCRIBE_TO_PUBLISHER message format: 465 * <ul> 466 * <li>Message type 467 * <li>Layer ID 468 * <li>Layer subtype 469 * <li>Layer version 470 * <li>Publisher ID 471 * </ul> 472 */ handleUnsubscribeFromPublisherEvent(List<Integer> message)473 private void handleUnsubscribeFromPublisherEvent(List<Integer> message) { 474 VmsLayer vmsLayer = parseVmsLayerFromMessage(message); 475 int publisherId = parsePublisherIdFromMessage(message); 476 if (DBG) { 477 Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer 478 + " Publisher: " + publisherId); 479 } 480 mSubscriptionHelper.unsubscribe(vmsLayer, publisherId); 481 } 482 setSubscriptions(Set<VmsAssociatedLayer> subscriptions)483 private void setSubscriptions(Set<VmsAssociatedLayer> subscriptions) { 484 getVmsClient().setSubscriptions(subscriptions); 485 } 486 487 /** 488 * PUBLISHER_ID_REQUEST message format: 489 * <ul> 490 * <li>Message type 491 * <li>Publisher info (bytes) 492 * </ul> 493 * 494 * PUBLISHER_ID_RESPONSE message format: 495 * <ul> 496 * <li>Message type 497 * <li>Publisher ID 498 * </ul> 499 */ handlePublisherIdRequest(byte[] payload)500 private void handlePublisherIdRequest(byte[] payload) { 501 if (DBG) { 502 Log.d(TAG, "Handling a publisher id request event"); 503 } 504 505 VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.PUBLISHER_ID_RESPONSE); 506 507 // Publisher ID 508 vehicleProp.value.int32Values.add(getVmsClient().registerProvider(payload)); 509 setPropertyValue(vehicleProp); 510 } 511 512 513 /** 514 * PUBLISHER_INFORMATION_REQUEST message format: 515 * <ul> 516 * <li>Message type 517 * <li>Publisher ID 518 * </ul> 519 * 520 * PUBLISHER_INFORMATION_RESPONSE message format: 521 * <ul> 522 * <li>Message type 523 * <li>Publisher info (bytes) 524 * </ul> 525 */ handlePublisherInfoRequest(List<Integer> message)526 private void handlePublisherInfoRequest(List<Integer> message) { 527 if (DBG) Log.d(TAG, "Handling a publisher info request event"); 528 int publisherId = message.get(VmsPublisherInformationIntegerValuesIndex.PUBLISHER_ID); 529 530 VehiclePropValue vehicleProp = 531 createVmsMessage(VmsMessageType.PUBLISHER_INFORMATION_RESPONSE); 532 533 // Publisher Info 534 byte[] publisherInfo = getVmsClient().getProviderDescription(publisherId); 535 appendBytes(vehicleProp.value.bytes, 536 publisherInfo != null ? publisherInfo : DEFAULT_PUBLISHER_INFO); 537 setPropertyValue(vehicleProp); 538 } 539 540 /** 541 * OFFERING message format: 542 * <ul> 543 * <li>Message type 544 * <li>Publisher ID 545 * <li>Number of offerings. 546 * <li>Offerings (x number of offerings) 547 * <ul> 548 * <li>Layer ID 549 * <li>Layer subtype 550 * <li>Layer version 551 * <li>Number of layer dependencies. 552 * <li>Layer dependencies (x number of layer dependencies) 553 * <ul> 554 * <li>Layer ID 555 * <li>Layer subtype 556 * <li>Layer version 557 * </ul> 558 * </ul> 559 * </ul> 560 */ handleOfferingEvent(List<Integer> message)561 private void handleOfferingEvent(List<Integer> message) { 562 // Publisher ID for OFFERING is stored at a different index than in other message types 563 int publisherId = message.get(VmsOfferingMessageIntegerValuesIndex.PUBLISHER_ID); 564 int numLayerDependencies = 565 message.get( 566 VmsOfferingMessageIntegerValuesIndex.NUMBER_OF_OFFERS); 567 if (DBG) { 568 Log.d(TAG, "Handling an offering event of " + numLayerDependencies 569 + " layers for Publisher: " + publisherId); 570 } 571 572 Set<VmsLayerDependency> offeredLayers = new ArraySet<>(numLayerDependencies); 573 int idx = VmsOfferingMessageIntegerValuesIndex.OFFERING_START; 574 for (int i = 0; i < numLayerDependencies; i++) { 575 VmsLayer offeredLayer = parseVmsLayerAtIndex(message, idx); 576 idx += NUM_INTEGERS_IN_VMS_LAYER; 577 578 int numDependenciesForLayer = message.get(idx++); 579 if (numDependenciesForLayer == 0) { 580 offeredLayers.add(new VmsLayerDependency(offeredLayer)); 581 } else { 582 Set<VmsLayer> dependencies = new HashSet<>(); 583 584 for (int j = 0; j < numDependenciesForLayer; j++) { 585 VmsLayer dependantLayer = parseVmsLayerAtIndex(message, idx); 586 idx += NUM_INTEGERS_IN_VMS_LAYER; 587 dependencies.add(dependantLayer); 588 } 589 offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies)); 590 } 591 } 592 getVmsClient().setProviderOfferings(publisherId, offeredLayers); 593 } 594 595 /** 596 * AVAILABILITY_REQUEST message format: 597 * <ul> 598 * <li>Message type 599 * </ul> 600 */ handleAvailabilityRequestEvent()601 private void handleAvailabilityRequestEvent() { 602 setPropertyValue(createAvailableLayersMessage(VmsMessageType.AVAILABILITY_RESPONSE, 603 getVmsClient().getAvailableLayers())); 604 } 605 606 /** 607 * SUBSCRIPTION_REQUEST message format: 608 * <ul> 609 * <li>Message type 610 * </ul> 611 */ handleSubscriptionsRequestEvent()612 private void handleSubscriptionsRequestEvent() { 613 setPropertyValue(createSubscriptionStateMessage(VmsMessageType.SUBSCRIPTIONS_RESPONSE, 614 getVmsClient().getSubscriptionState())); 615 } 616 setPropertyValue(VehiclePropValue vehicleProp)617 private void setPropertyValue(VehiclePropValue vehicleProp) { 618 int messageType = vehicleProp.value.int32Values.get( 619 VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE); 620 621 synchronized (mLock) { 622 if (!mIsSupported) { 623 Log.w(TAG, "HAL unsupported while attempting to send " 624 + VmsMessageType.toString(messageType)); 625 return; 626 } 627 } 628 629 try { 630 mVehicleHal.set(vehicleProp); 631 } catch (RuntimeException e) { 632 Log.e(TAG, "While sending " + VmsMessageType.toString(messageType), e); 633 if (mPropagatePropertyException) { 634 throw new IllegalStateException(e); 635 } 636 } 637 } 638 639 /** 640 * Creates a SESSION_START type {@link VehiclePropValue}. 641 * 642 * SESSION_START message format: 643 * <ul> 644 * <li>Message type 645 * <li>Core ID 646 * <li>Client ID 647 * </ul> 648 */ createStartSessionMessage(int coreId, int clientId)649 private static VehiclePropValue createStartSessionMessage(int coreId, int clientId) { 650 // Message type + layer 651 VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.START_SESSION); 652 List<Integer> message = vehicleProp.value.int32Values; 653 654 // Core ID 655 message.add(coreId); 656 657 // Client ID 658 message.add(clientId); 659 660 return vehicleProp; 661 } 662 663 /** 664 * Creates a DATA type {@link VehiclePropValue}. 665 * 666 * DATA message format: 667 * <ul> 668 * <li>Message type 669 * <li>Layer ID 670 * <li>Layer subtype 671 * <li>Layer version 672 * <li>Publisher ID 673 * <li>Payload 674 * </ul> 675 * 676 * @param layer Layer for which message was published. 677 * @param publisherId Publisher of message 678 * @param payload Data message 679 */ createDataMessage(VmsLayer layer, int publisherId, byte[] payload)680 private static VehiclePropValue createDataMessage(VmsLayer layer, int publisherId, 681 byte[] payload) { 682 // Message type + layer 683 VehiclePropValue vehicleProp = createVmsMessage(VmsMessageType.DATA); 684 appendLayer(vehicleProp.value.int32Values, layer); 685 List<Integer> message = vehicleProp.value.int32Values; 686 687 // Publisher ID 688 message.add(publisherId); 689 690 // Payload 691 appendBytes(vehicleProp.value.bytes, payload); 692 return vehicleProp; 693 } 694 695 /** 696 * Creates a SUBSCRIPTION_CHANGE or SUBSCRIPTION_RESPONSE type {@link VehiclePropValue}. 697 * 698 * Both message types have the same format: 699 * <ul> 700 * <li>Message type 701 * <li>Sequence number 702 * <li>Number of layers 703 * <li>Number of associated layers 704 * <li>Layers (x number of layers) (see {@link #appendLayer}) 705 * <li>Associated layers (x number of associated layers) (see {@link #appendAssociatedLayer}) 706 * </ul> 707 * 708 * @param messageType Either SUBSCRIPTIONS_CHANGE or SUBSCRIPTIONS_RESPONSE. 709 * @param subscriptionState The subscription state to encode in the message. 710 */ createSubscriptionStateMessage(int messageType, VmsSubscriptionState subscriptionState)711 private static VehiclePropValue createSubscriptionStateMessage(int messageType, 712 VmsSubscriptionState subscriptionState) { 713 // Message type 714 VehiclePropValue vehicleProp = createVmsMessage(messageType); 715 List<Integer> message = vehicleProp.value.int32Values; 716 717 // Sequence number 718 message.add(subscriptionState.getSequenceNumber()); 719 720 Set<VmsLayer> layers = subscriptionState.getLayers(); 721 Set<VmsAssociatedLayer> associatedLayers = subscriptionState.getAssociatedLayers(); 722 723 // Number of layers 724 message.add(layers.size()); 725 // Number of associated layers 726 message.add(associatedLayers.size()); 727 728 // Layers 729 for (VmsLayer layer : layers) { 730 appendLayer(message, layer); 731 } 732 733 // Associated layers 734 for (VmsAssociatedLayer layer : associatedLayers) { 735 appendAssociatedLayer(message, layer); 736 } 737 return vehicleProp; 738 } 739 740 /** 741 * Creates an AVAILABILITY_CHANGE or AVAILABILITY_RESPONSE type {@link VehiclePropValue}. 742 * 743 * Both message types have the same format: 744 * <ul> 745 * <li>Message type 746 * <li>Sequence number. 747 * <li>Number of associated layers. 748 * <li>Associated layers (x number of associated layers) (see {@link #appendAssociatedLayer}) 749 * </ul> 750 * 751 * @param messageType Either AVAILABILITY_CHANGE or AVAILABILITY_RESPONSE. 752 * @param availableLayers The available layers to encode in the message. 753 */ createAvailableLayersMessage(int messageType, VmsAvailableLayers availableLayers)754 private static VehiclePropValue createAvailableLayersMessage(int messageType, 755 VmsAvailableLayers availableLayers) { 756 // Message type 757 VehiclePropValue vehicleProp = createVmsMessage(messageType); 758 List<Integer> message = vehicleProp.value.int32Values; 759 760 // Sequence number 761 message.add(availableLayers.getSequence()); 762 763 // Number of associated layers 764 message.add(availableLayers.getAssociatedLayers().size()); 765 766 // Associated layers 767 for (VmsAssociatedLayer layer : availableLayers.getAssociatedLayers()) { 768 appendAssociatedLayer(message, layer); 769 } 770 return vehicleProp; 771 } 772 773 /** 774 * Creates a base {@link VehiclePropValue} of the requested message type, with no message fields 775 * populated. 776 * 777 * @param messageType Type of message, from {@link VmsMessageType} 778 */ createVmsMessage(int messageType)779 private static VehiclePropValue createVmsMessage(int messageType) { 780 VehiclePropValue vehicleProp = new VehiclePropValue(); 781 vehicleProp.prop = HAL_PROPERTY_ID; 782 vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; 783 vehicleProp.value.int32Values.add(messageType); 784 return vehicleProp; 785 } 786 787 /** 788 * Appends a {@link VmsLayer} to an encoded VMS message. 789 * 790 * Layer format: 791 * <ul> 792 * <li>Layer ID 793 * <li>Layer subtype 794 * <li>Layer version 795 * </ul> 796 * 797 * @param message Message to append to. 798 * @param layer Layer to append. 799 */ appendLayer(List<Integer> message, VmsLayer layer)800 private static void appendLayer(List<Integer> message, VmsLayer layer) { 801 message.add(layer.getType()); 802 message.add(layer.getSubtype()); 803 message.add(layer.getVersion()); 804 } 805 806 /** 807 * Appends a {@link VmsAssociatedLayer} to an encoded VMS message. 808 * 809 * AssociatedLayer format: 810 * <ul> 811 * <li>Layer ID 812 * <li>Layer subtype 813 * <li>Layer version 814 * <li>Number of publishers 815 * <li>Publisher ID (x number of publishers) 816 * </ul> 817 * 818 * @param message Message to append to. 819 * @param layer Layer to append. 820 */ appendAssociatedLayer(List<Integer> message, VmsAssociatedLayer layer)821 private static void appendAssociatedLayer(List<Integer> message, VmsAssociatedLayer layer) { 822 message.add(layer.getVmsLayer().getType()); 823 message.add(layer.getVmsLayer().getSubtype()); 824 message.add(layer.getVmsLayer().getVersion()); 825 message.add(layer.getProviderIds().size()); 826 message.addAll(layer.getProviderIds()); 827 } 828 appendBytes(ArrayList<Byte> dst, byte[] src)829 private static void appendBytes(ArrayList<Byte> dst, byte[] src) { 830 dst.ensureCapacity(src.length); 831 for (byte b : src) { 832 dst.add(b); 833 } 834 } 835 parseVmsLayerFromMessage(List<Integer> message)836 private static VmsLayer parseVmsLayerFromMessage(List<Integer> message) { 837 return parseVmsLayerAtIndex(message, 838 VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE); 839 } 840 parseVmsLayerAtIndex(List<Integer> message, int index)841 private static VmsLayer parseVmsLayerAtIndex(List<Integer> message, int index) { 842 List<Integer> layerValues = message.subList(index, index + NUM_INTEGERS_IN_VMS_LAYER); 843 return new VmsLayer(layerValues.get(0), layerValues.get(1), layerValues.get(2)); 844 } 845 parsePublisherIdFromMessage(List<Integer> message)846 private static int parsePublisherIdFromMessage(List<Integer> message) { 847 return message.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID); 848 } 849 } 850