1 /* 2 * Copyright (C) 2018 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.hal.CarPropertyUtils.toCarPropertyValue; 19 import static com.android.car.hal.CarPropertyUtils.toVehiclePropValue; 20 21 import static java.lang.Integer.toHexString; 22 23 import android.annotation.Nullable; 24 import android.car.hardware.CarPropertyConfig; 25 import android.car.hardware.CarPropertyValue; 26 import android.car.hardware.property.CarPropertyEvent; 27 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 28 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import com.android.car.CarLog; 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.io.PrintWriter; 36 import java.util.Collection; 37 import java.util.HashSet; 38 import java.util.LinkedList; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.concurrent.ConcurrentHashMap; 43 44 /** 45 * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty. 46 * Services that communicate by passing vehicle properties back and forth via ICarProperty should 47 * extend this class. 48 */ 49 public class PropertyHalService extends HalServiceBase { 50 private final boolean mDbg = true; 51 private final LinkedList<CarPropertyEvent> mEventsToDispatch = new LinkedList<>(); 52 private final Map<Integer, CarPropertyConfig<?>> mProps = 53 new ConcurrentHashMap<>(); 54 private final SparseArray<Float> mRates = new SparseArray<Float>(); 55 private static final String TAG = "PropertyHalService"; 56 private final VehicleHal mVehicleHal; 57 private final PropertyHalServiceIds mPropIds; 58 59 @GuardedBy("mLock") 60 private PropertyHalListener mListener; 61 62 private Set<Integer> mSubscribedPropIds; 63 64 private final Object mLock = new Object(); 65 66 /** 67 * Converts manager property ID to Vehicle HAL property ID. 68 * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}. 69 */ managerToHalPropId(int propId)70 private int managerToHalPropId(int propId) { 71 if (mProps.containsKey(propId)) { 72 return propId; 73 } else { 74 return NOT_SUPPORTED_PROPERTY; 75 } 76 } 77 78 /** 79 * Converts Vehicle HAL property ID to manager property ID. 80 * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}. 81 */ halToManagerPropId(int halPropId)82 private int halToManagerPropId(int halPropId) { 83 if (mProps.containsKey(halPropId)) { 84 return halPropId; 85 } else { 86 return NOT_SUPPORTED_PROPERTY; 87 } 88 } 89 90 /** 91 * PropertyHalListener used to send events to CarPropertyService 92 */ 93 public interface PropertyHalListener { 94 /** 95 * This event is sent whenever the property value is updated 96 * @param event 97 */ onPropertyChange(List<CarPropertyEvent> events)98 void onPropertyChange(List<CarPropertyEvent> events); 99 /** 100 * This event is sent when the set property call fails 101 * @param property 102 * @param area 103 */ onPropertySetError(int property, int area)104 void onPropertySetError(int property, int area); 105 } 106 PropertyHalService(VehicleHal vehicleHal)107 public PropertyHalService(VehicleHal vehicleHal) { 108 mPropIds = new PropertyHalServiceIds(); 109 mSubscribedPropIds = new HashSet<Integer>(); 110 mVehicleHal = vehicleHal; 111 if (mDbg) { 112 Log.d(TAG, "started PropertyHalService"); 113 } 114 } 115 116 /** 117 * Set the listener for the HAL service 118 * @param listener 119 */ setListener(PropertyHalListener listener)120 public void setListener(PropertyHalListener listener) { 121 synchronized (mLock) { 122 mListener = listener; 123 } 124 } 125 126 /** 127 * 128 * @return List<CarPropertyConfig> List of configs available. 129 */ getPropertyList()130 public Map<Integer, CarPropertyConfig<?>> getPropertyList() { 131 if (mDbg) { 132 Log.d(TAG, "getPropertyList"); 133 } 134 return mProps; 135 } 136 137 /** 138 * Returns property or null if property is not ready yet. 139 * @param mgrPropId 140 * @param areaId 141 */ 142 @Nullable getProperty(int mgrPropId, int areaId)143 public CarPropertyValue getProperty(int mgrPropId, int areaId) { 144 int halPropId = managerToHalPropId(mgrPropId); 145 if (halPropId == NOT_SUPPORTED_PROPERTY) { 146 throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId)); 147 } 148 149 VehiclePropValue value = null; 150 try { 151 value = mVehicleHal.get(halPropId, areaId); 152 } catch (PropertyTimeoutException e) { 153 Log.e(CarLog.TAG_PROPERTY, "get, property not ready 0x" + toHexString(halPropId), e); 154 } 155 156 return value == null ? null : toCarPropertyValue(value, mgrPropId); 157 } 158 159 /** 160 * Returns sample rate for the property 161 * @param propId 162 */ getSampleRate(int propId)163 public float getSampleRate(int propId) { 164 return mVehicleHal.getSampleRate(propId); 165 } 166 167 /** 168 * Get the read permission string for the property. 169 * @param propId 170 */ 171 @Nullable getReadPermission(int propId)172 public String getReadPermission(int propId) { 173 return mPropIds.getReadPermission(propId); 174 } 175 176 /** 177 * Get the write permission string for the property. 178 * @param propId 179 */ 180 @Nullable getWritePermission(int propId)181 public String getWritePermission(int propId) { 182 return mPropIds.getWritePermission(propId); 183 } 184 185 /** 186 * Return true if property is a display_units property 187 * @param propId 188 */ isDisplayUnitsProperty(int propId)189 public boolean isDisplayUnitsProperty(int propId) { 190 return mPropIds.isPropertyToChangeUnits(propId); 191 } 192 193 /** 194 * Set the property value. 195 * @param prop 196 */ setProperty(CarPropertyValue prop)197 public void setProperty(CarPropertyValue prop) { 198 int halPropId = managerToHalPropId(prop.getPropertyId()); 199 if (halPropId == NOT_SUPPORTED_PROPERTY) { 200 throw new IllegalArgumentException("Invalid property Id : 0x" 201 + toHexString(prop.getPropertyId())); 202 } 203 VehiclePropValue halProp = toVehiclePropValue(prop, halPropId); 204 try { 205 mVehicleHal.set(halProp); 206 } catch (PropertyTimeoutException e) { 207 Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e); 208 throw new RuntimeException(e); 209 } 210 } 211 212 /** 213 * Subscribe to this property at the specified update rate. 214 * @param propId 215 * @param rate 216 */ subscribeProperty(int propId, float rate)217 public void subscribeProperty(int propId, float rate) { 218 if (mDbg) { 219 Log.d(TAG, "subscribeProperty propId=0x" + toHexString(propId) + ", rate=" + rate); 220 } 221 int halPropId = managerToHalPropId(propId); 222 if (halPropId == NOT_SUPPORTED_PROPERTY) { 223 throw new IllegalArgumentException("Invalid property Id : 0x" 224 + toHexString(propId)); 225 } 226 // Validate the min/max rate 227 CarPropertyConfig cfg = mProps.get(propId); 228 if (rate > cfg.getMaxSampleRate()) { 229 rate = cfg.getMaxSampleRate(); 230 } else if (rate < cfg.getMinSampleRate()) { 231 rate = cfg.getMinSampleRate(); 232 } 233 synchronized (mSubscribedPropIds) { 234 mSubscribedPropIds.add(halPropId); 235 } 236 mVehicleHal.subscribeProperty(this, halPropId, rate); 237 } 238 239 /** 240 * Unsubscribe the property and turn off update events for it. 241 * @param propId 242 */ unsubscribeProperty(int propId)243 public void unsubscribeProperty(int propId) { 244 if (mDbg) { 245 Log.d(TAG, "unsubscribeProperty propId=0x" + toHexString(propId)); 246 } 247 int halPropId = managerToHalPropId(propId); 248 if (halPropId == NOT_SUPPORTED_PROPERTY) { 249 throw new IllegalArgumentException("Invalid property Id : 0x" 250 + toHexString(propId)); 251 } 252 synchronized (mSubscribedPropIds) { 253 if (mSubscribedPropIds.contains(halPropId)) { 254 mSubscribedPropIds.remove(halPropId); 255 mVehicleHal.unsubscribeProperty(this, halPropId); 256 } 257 } 258 } 259 260 @Override init()261 public void init() { 262 if (mDbg) { 263 Log.d(TAG, "init()"); 264 } 265 } 266 267 @Override release()268 public void release() { 269 if (mDbg) { 270 Log.d(TAG, "release()"); 271 } 272 synchronized (mSubscribedPropIds) { 273 for (Integer prop : mSubscribedPropIds) { 274 mVehicleHal.unsubscribeProperty(this, prop); 275 } 276 mSubscribedPropIds.clear(); 277 } 278 mProps.clear(); 279 280 synchronized (mLock) { 281 mListener = null; 282 } 283 } 284 285 @Override takeSupportedProperties( Collection<VehiclePropConfig> allProperties)286 public Collection<VehiclePropConfig> takeSupportedProperties( 287 Collection<VehiclePropConfig> allProperties) { 288 List<VehiclePropConfig> taken = new LinkedList<>(); 289 290 for (VehiclePropConfig p : allProperties) { 291 if (mPropIds.isSupportedProperty(p.prop)) { 292 CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, p.prop); 293 taken.add(p); 294 mProps.put(p.prop, config); 295 if (mDbg) { 296 Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop)); 297 } 298 } 299 } 300 if (mDbg) { 301 Log.d(TAG, "takeSupportedProperties() took " + taken.size() + " properties"); 302 } 303 return taken; 304 } 305 306 @Override handleHalEvents(List<VehiclePropValue> values)307 public void handleHalEvents(List<VehiclePropValue> values) { 308 PropertyHalListener listener; 309 synchronized (mLock) { 310 listener = mListener; 311 } 312 if (listener != null) { 313 for (VehiclePropValue v : values) { 314 if (v == null) { 315 continue; 316 } 317 int mgrPropId = halToManagerPropId(v.prop); 318 if (mgrPropId == NOT_SUPPORTED_PROPERTY) { 319 Log.e(TAG, "Property is not supported: 0x" + toHexString(v.prop)); 320 continue; 321 } 322 CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId); 323 CarPropertyEvent event = new CarPropertyEvent( 324 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal); 325 if (event != null) { 326 mEventsToDispatch.add(event); 327 } 328 } 329 listener.onPropertyChange(mEventsToDispatch); 330 mEventsToDispatch.clear(); 331 } 332 } 333 334 @Override handlePropertySetError(int property, int area)335 public void handlePropertySetError(int property, int area) { 336 PropertyHalListener listener; 337 synchronized (mLock) { 338 listener = mListener; 339 } 340 if (listener != null) { 341 listener.onPropertySetError(property, area); 342 } 343 } 344 345 @Override dump(PrintWriter writer)346 public void dump(PrintWriter writer) { 347 writer.println(TAG); 348 writer.println(" Properties available:"); 349 for (CarPropertyConfig prop : mProps.values()) { 350 writer.println(" " + prop.toString()); 351 } 352 } 353 } 354