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 */ 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 */ 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 */ 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 */ 104 void onPropertySetError(int property, int area); 105 } 106 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 */ 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 */ 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 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 */ 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 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 181 public String getWritePermission(int propId) { 182 return mPropIds.getWritePermission(propId); 183 } 184 185 /** 186 * Set the property value. 187 * @param prop 188 */ 189 public void setProperty(CarPropertyValue prop) { 190 int halPropId = managerToHalPropId(prop.getPropertyId()); 191 if (halPropId == NOT_SUPPORTED_PROPERTY) { 192 throw new IllegalArgumentException("Invalid property Id : 0x" 193 + toHexString(prop.getPropertyId())); 194 } 195 VehiclePropValue halProp = toVehiclePropValue(prop, halPropId); 196 try { 197 mVehicleHal.set(halProp); 198 } catch (PropertyTimeoutException e) { 199 Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e); 200 throw new RuntimeException(e); 201 } 202 } 203 204 /** 205 * Subscribe to this property at the specified update rate. 206 * @param propId 207 * @param rate 208 */ 209 public void subscribeProperty(int propId, float rate) { 210 if (mDbg) { 211 Log.d(TAG, "subscribeProperty propId=0x" + toHexString(propId) + ", rate=" + rate); 212 } 213 int halPropId = managerToHalPropId(propId); 214 if (halPropId == NOT_SUPPORTED_PROPERTY) { 215 throw new IllegalArgumentException("Invalid property Id : 0x" 216 + toHexString(propId)); 217 } 218 // Validate the min/max rate 219 CarPropertyConfig cfg = mProps.get(propId); 220 if (rate > cfg.getMaxSampleRate()) { 221 rate = cfg.getMaxSampleRate(); 222 } else if (rate < cfg.getMinSampleRate()) { 223 rate = cfg.getMinSampleRate(); 224 } 225 synchronized (mSubscribedPropIds) { 226 mSubscribedPropIds.add(halPropId); 227 } 228 mVehicleHal.subscribeProperty(this, halPropId, rate); 229 } 230 231 /** 232 * Unsubscribe the property and turn off update events for it. 233 * @param propId 234 */ 235 public void unsubscribeProperty(int propId) { 236 if (mDbg) { 237 Log.d(TAG, "unsubscribeProperty propId=0x" + toHexString(propId)); 238 } 239 int halPropId = managerToHalPropId(propId); 240 if (halPropId == NOT_SUPPORTED_PROPERTY) { 241 throw new IllegalArgumentException("Invalid property Id : 0x" 242 + toHexString(propId)); 243 } 244 synchronized (mSubscribedPropIds) { 245 if (mSubscribedPropIds.contains(halPropId)) { 246 mSubscribedPropIds.remove(halPropId); 247 mVehicleHal.unsubscribeProperty(this, halPropId); 248 } 249 } 250 } 251 252 @Override 253 public void init() { 254 if (mDbg) { 255 Log.d(TAG, "init()"); 256 } 257 } 258 259 @Override 260 public void release() { 261 if (mDbg) { 262 Log.d(TAG, "release()"); 263 } 264 synchronized (mSubscribedPropIds) { 265 for (Integer prop : mSubscribedPropIds) { 266 mVehicleHal.unsubscribeProperty(this, prop); 267 } 268 mSubscribedPropIds.clear(); 269 } 270 mProps.clear(); 271 272 synchronized (mLock) { 273 mListener = null; 274 } 275 } 276 277 @Override 278 public Collection<VehiclePropConfig> takeSupportedProperties( 279 Collection<VehiclePropConfig> allProperties) { 280 List<VehiclePropConfig> taken = new LinkedList<>(); 281 282 for (VehiclePropConfig p : allProperties) { 283 if (mPropIds.isSupportedProperty(p.prop)) { 284 CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, p.prop); 285 taken.add(p); 286 mProps.put(p.prop, config); 287 if (mDbg) { 288 Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop)); 289 } 290 } 291 } 292 if (mDbg) { 293 Log.d(TAG, "takeSupportedProperties() took " + taken.size() + " properties"); 294 } 295 return taken; 296 } 297 298 @Override 299 public void handleHalEvents(List<VehiclePropValue> values) { 300 PropertyHalListener listener; 301 synchronized (mLock) { 302 listener = mListener; 303 } 304 if (listener != null) { 305 for (VehiclePropValue v : values) { 306 int mgrPropId = halToManagerPropId(v.prop); 307 if (mgrPropId == NOT_SUPPORTED_PROPERTY) { 308 Log.e(TAG, "Property is not supported: 0x" + toHexString(v.prop)); 309 continue; 310 } 311 CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId); 312 CarPropertyEvent event = new CarPropertyEvent( 313 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal); 314 if (event != null) { 315 mEventsToDispatch.add(event); 316 } 317 } 318 listener.onPropertyChange(mEventsToDispatch); 319 mEventsToDispatch.clear(); 320 } 321 } 322 323 @Override 324 public void handlePropertySetError(int property, int area) { 325 PropertyHalListener listener; 326 synchronized (mLock) { 327 listener = mListener; 328 } 329 if (listener != null) { 330 listener.onPropertySetError(property, area); 331 } 332 } 333 334 @Override 335 public void dump(PrintWriter writer) { 336 writer.println(TAG); 337 writer.println(" Properties available:"); 338 for (CarPropertyConfig prop : mProps.values()) { 339 writer.println(" " + prop.toString()); 340 } 341 } 342 } 343