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 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 
22 import android.car.hardware.CarPropertyConfig;
23 import android.car.hardware.CarPropertyValue;
24 import android.car.hardware.hvac.CarHvacEvent;
25 import android.car.hardware.hvac.CarHvacManager.HvacPropertyId;
26 import android.os.ServiceSpecificException;
27 import android.util.Log;
28 import android.util.SparseIntArray;
29 
30 import com.android.car.CarLog;
31 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
32 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
33 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
34 
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.LinkedList;
39 import java.util.List;
40 
41 public class HvacHalService extends HalServiceBase {
42     private static final boolean   DBG = true;
43     private static final String    TAG = CarLog.TAG_HVAC + ".HvacHalService";
44     private HvacHalListener        mListener;
45     private final VehicleHal       mVehicleHal;
46 
47     private final HashMap<Integer, CarPropertyConfig<?>> mProps = new HashMap<>();
48     private final SparseIntArray mHalPropToValueType = new SparseIntArray();
49 
50     public interface HvacHalListener {
onPropertyChange(CarHvacEvent event)51         void onPropertyChange(CarHvacEvent event);
onError(int zone, int property)52         void onError(int zone, int property);
53     }
54 
HvacHalService(VehicleHal vehicleHal)55     public HvacHalService(VehicleHal vehicleHal) {
56         mVehicleHal = vehicleHal;
57         if (DBG) {
58             Log.d(TAG, "started HvacHalService!");
59         }
60     }
61 
setListener(HvacHalListener listener)62     public void setListener(HvacHalListener listener) {
63         synchronized (this) {
64             mListener = listener;
65         }
66     }
67 
getHvacProperties()68     public List<CarPropertyConfig> getHvacProperties() {
69         List<CarPropertyConfig> propList;
70         synchronized (mProps) {
71             propList = new ArrayList<>(mProps.values());
72         }
73         return propList;
74     }
75 
getHvacProperty(int hvacPropertyId, int areaId)76     public CarPropertyValue getHvacProperty(int hvacPropertyId, int areaId) {
77         int halProp = hvacToHalPropId(hvacPropertyId);
78 
79         VehiclePropValue value = null;
80         try {
81             VehiclePropValue valueRequest = VehiclePropValue.newBuilder()
82                     .setProp(halProp)
83                     .setZone(areaId)
84                     .setValueType(mHalPropToValueType.get(halProp))
85                     .build();
86 
87             value = mVehicleHal.getVehicleNetwork().getProperty(valueRequest);
88         } catch (ServiceSpecificException e) {
89             Log.e(CarLog.TAG_HVAC, "property not ready 0x" + toHexString(halProp), e);
90         }
91 
92         return value == null ? null : toCarPropertyValue(value, hvacPropertyId);
93     }
94 
setHvacProperty(CarPropertyValue prop)95     public void setHvacProperty(CarPropertyValue prop) {
96         VehiclePropValue halProp = toVehiclePropValue(prop, hvacToHalPropId(prop.getPropertyId()));
97         mVehicleHal.getVehicleNetwork().setProperty(halProp);
98     }
99 
100     @Override
init()101     public void init() {
102         if (DBG) {
103             Log.d(TAG, "init()");
104         }
105         synchronized (mProps) {
106             // Subscribe to each of the HVAC properties
107             for (Integer prop : mProps.keySet()) {
108                 mVehicleHal.subscribeProperty(this, prop, 0);
109             }
110         }
111     }
112 
113     @Override
release()114     public void release() {
115         if (DBG) {
116             Log.d(TAG, "release()");
117         }
118         synchronized (mProps) {
119             for (Integer prop : mProps.keySet()) {
120                 mVehicleHal.unsubscribeProperty(this, prop);
121             }
122 
123             // Clear the property list
124             mProps.clear();
125         }
126         mListener = null;
127     }
128 
129     @Override
takeSupportedProperties( List<VehiclePropConfig> allProperties)130     public synchronized List<VehiclePropConfig> takeSupportedProperties(
131             List<VehiclePropConfig> allProperties) {
132         List<VehiclePropConfig> taken = new LinkedList<>();
133 
134         for (VehiclePropConfig p : allProperties) {
135             int hvacPropId;
136             try {
137                 hvacPropId = halToHvacPropId(p.getProp());
138             } catch (IllegalArgumentException e) {
139                 Log.i(TAG, "Property not supported by HVAC: 0x" + toHexString(p.getProp()));
140                 continue;
141             }
142             CarPropertyConfig hvacConfig = CarPropertyUtils.toCarPropertyConfig(p, hvacPropId);
143 
144             taken.add(p);
145             mProps.put(p.getProp(), hvacConfig);
146             mHalPropToValueType.put(p.getProp(), p.getValueType());
147 
148             if (DBG) {
149                 Log.d(TAG, "takeSupportedProperties:  " + toHexString(p.getProp()));
150             }
151         }
152         return taken;
153     }
154 
155     @Override
handleHalEvents(List<VehiclePropValue> values)156     public void handleHalEvents(List<VehiclePropValue> values) {
157         HvacHalListener listener;
158         synchronized (this) {
159             listener = mListener;
160         }
161         if (listener != null) {
162             dispatchEventToListener(listener, values);
163         }
164     }
165 
dispatchEventToListener(HvacHalListener listener, List<VehiclePropValue> values)166     private void dispatchEventToListener(HvacHalListener listener, List<VehiclePropValue> values) {
167         for (VehiclePropValue v : values) {
168             int prop = v.getProp();
169 
170             int hvacPropId;
171             try {
172                 hvacPropId = halToHvacPropId(prop);
173             } catch (IllegalArgumentException ex) {
174                 Log.e(TAG, "Property is not supported: 0x" + toHexString(prop), ex);
175                 continue;
176             }
177 
178             CarHvacEvent event;
179             CarPropertyValue<?> hvacProperty = toCarPropertyValue(v, hvacPropId);
180             event = new CarHvacEvent(CarHvacEvent.HVAC_EVENT_PROPERTY_CHANGE, hvacProperty);
181 
182             listener.onPropertyChange(event);
183             if (DBG) {
184                 Log.d(TAG, "handleHalEvents event: " + event);
185             }
186         }
187     }
188 
189     @Override
dump(PrintWriter writer)190     public void dump(PrintWriter writer) {
191         writer.println("*HVAC HAL*");
192         writer.println("  Properties available:");
193         for (CarPropertyConfig prop : mProps.values()) {
194             writer.println("    " + prop.toString());
195         }
196     }
197 
198     // Convert the HVAC public API property ID to HAL property ID
hvacToHalPropId(int hvacPropId)199     private static int hvacToHalPropId(int hvacPropId) {
200         switch (hvacPropId) {
201             case HvacPropertyId.ZONED_FAN_SPEED_SETPOINT:
202                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_SPEED;
203             case HvacPropertyId.ZONED_FAN_POSITION:
204                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_DIRECTION;
205             case HvacPropertyId.ZONED_TEMP_ACTUAL:
206                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_TEMPERATURE_CURRENT;
207             case HvacPropertyId.ZONED_TEMP_SETPOINT:
208                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET;
209             case HvacPropertyId.WINDOW_DEFROSTER_ON:
210                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_DEFROSTER;
211             case HvacPropertyId.ZONED_AC_ON:
212                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_AC_ON;
213             case HvacPropertyId.ZONED_AIR_RECIRCULATION_ON:
214                 return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_RECIRC_ON;
215             default:
216                 throw new IllegalArgumentException("hvacPropId " + hvacPropId + " is not supported");
217         }
218     }
219 
220     // Convert he HAL specific property ID to HVAC public API
halToHvacPropId(int halPropId)221     private static int halToHvacPropId(int halPropId) {
222         switch (halPropId) {
223             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_SPEED:
224                 return HvacPropertyId.ZONED_FAN_SPEED_SETPOINT;
225             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_DIRECTION:
226                 return HvacPropertyId.ZONED_FAN_POSITION;
227             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_TEMPERATURE_CURRENT:
228                 return HvacPropertyId.ZONED_TEMP_ACTUAL;
229             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET:
230                 return HvacPropertyId.ZONED_TEMP_SETPOINT;
231             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_DEFROSTER:
232                 return HvacPropertyId.WINDOW_DEFROSTER_ON;
233             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_AC_ON:
234                 return HvacPropertyId.ZONED_AC_ON;
235             case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_RECIRC_ON:
236                 return HvacPropertyId.ZONED_AIR_RECIRCULATION_ON;
237             default:
238                 throw new IllegalArgumentException("halPropId " + halPropId + " is not supported");
239         }
240     }
241 }
242