keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 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 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 19 | import static com.android.car.CarServiceUtils.toByteArray; |
| 20 | import static com.android.car.CarServiceUtils.toFloatArray; |
| 21 | import static com.android.car.CarServiceUtils.toIntArray; |
| 22 | import static java.lang.Integer.toHexString; |
| 23 | |
| 24 | import android.annotation.CheckResult; |
| 25 | import android.hardware.vehicle.V2_0.IVehicle; |
| 26 | import android.hardware.vehicle.V2_0.IVehicleCallback; |
| 27 | import android.hardware.vehicle.V2_0.VehicleAreaConfig; |
| 28 | import android.hardware.vehicle.V2_0.VehiclePropConfig; |
| 29 | import android.hardware.vehicle.V2_0.VehiclePropValue; |
| 30 | import android.hardware.vehicle.V2_0.VehiclePropertyAccess; |
| 31 | import android.hardware.vehicle.V2_0.VehiclePropertyChangeMode; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 32 | import android.os.HandlerThread; |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 33 | import android.os.SystemClock; |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 34 | import android.util.ArraySet; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 35 | import android.util.Log; |
| 36 | import android.util.SparseArray; |
| 37 | |
| 38 | import com.android.car.CarLog; |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 39 | import com.android.internal.annotations.VisibleForTesting; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 40 | |
| 41 | import java.io.PrintWriter; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 42 | import java.lang.ref.WeakReference; |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 43 | import java.util.ArrayList; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 44 | import java.util.Arrays; |
keunyoung | a74b9ca | 2015-10-21 13:33:58 -0700 | [diff] [blame] | 45 | import java.util.Collection; |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 46 | import java.util.HashMap; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 47 | import java.util.HashSet; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 48 | import java.util.List; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 49 | import java.util.Set; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 50 | |
| 51 | /** |
| 52 | * Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing |
| 53 | * of received data (type check). Then each event is sent to corresponding {@link HalServiceBase} |
| 54 | * implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding |
| 55 | * Car*Service for Car*Manager API. |
| 56 | */ |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 57 | public class VehicleHal extends IVehicleCallback.Stub { |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 58 | |
Vitalii Tomkiv | 1b1247b | 2016-09-30 11:27:19 -0700 | [diff] [blame] | 59 | private static final boolean DBG = false; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 60 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 61 | private static final int NO_AREA = -1; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 62 | |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 63 | private final HandlerThread mHandlerThread; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 64 | private final SensorHalService mSensorHal; |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 65 | private final InfoHalService mInfoHal; |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 66 | private final AudioHalService mAudioHal; |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 67 | private final CabinHalService mCabinHal; |
Sanket Agarwal | 3cf096a | 2015-10-13 14:46:31 -0700 | [diff] [blame] | 68 | private final RadioHalService mRadioHal; |
keunyoung | 4b0212c | 2015-10-29 17:11:57 -0700 | [diff] [blame] | 69 | private final PowerHalService mPowerHal; |
Steve Paik | 6648198 | 2015-10-27 15:22:38 -0700 | [diff] [blame] | 70 | private final HvacHalService mHvacHal; |
Keun-young Park | a28d7b2 | 2016-02-29 16:54:29 -0800 | [diff] [blame] | 71 | private final InputHalService mInputHal; |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 72 | private final VendorExtensionHalService mVendorExtensionHal; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 73 | private final HalClient mHalClient; |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 74 | |
| 75 | /** stores handler for each HAL property. Property events are sent to handler. */ |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 76 | private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<>(); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 77 | /** This is for iterating all HalServices with fixed order. */ |
| 78 | private final HalServiceBase[] mAllServices; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 79 | private final ArraySet<Integer> mSubscribedProperties = new ArraySet<>(); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 80 | private final HashMap<Integer, VehiclePropConfig> mAllProperties = new HashMap<>(); |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 81 | private final HashMap<Integer, VehiclePropertyEventInfo> mEventLog = new HashMap<>(); |
| 82 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 83 | public VehicleHal(IVehicle vehicle) { |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 84 | mHandlerThread = new HandlerThread("VEHICLE-HAL"); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 85 | mHandlerThread.start(); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 86 | // passing this should be safe as long as it is just kept and not used in constructor |
keunyoung | 4b0212c | 2015-10-29 17:11:57 -0700 | [diff] [blame] | 87 | mPowerHal = new PowerHalService(this); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 88 | mSensorHal = new SensorHalService(this); |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 89 | mInfoHal = new InfoHalService(this); |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 90 | mAudioHal = new AudioHalService(this); |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 91 | mCabinHal = new CabinHalService(this); |
Sanket Agarwal | 3cf096a | 2015-10-13 14:46:31 -0700 | [diff] [blame] | 92 | mRadioHal = new RadioHalService(this); |
Steve Paik | 6648198 | 2015-10-27 15:22:38 -0700 | [diff] [blame] | 93 | mHvacHal = new HvacHalService(this); |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 94 | mInputHal = new InputHalService(this); |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 95 | mVendorExtensionHal = new VendorExtensionHalService(this); |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 96 | mAllServices = new HalServiceBase[] { |
keunyoung | 4b0212c | 2015-10-29 17:11:57 -0700 | [diff] [blame] | 97 | mPowerHal, |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 98 | mAudioHal, |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 99 | mCabinHal, |
Steve Paik | 6648198 | 2015-10-27 15:22:38 -0700 | [diff] [blame] | 100 | mHvacHal, |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 101 | mInfoHal, |
Sanket Agarwal | 3cf096a | 2015-10-13 14:46:31 -0700 | [diff] [blame] | 102 | mSensorHal, |
Keun-young Park | a28d7b2 | 2016-02-29 16:54:29 -0800 | [diff] [blame] | 103 | mRadioHal, |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 104 | mInputHal, |
| 105 | mVendorExtensionHal |
Keun-young Park | a28d7b2 | 2016-02-29 16:54:29 -0800 | [diff] [blame] | 106 | }; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 107 | |
| 108 | mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 111 | /** Dummy version only for testing */ |
| 112 | @VisibleForTesting |
| 113 | public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 114 | AudioHalService audioHal, CabinHalService cabinHal, RadioHalService radioHal, |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 115 | HvacHalService hvacHal, HalClient halClient) { |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 116 | mHandlerThread = null; |
| 117 | mPowerHal = powerHal; |
| 118 | mSensorHal = sensorHal; |
| 119 | mInfoHal = infoHal; |
| 120 | mAudioHal = audioHal; |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 121 | mCabinHal = cabinHal; |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 122 | mRadioHal = radioHal; |
| 123 | mHvacHal = hvacHal; |
Keun-young Park | a28d7b2 | 2016-02-29 16:54:29 -0800 | [diff] [blame] | 124 | mInputHal = null; |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 125 | mVendorExtensionHal = null; |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 126 | mAllServices = null; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 127 | mHalClient = halClient; |
Keun-young Park | fd3fbf7 | 2016-01-22 17:00:17 -0800 | [diff] [blame] | 128 | } |
| 129 | |
Keun-young Park | 021310d | 2016-04-25 21:09:39 -0700 | [diff] [blame] | 130 | public void init() { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 131 | Set<VehiclePropConfig> properties = new HashSet<>(mHalClient.getAllPropConfigs()); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 132 | |
| 133 | synchronized (this) { |
| 134 | // Create map of all properties |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 135 | for (VehiclePropConfig p : properties) { |
| 136 | mAllProperties.put(p.prop, p); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 137 | } |
| 138 | } |
| 139 | |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 140 | for (HalServiceBase service: mAllServices) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 141 | Collection<VehiclePropConfig> taken = service.takeSupportedProperties(properties); |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 142 | if (taken == null) { |
| 143 | continue; |
| 144 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 145 | if (DBG) { |
| 146 | Log.i(CarLog.TAG_HAL, "HalService " + service + " take properties " + taken.size()); |
| 147 | } |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 148 | synchronized (this) { |
| 149 | for (VehiclePropConfig p: taken) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 150 | mPropertyHandlers.append(p.prop, service); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 151 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 152 | } |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 153 | properties.removeAll(taken); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 154 | service.init(); |
| 155 | } |
| 156 | } |
| 157 | |
Keun-young Park | 021310d | 2016-04-25 21:09:39 -0700 | [diff] [blame] | 158 | public void release() { |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 159 | // release in reverse order from init |
| 160 | for (int i = mAllServices.length - 1; i >= 0; i--) { |
| 161 | mAllServices[i].release(); |
| 162 | } |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 163 | synchronized (this) { |
| 164 | for (int p : mSubscribedProperties) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 165 | mHalClient.unsubscribe(p); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 166 | } |
| 167 | mSubscribedProperties.clear(); |
Keun-young Park | 450faba | 2016-02-10 18:15:12 -0800 | [diff] [blame] | 168 | mAllProperties.clear(); |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 169 | } |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 170 | // keep the looper thread as should be kept for the whole life cycle. |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | public SensorHalService getSensorHal() { |
| 174 | return mSensorHal; |
| 175 | } |
| 176 | |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 177 | public InfoHalService getInfoHal() { |
| 178 | return mInfoHal; |
| 179 | } |
| 180 | |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 181 | public AudioHalService getAudioHal() { |
| 182 | return mAudioHal; |
| 183 | } |
| 184 | |
Steve Paik | 43c04a7 | 2016-07-08 19:12:09 -0700 | [diff] [blame] | 185 | public CabinHalService getCabinHal() { |
| 186 | return mCabinHal; |
| 187 | } |
| 188 | |
Sanket Agarwal | 3cf096a | 2015-10-13 14:46:31 -0700 | [diff] [blame] | 189 | public RadioHalService getRadioHal() { |
| 190 | return mRadioHal; |
| 191 | } |
| 192 | |
keunyoung | 4b0212c | 2015-10-29 17:11:57 -0700 | [diff] [blame] | 193 | public PowerHalService getPowerHal() { |
| 194 | return mPowerHal; |
| 195 | } |
| 196 | |
Steve Paik | 6648198 | 2015-10-27 15:22:38 -0700 | [diff] [blame] | 197 | public HvacHalService getHvacHal() { |
| 198 | return mHvacHal; |
| 199 | } |
| 200 | |
Keun-young Park | a28d7b2 | 2016-02-29 16:54:29 -0800 | [diff] [blame] | 201 | public InputHalService getInputHal() { |
| 202 | return mInputHal; |
| 203 | } |
| 204 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 205 | public VendorExtensionHalService getVendorExtensionHal() { |
| 206 | return mVendorExtensionHal; |
| 207 | } |
| 208 | |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 209 | private void assertServiceOwnerLocked(HalServiceBase service, int property) { |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 210 | if (service != mPropertyHandlers.get(property)) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 211 | throw new IllegalArgumentException("Property 0x" + toHexString(property) |
Pavel Maltsev | 1bfbaef | 2016-07-25 14:23:51 -0700 | [diff] [blame] | 212 | + " is not owned by service: " + service); |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 213 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 214 | } |
| 215 | |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 216 | /** |
| 217 | * Subscribe given property. Only Hal service owning the property can subscribe it. |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 218 | */ |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 219 | public void subscribeProperty(HalServiceBase service, int property, |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 220 | float samplingRateHz) throws IllegalArgumentException { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 221 | if (DBG) { |
| 222 | Log.i(CarLog.TAG_HAL, "subscribeProperty, service:" + service |
| 223 | + ", property: 0x" + toHexString(property)); |
| 224 | } |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 225 | VehiclePropConfig config; |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 226 | synchronized (this) { |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 227 | config = mAllProperties.get(property); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 228 | } |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 229 | |
| 230 | if (config == null) { |
| 231 | throw new IllegalArgumentException("subscribe error: config is null for property " + |
| 232 | property); |
| 233 | } else if (isPropertySubscribable(config)) { |
| 234 | synchronized (this) { |
| 235 | assertServiceOwnerLocked(service, property); |
| 236 | mSubscribedProperties.add(property); |
| 237 | } |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 238 | mHalClient.subscribe(property, samplingRateHz); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 239 | } else { |
| 240 | Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property); |
| 241 | } |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 242 | } |
| 243 | |
keunyoung | d32f4e6 | 2015-09-21 11:33:06 -0700 | [diff] [blame] | 244 | public void unsubscribeProperty(HalServiceBase service, int property) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 245 | if (DBG) { |
| 246 | Log.i(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service |
| 247 | + ", property: 0x" + toHexString(property)); |
| 248 | } |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 249 | VehiclePropConfig config; |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 250 | synchronized (this) { |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 251 | config = mAllProperties.get(property); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 252 | } |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 253 | |
| 254 | if (config == null) { |
| 255 | Log.e(CarLog.TAG_HAL, "unsubscribeProperty: property " + property + " does not exist"); |
| 256 | } else if (isPropertySubscribable(config)) { |
| 257 | synchronized (this) { |
| 258 | assertServiceOwnerLocked(service, property); |
| 259 | mSubscribedProperties.remove(property); |
| 260 | } |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 261 | mHalClient.unsubscribe(property); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 262 | } else { |
| 263 | Log.e(CarLog.TAG_HAL, "Cannot unsubscribe property: " + property); |
| 264 | } |
keunyoung | a3b28d8 | 2015-08-25 13:05:15 -0700 | [diff] [blame] | 265 | } |
| 266 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 267 | public boolean isPropertySupported(int propertyId) { |
| 268 | return mAllProperties.containsKey(propertyId); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 269 | } |
| 270 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 271 | public Collection<VehiclePropConfig> getAllPropConfigs() { |
| 272 | return mAllProperties.values(); |
| 273 | } |
| 274 | |
| 275 | public VehiclePropValue get(int propertyId) throws PropertyTimeoutException { |
| 276 | return get(propertyId, NO_AREA); |
| 277 | } |
| 278 | |
| 279 | public VehiclePropValue get(int propertyId, int areaId) throws PropertyTimeoutException { |
| 280 | Log.i(CarLog.TAG_HAL, "get, property: 0x" + toHexString(propertyId) |
| 281 | + ", areaId: 0x" + toHexString(areaId)); |
| 282 | VehiclePropValue propValue = new VehiclePropValue(); |
| 283 | propValue.prop = propertyId; |
| 284 | propValue.areaId = areaId; |
| 285 | return mHalClient.getValue(propValue); |
| 286 | } |
| 287 | |
| 288 | public <T> T get(Class clazz, int propertyId) throws PropertyTimeoutException { |
| 289 | return get(clazz, createPropValue(propertyId, NO_AREA)); |
| 290 | } |
| 291 | |
| 292 | public <T> T get(Class clazz, int propertyId, int areaId) throws PropertyTimeoutException { |
| 293 | return get(clazz, createPropValue(propertyId, areaId)); |
| 294 | } |
| 295 | |
| 296 | @SuppressWarnings("unchecked") |
| 297 | public <T> T get(Class clazz, VehiclePropValue requestedPropValue) |
| 298 | throws PropertyTimeoutException { |
| 299 | VehiclePropValue propValue = mHalClient.getValue(requestedPropValue); |
| 300 | if (clazz == Integer.class || clazz == int.class) { |
| 301 | return (T) propValue.value.int32Values.get(0); |
| 302 | } else if (clazz == Boolean.class || clazz == boolean.class) { |
| 303 | return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1); |
| 304 | } else if (clazz == Float.class || clazz == float.class) { |
| 305 | return (T) propValue.value.floatValues.get(0); |
| 306 | } else if (clazz == Integer[].class) { |
| 307 | Integer[] intArray = new Integer[propValue.value.int32Values.size()]; |
| 308 | return (T) propValue.value.int32Values.toArray(intArray); |
| 309 | } else if (clazz == Float[].class) { |
| 310 | Float[] floatArray = new Float[propValue.value.floatValues.size()]; |
| 311 | return (T) propValue.value.floatValues.toArray(floatArray); |
| 312 | } else if (clazz == int[].class) { |
| 313 | return (T) toIntArray(propValue.value.int32Values); |
| 314 | } else if (clazz == float[].class) { |
| 315 | return (T) toFloatArray(propValue.value.floatValues); |
| 316 | } else if (clazz == byte[].class) { |
| 317 | return (T) toByteArray(propValue.value.bytes); |
| 318 | } else if (clazz == String.class) { |
| 319 | return (T) propValue.value.stringValue; |
| 320 | } else { |
| 321 | throw new IllegalArgumentException("Unexpected type: " + clazz); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | public VehiclePropValue get(VehiclePropValue requestedPropValue) |
| 326 | throws PropertyTimeoutException { |
| 327 | return mHalClient.getValue(requestedPropValue); |
| 328 | } |
| 329 | |
| 330 | void set(VehiclePropValue propValue) throws PropertyTimeoutException { |
| 331 | mHalClient.setValue(propValue); |
| 332 | } |
| 333 | |
| 334 | @CheckResult |
| 335 | VehiclePropValueSetter set(int propId) { |
| 336 | return new VehiclePropValueSetter(mHalClient, propId, NO_AREA); |
| 337 | } |
| 338 | |
| 339 | @CheckResult |
| 340 | VehiclePropValueSetter set(int propId, int areaId) { |
| 341 | return new VehiclePropValueSetter(mHalClient, propId, areaId); |
| 342 | } |
| 343 | |
| 344 | static boolean isPropertySubscribable(VehiclePropConfig config) { |
| 345 | if ((config.access & VehiclePropertyAccess.READ) == 0 || |
| 346 | (config.changeMode == VehiclePropertyChangeMode.STATIC)) { |
keunyoung | a74b9ca | 2015-10-21 13:33:58 -0700 | [diff] [blame] | 347 | return false; |
| 348 | } |
| 349 | return true; |
| 350 | } |
| 351 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 352 | static void dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs) { |
keunyoung | a74b9ca | 2015-10-21 13:33:58 -0700 | [diff] [blame] | 353 | for (VehiclePropConfig config : configs) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 354 | writer.println(String.format("property 0x%x", config.prop)); |
keunyoung | a74b9ca | 2015-10-21 13:33:58 -0700 | [diff] [blame] | 355 | } |
| 356 | } |
| 357 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 358 | private final ArraySet<HalServiceBase> mServicesToDispatch = new ArraySet<>(); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 359 | |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 360 | @Override |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 361 | public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) { |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 362 | synchronized (this) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 363 | for (VehiclePropValue v : propValues) { |
| 364 | HalServiceBase service = mPropertyHandlers.get(v.prop); |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 365 | service.getDispatchList().add(v); |
| 366 | mServicesToDispatch.add(service); |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 367 | VehiclePropertyEventInfo info = mEventLog.get(v.prop); |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 368 | if (info == null) { |
| 369 | info = new VehiclePropertyEventInfo(v); |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 370 | mEventLog.put(v.prop, info); |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 371 | } else { |
| 372 | info.addNewEvent(v); |
| 373 | } |
keunyoung | e4c90c4 | 2015-11-16 18:42:52 -0800 | [diff] [blame] | 374 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 375 | } |
keunyoung | fe30ba0 | 2015-09-17 17:56:35 -0700 | [diff] [blame] | 376 | for (HalServiceBase s : mServicesToDispatch) { |
| 377 | s.handleHalEvents(s.getDispatchList()); |
| 378 | s.getDispatchList().clear(); |
| 379 | } |
| 380 | mServicesToDispatch.clear(); |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 381 | } |
| 382 | |
Keun-young Park | 28dd470 | 2015-11-19 18:06:04 -0800 | [diff] [blame] | 383 | @Override |
Pavel Maltsev | b0324b4 | 2016-09-27 21:00:41 -0700 | [diff] [blame] | 384 | public void onPropertySet(VehiclePropValue value) { |
| 385 | // No need to handle on-property-set events in HAL service yet. |
| 386 | } |
| 387 | |
| 388 | @Override |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 389 | public void onPropertySetError(int errorCode, int propId, int areaId) { |
| 390 | Log.e(CarLog.TAG_HAL, String.format("onPropertySetError, errorCode: %d, prop: 0x%x, " |
| 391 | + "area: 0x%x", errorCode, propId, areaId)); |
Keun-young Park | f921520 | 2016-10-10 12:34:08 -0700 | [diff] [blame] | 392 | // TODO propagate per property error to HAL services and handle global error, bug:32068464 |
Keun-young Park | 28dd470 | 2015-11-19 18:06:04 -0800 | [diff] [blame] | 393 | } |
| 394 | |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 395 | public void dump(PrintWriter writer) { |
| 396 | writer.println("**dump HAL services**"); |
| 397 | for (HalServiceBase service: mAllServices) { |
| 398 | service.dump(writer); |
| 399 | } |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 400 | |
| 401 | List<VehiclePropConfig> configList; |
| 402 | synchronized (this) { |
| 403 | configList = new ArrayList<>(mAllProperties.values()); |
| 404 | } |
| 405 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 406 | |
Keun-young Park | 450faba | 2016-02-10 18:15:12 -0800 | [diff] [blame] | 407 | writer.println("**All properties**"); |
Steve Paik | 4d6088c | 2016-07-20 20:27:55 -0700 | [diff] [blame] | 408 | for (VehiclePropConfig config : configList) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 409 | StringBuilder builder = new StringBuilder() |
| 410 | .append("Property:0x").append(toHexString(config.prop)) |
| 411 | .append(",access:0x").append(toHexString(config.access)) |
| 412 | .append(",changeMode:0x").append(toHexString(config.changeMode)) |
| 413 | .append(",permission:0x").append(toHexString(config.permissionModel)) |
| 414 | .append(",areas:0x").append(toHexString(config.supportedAreas)) |
| 415 | .append(",config:0x").append(Arrays.toString(config.configArray.toArray())) |
| 416 | .append(",fs min:").append(config.minSampleRate) |
| 417 | .append(",fs max:").append(config.maxSampleRate); |
| 418 | for (VehicleAreaConfig area : config.areaConfigs) { |
| 419 | builder.append(",areaId :").append(toHexString(area.areaId)) |
| 420 | .append(",f min:").append(area.minFloatValue) |
| 421 | .append(",f max:").append(area.maxFloatValue) |
| 422 | .append(",i min:").append(area.minInt32Value) |
| 423 | .append(",i max:").append(area.maxInt32Value) |
| 424 | .append(",i64 min:").append(area.minInt64Value) |
| 425 | .append(",i64 max:").append(area.maxInt64Value); |
Keun-young Park | 450faba | 2016-02-10 18:15:12 -0800 | [diff] [blame] | 426 | } |
| 427 | writer.println(builder.toString()); |
| 428 | } |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 429 | writer.println(String.format("**All Events, now ns:%d**", |
| 430 | SystemClock.elapsedRealtimeNanos())); |
| 431 | for (VehiclePropertyEventInfo info : mEventLog.values()) { |
| 432 | writer.println(String.format("event count:%d, lastEvent:%s", |
| 433 | info.eventCount, dumpVehiclePropValue(info.lastEvent))); |
| 434 | } |
Pavel Maltsev | 437ab41 | 2016-08-15 15:41:06 -0700 | [diff] [blame] | 435 | |
| 436 | writer.println("**Property handlers**"); |
| 437 | for (int i = 0; i < mPropertyHandlers.size(); i++) { |
| 438 | int propId = mPropertyHandlers.keyAt(i); |
| 439 | HalServiceBase service = mPropertyHandlers.valueAt(i); |
| 440 | writer.println(String.format("Prop: 0x%08X, service: %s", propId, service)); |
| 441 | } |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 442 | } |
| 443 | |
Keun-young Park | 97e46eb | 2016-04-09 15:02:38 -0700 | [diff] [blame] | 444 | private static class VehiclePropertyEventInfo { |
| 445 | private int eventCount; |
| 446 | private VehiclePropValue lastEvent; |
| 447 | |
| 448 | private VehiclePropertyEventInfo(VehiclePropValue event) { |
| 449 | eventCount = 1; |
| 450 | lastEvent = event; |
| 451 | } |
| 452 | |
| 453 | private void addNewEvent(VehiclePropValue event) { |
| 454 | eventCount++; |
| 455 | lastEvent = event; |
| 456 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 457 | } |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 458 | |
| 459 | final class VehiclePropValueSetter { |
| 460 | final WeakReference<HalClient> mClient; |
| 461 | final VehiclePropValue mPropValue; |
| 462 | |
| 463 | private VehiclePropValueSetter(HalClient client, int propId, int areaId) { |
| 464 | mClient = new WeakReference<>(client); |
| 465 | mPropValue = new VehiclePropValue(); |
| 466 | mPropValue.prop = propId; |
| 467 | mPropValue.areaId = areaId; |
| 468 | } |
| 469 | |
| 470 | void to(boolean value) throws PropertyTimeoutException { |
| 471 | to(value ? 1 : 0); |
| 472 | } |
| 473 | |
| 474 | void to(int value) throws PropertyTimeoutException { |
| 475 | mPropValue.value.int32Values.add(value); |
| 476 | submit(); |
| 477 | } |
| 478 | |
| 479 | void to(int[] values) throws PropertyTimeoutException { |
| 480 | for (int value : values) { |
| 481 | mPropValue.value.int32Values.add(value); |
| 482 | } |
| 483 | submit(); |
| 484 | } |
| 485 | |
| 486 | void to(Collection<Integer> values) throws PropertyTimeoutException { |
| 487 | mPropValue.value.int32Values.addAll(values); |
| 488 | submit(); |
| 489 | } |
| 490 | |
| 491 | void submit() throws PropertyTimeoutException { |
| 492 | HalClient client = mClient.get(); |
| 493 | if (client != null) { |
| 494 | Log.i(CarLog.TAG_HAL, "set, property: 0x" + toHexString(mPropValue.prop) |
| 495 | + ", areaId: 0x" + toHexString(mPropValue.areaId)); |
| 496 | client.setValue(mPropValue); |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | private static String dumpVehiclePropValue(VehiclePropValue value) { |
| 502 | final int MAX_BYTE_SIZE = 20; |
| 503 | |
| 504 | StringBuilder sb = new StringBuilder() |
| 505 | .append("Property:0x").append(toHexString(value.prop)) |
| 506 | .append(",timestamp:").append(value.timestamp) |
| 507 | .append(",zone:0x").append(toHexString(value.areaId)) |
| 508 | .append(",floatValues: ").append(Arrays.toString(value.value.floatValues.toArray())) |
| 509 | .append(",int32Values: ").append(Arrays.toString(value.value.int32Values.toArray())) |
| 510 | .append(",int64Values: ") |
| 511 | .append(Arrays.toString(value.value.int64Values.toArray())); |
| 512 | |
| 513 | if (value.value.bytes.size() > MAX_BYTE_SIZE) { |
| 514 | Object[] bytes = Arrays.copyOf(value.value.bytes.toArray(), MAX_BYTE_SIZE); |
| 515 | sb.append(",bytes: ").append(Arrays.toString(bytes)); |
| 516 | } else { |
| 517 | sb.append(",bytes: ").append(Arrays.toString(value.value.bytes.toArray())); |
| 518 | } |
| 519 | sb.append(",string: ").append(value.value.stringValue); |
| 520 | |
| 521 | return sb.toString(); |
| 522 | } |
| 523 | |
| 524 | private static VehiclePropValue createPropValue(int propId, int areaId) { |
| 525 | VehiclePropValue propValue = new VehiclePropValue(); |
| 526 | propValue.prop = propId; |
| 527 | propValue.areaId = areaId; |
| 528 | return propValue; |
| 529 | } |
keunyoung | cc449f7 | 2015-08-12 10:46:27 -0700 | [diff] [blame] | 530 | } |