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 {
createInstance()54         createInstance();
55     }
56 
57     private static VehicleHal sInstance;
58 
getInstance()59     public static synchronized VehicleHal getInstance() {
60         if (sInstance == null) {
61             createInstance();
62         }
63         return sInstance;
64     }
65 
createInstance()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 
releaseInstance()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 
VehicleHal()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
VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal, AudioHalService audioHal, RadioHalService radioHal, HvacHalService hvacHal, VehicleNetwork vehicleNetwork)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 
init()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 
release()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 
startMocking()183     public void startMocking() {
184         reinitHals();
185     }
186 
stopMocking()187     public void stopMocking() {
188         reinitHals();
189     }
190 
reinitHals()191     private void reinitHals() {
192         release();
193         init();
194     }
195 
getSensorHal()196     public SensorHalService getSensorHal() {
197         return mSensorHal;
198     }
199 
getInfoHal()200     public InfoHalService getInfoHal() {
201         return mInfoHal;
202     }
203 
getAudioHal()204     public AudioHalService getAudioHal() {
205         return mAudioHal;
206     }
207 
getRadioHal()208     public RadioHalService getRadioHal() {
209         return mRadioHal;
210     }
211 
getPowerHal()212     public PowerHalService getPowerHal() {
213         return mPowerHal;
214     }
215 
getHvacHal()216     public HvacHalService getHvacHal() {
217         return mHvacHal;
218     }
219 
getInputHal()220     public InputHalService getInputHal() {
221         return mInputHal;
222     }
223 
assertServiceOwnerLocked(HalServiceBase service, int property)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      */
subscribeProperty(HalServiceBase service, int property, float samplingRateHz)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 
unsubscribeProperty(HalServiceBase service, int property)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 
getVehicleNetwork()253     public VehicleNetwork getVehicleNetwork() {
254         return mVehicleNetwork;
255     }
256 
isPropertySubscribable(VehiclePropConfig config)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 
dumpProperties(PrintWriter writer, Collection<VehiclePropConfig> configs)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
onVehicleNetworkEvents(VehiclePropValues values)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
onHalError(int errorCode, int property, int operation)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
onHalRestart(boolean inMocking)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 
dump(PrintWriter writer)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