Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | import static java.lang.Integer.toHexString; |
| 21 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 22 | import android.annotation.Nullable; |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 23 | import android.car.hardware.CarPropertyConfig; |
| 24 | import android.car.hardware.CarPropertyValue; |
| 25 | import android.car.hardware.property.CarPropertyEvent; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 26 | import android.hardware.vehicle.V2_0.VehiclePropConfig; |
| 27 | import android.hardware.vehicle.V2_0.VehiclePropValue; |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 28 | import android.util.Log; |
| 29 | import android.util.SparseIntArray; |
| 30 | |
| 31 | import com.android.car.CarLog; |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 32 | import com.android.internal.annotations.GuardedBy; |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 33 | |
| 34 | import java.io.PrintWriter; |
| 35 | import java.util.ArrayList; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 36 | import java.util.Collection; |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 37 | import java.util.LinkedList; |
| 38 | import java.util.List; |
| 39 | import java.util.concurrent.ConcurrentHashMap; |
| 40 | |
| 41 | /** |
| 42 | * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty. |
| 43 | * Services that communicate by passing vehicle properties back and forth via ICarProperty should |
| 44 | * extend this class. |
| 45 | */ |
| 46 | public abstract class PropertyHalServiceBase extends HalServiceBase { |
| 47 | private final boolean mDbg; |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 48 | private final ConcurrentHashMap<Integer, CarPropertyConfig<?>> mProps = |
| 49 | new ConcurrentHashMap<>(); |
| 50 | private final String mTag; |
| 51 | private final VehicleHal mVehicleHal; |
| 52 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 53 | @GuardedBy("mLock") |
| 54 | private PropertyHalListener mListener; |
| 55 | private final Object mLock = new Object(); |
| 56 | |
| 57 | protected final static int NOT_SUPPORTED_PROPERTY = -1; |
| 58 | |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 59 | public interface PropertyHalListener { |
| 60 | void onPropertyChange(CarPropertyEvent event); |
| 61 | void onError(int zone, int property); |
| 62 | } |
| 63 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 64 | protected PropertyHalServiceBase(VehicleHal vehicleHal, String tag, boolean dbg) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 65 | mVehicleHal = vehicleHal; |
| 66 | mTag = "PropertyHalServiceBase." + tag; |
| 67 | mDbg = dbg; |
| 68 | |
| 69 | if (mDbg) { |
| 70 | Log.d(mTag, "started PropertyHalServiceBase!"); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | public void setListener(PropertyHalListener listener) { |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 75 | synchronized (mLock) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 76 | mListener = listener; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | public List<CarPropertyConfig> getPropertyList() { |
| 81 | return new ArrayList<>(mProps.values()); |
| 82 | } |
| 83 | |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 84 | /** |
| 85 | * Returns property or null if property is not ready yet. |
| 86 | */ |
| 87 | @Nullable |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 88 | public CarPropertyValue getProperty(int mgrPropId, int areaId) { |
| 89 | int halPropId = managerToHalPropId(mgrPropId); |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 90 | if (halPropId == NOT_SUPPORTED_PROPERTY) { |
| 91 | throw new IllegalArgumentException("Invalid property Id : 0x" + toHexString(mgrPropId)); |
| 92 | } |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 93 | |
| 94 | VehiclePropValue value = null; |
| 95 | try { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 96 | value = mVehicleHal.get(halPropId, areaId); |
| 97 | } catch (PropertyTimeoutException e) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 98 | Log.e(CarLog.TAG_PROPERTY, "get, property not ready 0x" + toHexString(halPropId), e); |
| 99 | } |
| 100 | |
| 101 | return value == null ? null : toCarPropertyValue(value, mgrPropId); |
| 102 | } |
| 103 | |
| 104 | public void setProperty(CarPropertyValue prop) { |
| 105 | int halPropId = managerToHalPropId(prop.getPropertyId()); |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 106 | if (halPropId == NOT_SUPPORTED_PROPERTY) { |
| 107 | throw new IllegalArgumentException("Invalid property Id : 0x" |
| 108 | + toHexString(prop.getPropertyId())); |
| 109 | } |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 110 | VehiclePropValue halProp = toVehiclePropValue(prop, halPropId); |
| 111 | try { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 112 | mVehicleHal.set(halProp); |
| 113 | } catch (PropertyTimeoutException e) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 114 | Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e); |
| 115 | throw new RuntimeException(e); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | @Override |
| 120 | public void init() { |
| 121 | if (mDbg) { |
| 122 | Log.d(mTag, "init()"); |
| 123 | } |
| 124 | // Subscribe to each of the properties |
| 125 | for (Integer prop : mProps.keySet()) { |
| 126 | mVehicleHal.subscribeProperty(this, prop, 0); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | @Override |
| 131 | public void release() { |
| 132 | if (mDbg) { |
| 133 | Log.d(mTag, "release()"); |
| 134 | } |
| 135 | |
| 136 | for (Integer prop : mProps.keySet()) { |
| 137 | mVehicleHal.unsubscribeProperty(this, prop); |
| 138 | } |
| 139 | |
| 140 | // Clear the property list |
| 141 | mProps.clear(); |
| 142 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 143 | synchronized (mLock) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 144 | mListener = null; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | @Override |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 149 | public Collection<VehiclePropConfig> takeSupportedProperties( |
| 150 | Collection<VehiclePropConfig> allProperties) { |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 151 | List<VehiclePropConfig> taken = new LinkedList<>(); |
| 152 | |
| 153 | for (VehiclePropConfig p : allProperties) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 154 | int mgrPropId = halToManagerPropId(p.prop); |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 155 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 156 | if (mgrPropId == NOT_SUPPORTED_PROPERTY) { |
| 157 | continue; // The property is not handled by this HAL. |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 158 | } |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 159 | |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 160 | CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, mgrPropId); |
| 161 | |
| 162 | taken.add(p); |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 163 | mProps.put(p.prop, config); |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 164 | |
| 165 | if (mDbg) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 166 | Log.d(mTag, "takeSupportedProperties: " + toHexString(p.prop)); |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | return taken; |
| 170 | } |
| 171 | |
| 172 | @Override |
| 173 | public void handleHalEvents(List<VehiclePropValue> values) { |
| 174 | PropertyHalListener listener; |
| 175 | synchronized (this) { |
| 176 | listener = mListener; |
| 177 | } |
| 178 | if (listener != null) { |
| 179 | for (VehiclePropValue v : values) { |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame^] | 180 | int prop = v.prop; |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 181 | int mgrPropId = halToManagerPropId(prop); |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 182 | |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 183 | if (mgrPropId == NOT_SUPPORTED_PROPERTY) { |
| 184 | Log.e(mTag, "Property is not supported: 0x" + toHexString(prop)); |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 185 | continue; |
| 186 | } |
| 187 | |
| 188 | CarPropertyEvent event; |
| 189 | CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId); |
| 190 | event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, |
| 191 | propVal); |
| 192 | |
| 193 | listener.onPropertyChange(event); |
| 194 | if (mDbg) { |
| 195 | Log.d(mTag, "handleHalEvents event: " + event); |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | @Override |
| 202 | public void dump(PrintWriter writer) { |
| 203 | writer.println(mTag); |
| 204 | writer.println(" Properties available:"); |
| 205 | for (CarPropertyConfig prop : mProps.values()) { |
| 206 | writer.println(" " + prop.toString()); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /** |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 211 | * Converts manager property ID to Vehicle HAL property ID. |
| 212 | * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}. |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 213 | */ |
| 214 | abstract protected int managerToHalPropId(int managerPropId); |
| 215 | |
| 216 | /** |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 217 | * Converts Vehicle HAL property ID to manager property ID. |
| 218 | * If property is not supported, it will return {@link #NOT_SUPPORTED_PROPERTY}. |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 219 | */ |
| 220 | abstract protected int halToManagerPropId(int halPropId); |
Pavel Maltsev | 634e1ff | 2016-07-14 15:41:26 -0700 | [diff] [blame] | 221 | |
| 222 | /** |
| 223 | * Helper class that maintains bi-directional mapping between manager's property |
| 224 | * Id (public or system API) and vehicle HAL property Id. |
| 225 | * |
| 226 | * <p>This class is supposed to be immutable. Use {@link #create(int[])} factory method to |
| 227 | * instantiate this class. |
| 228 | */ |
| 229 | static class ManagerToHalPropIdMap { |
| 230 | private final SparseIntArray mMap; |
| 231 | private final SparseIntArray mInverseMap; |
| 232 | |
| 233 | /** |
| 234 | * Creates {@link ManagerToHalPropIdMap} for provided [manager prop Id, hal prop Id] pairs. |
| 235 | * |
| 236 | * <p> The input array should have an odd number of elements. |
| 237 | */ |
| 238 | static ManagerToHalPropIdMap create(int[] mgrToHalPropIds) { |
| 239 | int inputLength = mgrToHalPropIds.length; |
| 240 | if (inputLength % 2 != 0) { |
| 241 | throw new IllegalArgumentException("Odd number of key-value elements"); |
| 242 | } |
| 243 | |
| 244 | ManagerToHalPropIdMap biMap = new ManagerToHalPropIdMap(inputLength / 2); |
| 245 | for (int i = 0; i < mgrToHalPropIds.length; i += 2) { |
| 246 | biMap.put(mgrToHalPropIds[i], mgrToHalPropIds[i + 1]); |
| 247 | } |
| 248 | return biMap; |
| 249 | } |
| 250 | |
| 251 | private ManagerToHalPropIdMap(int initialCapacity) { |
| 252 | mMap = new SparseIntArray(initialCapacity); |
| 253 | mInverseMap = new SparseIntArray(initialCapacity); |
| 254 | } |
| 255 | |
| 256 | private void put(int managerPropId, int halPropId) { |
| 257 | mMap.put(managerPropId, halPropId); |
| 258 | mInverseMap.put(halPropId, managerPropId); |
| 259 | } |
| 260 | |
| 261 | int getHalPropId(int managerPropId) { |
| 262 | return mMap.get(managerPropId, NOT_SUPPORTED_PROPERTY); |
| 263 | } |
| 264 | |
| 265 | int getManagerPropId(int halPropId) { |
| 266 | return mInverseMap.get(halPropId, NOT_SUPPORTED_PROPERTY); |
| 267 | } |
| 268 | } |
Steve Paik | 461ecc6 | 2016-06-08 15:28:32 -0700 | [diff] [blame] | 269 | } |