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 import static java.lang.Integer.toHexString; 20 21 import android.car.VehicleAreaType; 22 import android.car.annotation.FutureFeature; 23 import android.car.vms.IVmsSubscriberClient; 24 import android.car.vms.VmsLayer; 25 import android.car.vms.VmsSubscriptionState; 26 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 27 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 28 import android.hardware.automotive.vehicle.V2_1.VehicleProperty; 29 import android.hardware.automotive.vehicle.V2_1.VmsMessageIntegerValuesIndex; 30 import android.hardware.automotive.vehicle.V2_1.VmsMessageType; 31 import android.os.SystemClock; 32 import android.util.Log; 33 import com.android.car.CarLog; 34 import com.android.car.VmsRouting; 35 import com.android.internal.annotations.GuardedBy; 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.HashSet; 41 import java.util.LinkedList; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.concurrent.CopyOnWriteArrayList; 45 46 /** 47 * This is a glue layer between the VehicleHal and the VmsService. It sends VMS properties back and 48 * forth. 49 */ 50 @FutureFeature 51 public class VmsHalService extends HalServiceBase { 52 private static final boolean DBG = true; 53 private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE; 54 private static final String TAG = "VmsHalService"; 55 private static final Set<Integer> SUPPORTED_MESSAGE_TYPES = 56 new HashSet<Integer>( 57 Arrays.asList( 58 VmsMessageType.SUBSCRIBE, 59 VmsMessageType.UNSUBSCRIBE, 60 VmsMessageType.DATA)); 61 62 private boolean mIsSupported = false; 63 private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners = 64 new CopyOnWriteArrayList<>(); 65 private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners = 66 new CopyOnWriteArrayList<>(); 67 private final VehicleHal mVehicleHal; 68 @GuardedBy("mLock") 69 private VmsRouting mRouting = new VmsRouting(); 70 private final Object mLock = new Object(); 71 72 /** 73 * The VmsPublisherService implements this interface to receive data from the HAL. 74 */ 75 public interface VmsHalPublisherListener { onChange(VmsSubscriptionState subscriptionState)76 void onChange(VmsSubscriptionState subscriptionState); 77 } 78 79 /** 80 * The VmsSubscriberService implements this interface to receive data from the HAL. 81 */ 82 public interface VmsHalSubscriberListener { onChange(VmsLayer layer, byte[] payload)83 void onChange(VmsLayer layer, byte[] payload); 84 } 85 86 /** 87 * The VmsService implements this interface to receive data from the HAL. 88 */ VmsHalService(VehicleHal vehicleHal)89 protected VmsHalService(VehicleHal vehicleHal) { 90 mVehicleHal = vehicleHal; 91 if (DBG) { 92 Log.d(TAG, "started VmsHalService!"); 93 } 94 } 95 addPublisherListener(VmsHalPublisherListener listener)96 public void addPublisherListener(VmsHalPublisherListener listener) { 97 mPublisherListeners.add(listener); 98 } 99 addSubscriberListener(VmsHalSubscriberListener listener)100 public void addSubscriberListener(VmsHalSubscriberListener listener) { 101 mSubscriberListeners.add(listener); 102 } 103 removePublisherListener(VmsHalPublisherListener listener)104 public void removePublisherListener(VmsHalPublisherListener listener) { 105 mPublisherListeners.remove(listener); 106 } 107 removeSubscriberListener(VmsHalSubscriberListener listener)108 public void removeSubscriberListener(VmsHalSubscriberListener listener) { 109 mSubscriberListeners.remove(listener); 110 } 111 addSubscription(IVmsSubscriberClient listener, VmsLayer layer)112 public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) { 113 synchronized (mLock) { 114 // Check if publishers need to be notified about this change in subscriptions. 115 boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); 116 117 // Add the listeners subscription to the layer 118 mRouting.addSubscription(listener, layer); 119 120 // Notify the publishers 121 if (firstSubscriptionForLayer) { 122 notifyPublishers(layer, true); 123 } 124 } 125 } 126 removeSubscription(IVmsSubscriberClient listener, VmsLayer layer)127 public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) { 128 synchronized (mLock) { 129 if (!mRouting.hasLayerSubscriptions(layer)) { 130 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); 131 return; 132 } 133 134 // Remove the listeners subscription to the layer 135 mRouting.removeSubscription(listener, layer); 136 137 // Check if publishers need to be notified about this change in subscriptions. 138 boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); 139 140 // Notify the publishers 141 if (!layerHasSubscribers) { 142 notifyPublishers(layer, false); 143 } 144 } 145 } 146 addSubscription(IVmsSubscriberClient listener)147 public void addSubscription(IVmsSubscriberClient listener) { 148 synchronized (mLock) { 149 mRouting.addSubscription(listener); 150 } 151 } 152 removeSubscription(IVmsSubscriberClient listener)153 public void removeSubscription(IVmsSubscriberClient listener) { 154 synchronized (mLock) { 155 mRouting.removeSubscription(listener); 156 } 157 } 158 removeDeadListener(IVmsSubscriberClient listener)159 public void removeDeadListener(IVmsSubscriberClient listener) { 160 synchronized (mLock) { 161 mRouting.removeDeadListener(listener); 162 } 163 } 164 getListeners(VmsLayer layer)165 public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) { 166 synchronized (mLock) { 167 return mRouting.getListeners(layer); 168 } 169 } 170 isHalSubscribed(VmsLayer layer)171 public boolean isHalSubscribed(VmsLayer layer) { 172 synchronized (mLock) { 173 return mRouting.isHalSubscribed(layer); 174 } 175 } 176 getSubscriptionState()177 public VmsSubscriptionState getSubscriptionState() { 178 synchronized (mLock) { 179 return mRouting.getSubscriptionState(); 180 } 181 } 182 addHalSubscription(VmsLayer layer)183 public void addHalSubscription(VmsLayer layer) { 184 synchronized (mLock) { 185 // Check if publishers need to be notified about this change in subscriptions. 186 boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); 187 188 // Add the listeners subscription to the layer 189 mRouting.addHalSubscription(layer); 190 191 if (firstSubscriptionForLayer) { 192 notifyPublishers(layer, true); 193 } 194 } 195 } 196 removeHalSubscription(VmsLayer layer)197 public void removeHalSubscription(VmsLayer layer) { 198 synchronized (mLock) { 199 if (!mRouting.hasLayerSubscriptions(layer)) { 200 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); 201 return; 202 } 203 204 // Remove the listeners subscription to the layer 205 mRouting.removeHalSubscription(layer); 206 207 // Check if publishers need to be notified about this change in subscriptions. 208 boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer); 209 210 // Notify the publishers 211 if (!layerHasSubscribers) { 212 notifyPublishers(layer, false); 213 } 214 } 215 } 216 containsListener(IVmsSubscriberClient listener)217 public boolean containsListener(IVmsSubscriberClient listener) { 218 synchronized (mLock) { 219 return mRouting.containsListener(listener); 220 } 221 } 222 223 /** 224 * Notify all the publishers and the HAL on subscription changes regardless of who triggered 225 * the change. 226 * 227 * @param layer layer which is being subscribed to or unsubscribed from. 228 * @param hasSubscribers indicates if the notification is for subscription or unsubscription. 229 */ notifyPublishers(VmsLayer layer, boolean hasSubscribers)230 public void notifyPublishers(VmsLayer layer, boolean hasSubscribers) { 231 synchronized (mLock) { 232 // notify the HAL 233 setSubscriptionRequest(layer, hasSubscribers); 234 235 // Notify the App publishers 236 for (VmsHalPublisherListener listener : mPublisherListeners) { 237 // Besides the list of layers, also a timestamp is provided to the clients. 238 // They should ignore any notification with a timestamp that is older than the most 239 // recent timestamp they have seen. 240 listener.onChange(getSubscriptionState()); 241 } 242 } 243 } 244 245 @Override init()246 public void init() { 247 if (DBG) { 248 Log.d(TAG, "init()"); 249 } 250 if (mIsSupported) { 251 mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID); 252 } 253 } 254 255 @Override release()256 public void release() { 257 if (DBG) { 258 Log.d(TAG, "release()"); 259 } 260 if (mIsSupported) { 261 mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID); 262 } 263 mPublisherListeners.clear(); 264 mSubscriberListeners.clear(); 265 } 266 267 @Override takeSupportedProperties( Collection<VehiclePropConfig> allProperties)268 public Collection<VehiclePropConfig> takeSupportedProperties( 269 Collection<VehiclePropConfig> allProperties) { 270 List<VehiclePropConfig> taken = new LinkedList<>(); 271 for (VehiclePropConfig p : allProperties) { 272 if (p.prop == HAL_PROPERTY_ID) { 273 taken.add(p); 274 mIsSupported = true; 275 if (DBG) { 276 Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop)); 277 } 278 break; 279 } 280 } 281 return taken; 282 } 283 284 @Override handleHalEvents(List<VehiclePropValue> values)285 public void handleHalEvents(List<VehiclePropValue> values) { 286 if (DBG) { 287 Log.d(TAG, "Handling a VMS property change"); 288 } 289 for (VehiclePropValue v : values) { 290 ArrayList<Integer> vec = v.value.int32Values; 291 int messageType = vec.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE); 292 int layerId = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_ID); 293 int layerVersion = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_VERSION); 294 295 // Check if message type is supported. 296 if (!SUPPORTED_MESSAGE_TYPES.contains(messageType)) { 297 throw new IllegalArgumentException("Unexpected message type. " + 298 "Expecting: " + SUPPORTED_MESSAGE_TYPES + 299 ". Got: " + messageType); 300 301 } 302 303 if (DBG) { 304 Log.d(TAG, 305 "Received message for Type: " + messageType + 306 " Layer Id: " + layerId + 307 "Version: " + layerVersion); 308 } 309 // This is a data message intended for subscribers. 310 if (messageType == VmsMessageType.DATA) { 311 // Get the payload. 312 byte[] payload = toByteArray(v.value.bytes); 313 314 // Send the message. 315 for (VmsHalSubscriberListener listener : mSubscriberListeners) { 316 listener.onChange(new VmsLayer(layerId, layerVersion), payload); 317 } 318 } else if (messageType == VmsMessageType.SUBSCRIBE) { 319 addHalSubscription(new VmsLayer(layerId, layerVersion)); 320 } else { 321 // messageType == VmsMessageType.UNSUBSCRIBE 322 removeHalSubscription(new VmsLayer(layerId, layerVersion)); 323 } 324 } 325 } 326 327 @Override dump(PrintWriter writer)328 public void dump(PrintWriter writer) { 329 writer.println(TAG); 330 writer.println("VmsProperty " + (mIsSupported ? "" : "not") + " supported."); 331 } 332 333 /** 334 * Updates the VMS HAL property with the given value. 335 * 336 * @param layer layer data to update the hal property. 337 * @param hasSubscribers if it is a subscribe or unsubscribe message. 338 * @return true if the call to the HAL to update the property was successful. 339 */ setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers)340 public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) { 341 VehiclePropValue vehiclePropertyValue = toVehiclePropValue( 342 hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer); 343 return setPropertyValue(vehiclePropertyValue); 344 } 345 setDataMessage(VmsLayer layer, byte[] payload)346 public boolean setDataMessage(VmsLayer layer, byte[] payload) { 347 VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA, 348 layer, 349 payload); 350 return setPropertyValue(vehiclePropertyValue); 351 } 352 setPropertyValue(VehiclePropValue vehiclePropertyValue)353 public boolean setPropertyValue(VehiclePropValue vehiclePropertyValue) { 354 try { 355 mVehicleHal.set(vehiclePropertyValue); 356 return true; 357 } catch (PropertyTimeoutException e) { 358 Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(HAL_PROPERTY_ID)); 359 } 360 return false; 361 } 362 363 /** Creates a {@link VehiclePropValue} */ toVehiclePropValue(int messageType, VmsLayer layer)364 private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) { 365 VehiclePropValue vehicleProp = new VehiclePropValue(); 366 vehicleProp.prop = HAL_PROPERTY_ID; 367 vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE; 368 VehiclePropValue.RawValue v = vehicleProp.value; 369 370 v.int32Values.add(messageType); 371 v.int32Values.add(layer.getId()); 372 v.int32Values.add(layer.getVersion()); 373 return vehicleProp; 374 } 375 376 /** Creates a {@link VehiclePropValue} with payload */ toVehiclePropValue(int messageType, VmsLayer layer, byte[] payload)377 private static VehiclePropValue toVehiclePropValue(int messageType, 378 VmsLayer layer, 379 byte[] payload) { 380 VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer); 381 VehiclePropValue.RawValue v = vehicleProp.value; 382 v.bytes.ensureCapacity(payload.length); 383 for (byte b : payload) { 384 v.bytes.add(b); 385 } 386 return vehicleProp; 387 } 388 }