1 /* 2 * Copyright (C) 2015 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 com.android.car.hal; 18 19 import android.os.HandlerThread; 20 import android.util.ArraySet; 21 import android.util.Log; 22 import android.util.SparseArray; 23 24 import com.android.car.CarLog; 25 import com.android.car.vehiclenetwork.VehicleNetwork; 26 import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkListener; 27 import com.android.car.vehiclenetwork.VehicleNetworkConsts; 28 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess; 29 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode; 30 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig; 31 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs; 32 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue; 33 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValues; 34 import com.android.car.vehiclenetwork.VehiclePropValueUtil; 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.io.PrintWriter; 38 import java.util.Collection; 39 import java.util.HashMap; 40 import java.util.LinkedList; 41 import java.util.List; 42 43 /** 44 * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing 45 * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase} 46 * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding 47 * Car*Service for Car*Manager API. 48 */ 49 public class VehicleHal implements VehicleNetworkListener { 50 51 private static final boolean DBG = true; 52 53 static { 54 createInstance(); 55 } 56 57 private static VehicleHal sInstance; 58 59 public static synchronized VehicleHal getInstance() { 60 if (sInstance == null) { 61 createInstance(); 62 } 63 return sInstance; 64 } 65 66 private static synchronized void createInstance() { 67 sInstance = new VehicleHal(); 68 // init is handled in a separate thread to prevent blocking the calling thread for too 69 // long. 70 sInstance.init(); 71 } 72 73 public static synchronized void releaseInstance() { 74 if (sInstance != null) { 75 sInstance.release(); 76 sInstance = null; 77 } 78 } 79 80 private final HandlerThread mHandlerThread; 81 private final VehicleNetwork mVehicleNetwork; 82 private final SensorHalService mSensorHal; 83 private final InfoHalService mInfoHal; 84 private final AudioHalService mAudioHal; 85 private final RadioHalService mRadioHal; 86 private final PowerHalService mPowerHal; 87 private final HvacHalService mHvacHal; 88 private final InputHalService mInputHal; 89 90 /** stores handler for each HAL property. Property events are sent to handler. */ 91 private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<HalServiceBase>(); 92 /** This is for iterating all HalServices with fixed order. */ 93 private final HalServiceBase[] mAllServices; 94 private final ArraySet<Integer> mSubscribedProperties = new ArraySet<Integer>(); 95 private final HashMap<Integer, VehiclePropConfig> mUnclaimedProperties = new HashMap<>(); 96 private final List<VehiclePropConfig> mAllProperties = new LinkedList<>(); 97 98 private VehicleHal() { 99 mHandlerThread = new HandlerThread("VEHICLE-HAL"); 100 mHandlerThread.start(); 101 // passing this should be safe as long as it is just kept and not used in constructor 102 mPowerHal = new PowerHalService(this); 103 mSensorHal = new SensorHalService(this); 104 mInfoHal = new InfoHalService(this); 105 mAudioHal = new AudioHalService(this); 106 mRadioHal = new RadioHalService(this); 107 mHvacHal = new HvacHalService(this); 108 mInputHal = new InputHalService(); 109 mAllServices = new HalServiceBase[] { 110 mPowerHal, 111 mAudioHal, 112 mHvacHal, 113 mInfoHal, 114 mSensorHal, 115 mRadioHal, 116 mInputHal 117 }; 118 mVehicleNetwork = VehicleNetwork.createVehicleNetwork(this, mHandlerThread.getLooper()); 119 } 120 121 /** Dummy version only for testing */ 122 @VisibleForTesting 123 public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, 124 AudioHalService audioHal, RadioHalService radioHal, HvacHalService hvacHal, 125 VehicleNetwork vehicleNetwork) { 126 mHandlerThread = null; 127 mPowerHal = powerHal; 128 mSensorHal = sensorHal; 129 mInfoHal = infoHal; 130 mAudioHal = audioHal; 131 mRadioHal = radioHal; 132 mHvacHal = hvacHal; 133 mInputHal = null; 134 mAllServices = null; 135 mVehicleNetwork = vehicleNetwork; 136 } 137 138 private void init() { 139 VehiclePropConfigs properties = mVehicleNetwork.listProperties(); 140 // needs copy as getConfigsList gives unmodifiable one. 141 List<VehiclePropConfig> propertiesList = 142 new LinkedList<VehiclePropConfig>(properties.getConfigsList()); 143 for (HalServiceBase service: mAllServices) { 144 List<VehiclePropConfig> taken = service.takeSupportedProperties(propertiesList); 145 if (taken == null) { 146 continue; 147 } 148 if (DBG) { 149 Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size()); 150 } 151 synchronized (this) { 152 for (VehiclePropConfig p: taken) { 153 mPropertyHandlers.append(p.getProp(), service); 154 } 155 } 156 propertiesList.removeAll(taken); 157 service.init(); 158 } 159 synchronized (this) { 160 for (VehiclePropConfig p: propertiesList) { 161 mUnclaimedProperties.put(p.getProp(), p); 162 } 163 mAllProperties.addAll(properties.getConfigsList()); 164 } 165 } 166 167 private void release() { 168 // release in reverse order from init 169 for (int i = mAllServices.length - 1; i >= 0; i--) { 170 mAllServices[i].release(); 171 } 172 synchronized (this) { 173 for (int p : mSubscribedProperties) { 174 mVehicleNetwork.unsubscribe(p); 175 } 176 mSubscribedProperties.clear(); 177 mUnclaimedProperties.clear(); 178 mAllProperties.clear(); 179 } 180 // keep the looper thread as should be kept for the whole life cycle. 181 } 182 183 public void startMocking() { 184 reinitHals(); 185 } 186 187 public void stopMocking() { 188 reinitHals(); 189 } 190 191 private void reinitHals() { 192 release(); 193 init(); 194 } 195 196 public SensorHalService getSensorHal() { 197 return mSensorHal; 198 } 199 200 public InfoHalService getInfoHal() { 201 return mInfoHal; 202 } 203 204 public AudioHalService getAudioHal() { 205 return mAudioHal; 206 } 207 208 public RadioHalService getRadioHal() { 209 return mRadioHal; 210 } 211 212 public PowerHalService getPowerHal() { 213 return mPowerHal; 214 } 215 216 public HvacHalService getHvacHal() { 217 return mHvacHal; 218 } 219 220 public InputHalService getInputHal() { 221 return mInputHal; 222 } 223 224 private void assertServiceOwnerLocked(HalServiceBase service, int property) { 225 if (service != mPropertyHandlers.get(property)) { 226 throw new IllegalArgumentException("not owned"); 227 } 228 } 229 230 /** 231 * Subscribe given property. Only Hal service owning the property can subscribe it. 232 * @param service 233 * @param property 234 * @param samplingRateHz 235 */ 236 public void subscribeProperty(HalServiceBase service, int property, 237 float samplingRateHz) throws IllegalArgumentException { 238 synchronized (this) { 239 assertServiceOwnerLocked(service, property); 240 mSubscribedProperties.add(property); 241 } 242 mVehicleNetwork.subscribe(property, samplingRateHz); 243 } 244 245 public void unsubscribeProperty(HalServiceBase service, int property) { 246 synchronized (this) { 247 assertServiceOwnerLocked(service, property); 248 mSubscribedProperties.remove(property); 249 } 250 mVehicleNetwork.unsubscribe(property); 251 } 252 253 public VehicleNetwork getVehicleNetwork() { 254 return mVehicleNetwork; 255 } 256 257 public static boolean isPropertySubscribable(VehiclePropConfig config) { 258 if (config.hasAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ == 0 || 259 config.getChangeMode() == 260 VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC) { 261 return false; 262 } 263 return true; 264 } 265 266 public static void dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs) { 267 for (VehiclePropConfig config : configs) { 268 writer.println("property " + 269 VehicleNetworkConsts.getVehiclePropertyName(config.getProp())); 270 } 271 } 272 273 private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<HalServiceBase>(); 274 275 @Override 276 public void onVehicleNetworkEvents(VehiclePropValues values) { 277 synchronized (this) { 278 for (VehiclePropValue v : values.getValuesList()) { 279 HalServiceBase service = mPropertyHandlers.get(v.getProp()); 280 service.getDispatchList().add(v); 281 mServicesToDispatch.add(service); 282 } 283 } 284 for (HalServiceBase s : mServicesToDispatch) { 285 s.handleHalEvents(s.getDispatchList()); 286 s.getDispatchList().clear(); 287 } 288 mServicesToDispatch.clear(); 289 } 290 291 @Override 292 public void onHalError(int errorCode, int property, int operation) { 293 Log.e(CarLog.TAG_HAL, "onHalError, errorCode:" + errorCode + 294 " property:0x" + Integer.toHexString(property) + 295 " operation:" + operation); 296 // TODO propagate per property error to HAL services and handle global error 297 } 298 299 @Override 300 public void onHalRestart(boolean inMocking) { 301 Log.e(CarLog.TAG_HAL, "onHalRestart, inMocking:" + inMocking); 302 // TODO restart things as other components started mocking. For now, ignore. 303 } 304 305 public void dump(PrintWriter writer) { 306 writer.println("**dump HAL services**"); 307 for (HalServiceBase service: mAllServices) { 308 service.dump(writer); 309 } 310 writer.println("**All properties**"); 311 for (VehiclePropConfig config : mAllProperties) { 312 StringBuilder builder = new StringBuilder(); 313 builder.append("Property:" + Integer.toHexString(config.getProp())); 314 builder.append(",access:" + Integer.toHexString(config.getAccess())); 315 builder.append(",changeMode:" + Integer.toHexString(config.getChangeMode())); 316 builder.append(",valueType:" + Integer.toHexString(config.getValueType())); 317 builder.append(",permission:" + Integer.toHexString(config.getPermissionModel())); 318 builder.append(",config:" + Integer.toHexString(config.getConfigArray(0))); 319 builder.append(",fs min:" + config.getSampleRateMin()); 320 builder.append(",fs max:" + config.getSampleRateMax()); 321 for (int i = 0; i < config.getFloatMaxsCount(); i++) { 322 builder.append(",v min:" + config.getFloatMins(i)); 323 builder.append(",v max:" + config.getFloatMaxs(i)); 324 } 325 for (int i = 0; i < config.getInt32MaxsCount(); i++) { 326 builder.append(",v min:" + config.getInt32Mins(i)); 327 builder.append(",v max:" + config.getInt32Maxs(i)); 328 } 329 for (int i = 0; i < config.getInt64MaxsCount(); i++) { 330 builder.append(",v min:" + config.getInt64Mins(i)); 331 builder.append(",v max:" + config.getInt64Maxs(i)); 332 } 333 writer.println(builder.toString()); 334 } 335 } 336 } 337