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 android.car.hardware.hvac;
18 
19 import android.annotation.IntDef;
20 import android.annotation.SystemApi;
21 import android.car.Car;
22 import android.car.CarManagerBase;
23 import android.car.CarNotConnectedException;
24 import android.car.hardware.CarPropertyConfig;
25 import android.car.hardware.CarPropertyValue;
26 import android.car.hardware.property.CarPropertyManagerBase;
27 import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventCallback;
28 import android.content.Context;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.util.ArraySet;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.lang.ref.WeakReference;
36 import java.util.Collection;
37 import java.util.List;
38 
39 /**
40  * API for controlling HVAC system in cars
41  * @hide
42  */
43 @SystemApi
44 public final class CarHvacManager implements CarManagerBase {
45     private final static boolean DBG = false;
46     private final static String TAG = "CarHvacManager";
47     private final CarPropertyManagerBase mMgr;
48     private final ArraySet<CarHvacEventCallback> mCallbacks = new ArraySet<>();
49     private CarPropertyEventListenerToBase mListenerToBase = null;
50 
51     /**
52      * HVAC property IDs for get/set methods
53      */
54     /**
55      * Global HVAC properties.  There is only a single instance in a car.
56      * Global properties are in the range of 0-0x3FFF.
57      */
58     /**
59      * Mirror defrosters state, bool type
60      * true indicates mirror defroster is on
61      */
62     public static final int ID_MIRROR_DEFROSTER_ON = 0x0001;
63     /**
64      * Steering wheel temp, int type
65      * Positive values indicate heating.
66      * Negative values indicate cooling
67      */
68     public static final int ID_STEERING_WHEEL_TEMP = 0x0002;
69     /**
70      * Outside air temperature, float type
71      * Value is in degrees of ID_TEMPERATURE_UNITS
72      */
73     public static final int ID_OUTSIDE_AIR_TEMP = 0x0003;
74     /**
75      * Temperature units being used, int type
76      *  0x30 = Celsius
77      *  0x31 = Fahrenheit
78      */
79     public static final int ID_TEMPERATURE_UNITS = 0x0004;
80 
81 
82     /**
83      * The maximum id that can be assigned to global (non-zoned) property.
84      * @hide
85      */
86     public static final int ID_MAX_GLOBAL_PROPERTY_ID = 0x3fff;
87 
88     /**
89      * ID_ZONED_* represents properties available on a per-zone basis.  All zones in a car are
90      * not required to have the same properties.  Zone specific properties start at 0x4000.
91      */
92     /**
93      * Temperature setpoint, float type
94      * Temperature set by the user, units are determined by ID_TEMPERTURE_UNITS property.
95      */
96     public static final int ID_ZONED_TEMP_SETPOINT = 0x4001;
97     /**
98      * Actual temperature, float type
99      * Actual zone temperature is read only value, in terms of F or C.
100      */
101     public static final int ID_ZONED_TEMP_ACTUAL = 0x4002;
102     /**
103      * HVAC system powered on / off, bool type
104      * In many vehicles, if the HVAC system is powered off, the SET and GET command will
105      * throw an IllegalStateException.  To correct this, need to turn on the HVAC module first
106      * before manipulating a parameter.
107      */
108     public static final int ID_ZONED_HVAC_POWER_ON = 0x4003;
109     /**
110      * Fan speed setpoint, int type
111      * Fan speed is an integer from 0-n, depending on number of fan speeds available.
112      */
113     public static final int ID_ZONED_FAN_SPEED_SETPOINT = 0x4004;
114     /**
115      * Actual fan speed, int type
116      * Actual fan speed is a read-only value, expressed in RPM.
117      */
118     public static final int ID_ZONED_FAN_SPEED_RPM = 0x4005;
119     /** Fan position available, int type
120      *  Fan position is a bitmask of positions available for each zone.
121      */
122     public static final int ID_ZONED_FAN_POSITION_AVAILABLE = 0x4006;
123     /**
124      * Current fan position setting, int type. The value must be one of the FAN_POSITION_*
125      * constants declared in {@link CarHvacManager}.
126      */
127     public static final int ID_ZONED_FAN_POSITION = 0x4007;
128     /**
129      * Seat temperature, int type
130      * Seat temperature is negative for cooling, positive for heating.  Temperature is a
131      * setting, i.e. -3 to 3 for 3 levels of cooling and 3 levels of heating.
132      */
133     public static final int ID_ZONED_SEAT_TEMP = 0x4008;
134     /**
135      * Air ON, bool type
136      * true indicates AC is ON.
137      */
138     public static final int ID_ZONED_AC_ON = 0x4009;
139     /**
140      * Automatic Mode ON, bool type
141      * true indicates HVAC is in automatic mode
142      */
143     public static final int ID_ZONED_AUTOMATIC_MODE_ON = 0x400A;
144     /**
145      * Air recirculation ON, bool type
146      * true indicates recirculation is active.
147      */
148     public static final int ID_ZONED_AIR_RECIRCULATION_ON = 0x400B;
149     /**
150      * Max AC ON, bool type
151      * true indicates MAX AC is ON
152      */
153     public static final int ID_ZONED_MAX_AC_ON = 0x400C;
154     /** Dual zone ON, bool type
155      * true indicates dual zone mode is ON
156      */
157     public static final int ID_ZONED_DUAL_ZONE_ON = 0x400D;
158     /**
159      * Max Defrost ON, bool type
160      * true indicates max defrost is active.
161      */
162     public static final int ID_ZONED_MAX_DEFROST_ON = 0x400E;
163     /**
164      * Defroster ON, bool type
165      * Defroster controls are based on window position.
166      * True indicates the defroster is ON.
167      */
168     public static final int ID_WINDOW_DEFROSTER_ON = 0x5001;
169 
170     /** @hide */
171     @IntDef({
172             ID_MIRROR_DEFROSTER_ON,
173             ID_STEERING_WHEEL_TEMP,
174             ID_OUTSIDE_AIR_TEMP,
175             ID_TEMPERATURE_UNITS,
176             ID_ZONED_TEMP_SETPOINT,
177             ID_ZONED_TEMP_ACTUAL,
178             ID_ZONED_FAN_SPEED_SETPOINT,
179             ID_ZONED_FAN_SPEED_RPM,
180             ID_ZONED_FAN_POSITION_AVAILABLE,
181             ID_ZONED_FAN_POSITION,
182             ID_ZONED_SEAT_TEMP,
183             ID_ZONED_AC_ON,
184             ID_ZONED_AUTOMATIC_MODE_ON,
185             ID_ZONED_AIR_RECIRCULATION_ON,
186             ID_ZONED_MAX_AC_ON,
187             ID_ZONED_DUAL_ZONE_ON,
188             ID_ZONED_MAX_DEFROST_ON,
189             ID_ZONED_HVAC_POWER_ON,
190             ID_WINDOW_DEFROSTER_ON,
191     })
192     @Retention(RetentionPolicy.SOURCE)
193     public @interface PropertyId {}
194 
195     /**
196      * Represents fan position when air flows through face directed vents.
197      * This constant must be used with {@link #ID_ZONED_FAN_POSITION} property.
198      */
199     public static final int FAN_POSITION_FACE = 1;
200     /**
201      * Represents fan position when air flows through floor directed vents.
202      * This constant must be used with {@link #ID_ZONED_FAN_POSITION} property.
203      */
204     public static final int FAN_POSITION_FLOOR = 2;
205     /**
206      * Represents fan position when air flows through face and floor directed vents.
207      * This constant must be used with {@link #ID_ZONED_FAN_POSITION} property.
208      */
209     public static final int FAN_POSITION_FACE_AND_FLOOR = 3;
210     /**
211      * Represents fan position when air flows through defrost vents.
212      * This constant must be used with {@link #ID_ZONED_FAN_POSITION} property.
213      */
214     public static final int FAN_POSITION_DEFROST = 4;
215     /**
216      * Represents fan position when air flows through defrost and floor directed vents.
217      * This constant must be used with {@link #ID_ZONED_FAN_POSITION} property.
218      */
219     public static final int FAN_POSITION_DEFROST_AND_FLOOR = 5;
220 
221     /**
222      * Application registers {@link CarHvacEventCallback} object to receive updates and changes to
223      * subscribed Car HVAC properties.
224      */
225     public interface CarHvacEventCallback {
226         /**
227          * Called when a property is updated
228          * @param value Property that has been updated.
229          */
onChangeEvent(CarPropertyValue value)230         void onChangeEvent(CarPropertyValue value);
231 
232         /**
233          * Called when an error is detected with a property
234          * @param propertyId
235          * @param zone
236          */
onErrorEvent(@ropertyId int propertyId, int zone)237         void onErrorEvent(@PropertyId int propertyId, int zone);
238     }
239 
240     private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
241         private final WeakReference<CarHvacManager> mManager;
242 
CarPropertyEventListenerToBase(CarHvacManager manager)243         public CarPropertyEventListenerToBase(CarHvacManager manager) {
244             mManager = new WeakReference<>(manager);
245         }
246 
247         @Override
onChangeEvent(CarPropertyValue value)248         public void onChangeEvent(CarPropertyValue value) {
249             CarHvacManager manager = mManager.get();
250             if (manager != null) {
251                 manager.handleOnChangeEvent(value);
252             }
253         }
254 
255         @Override
onErrorEvent(int propertyId, int zone)256         public void onErrorEvent(int propertyId, int zone) {
257             CarHvacManager manager = mManager.get();
258             if (manager != null) {
259                 manager.handleOnErrorEvent(propertyId, zone);
260             }
261         }
262     }
263 
handleOnChangeEvent(CarPropertyValue value)264     private void handleOnChangeEvent(CarPropertyValue value) {
265         Collection<CarHvacEventCallback> callbacks;
266         synchronized (this) {
267             callbacks = new ArraySet<>(mCallbacks);
268         }
269         if (!callbacks.isEmpty()) {
270             for (CarHvacEventCallback l: callbacks) {
271                 l.onChangeEvent(value);
272             }
273         }
274     }
275 
handleOnErrorEvent(int propertyId, int zone)276     private void handleOnErrorEvent(int propertyId, int zone) {
277         Collection<CarHvacEventCallback> callbacks;
278         synchronized (this) {
279             callbacks = new ArraySet<>(mCallbacks);
280         }
281         if (!callbacks.isEmpty()) {
282             for (CarHvacEventCallback l: callbacks) {
283                 l.onErrorEvent(propertyId, zone);
284             }
285         }
286     }
287 
288     /**
289      * Get an instance of the CarHvacManager.
290      *
291      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
292      * @param service
293      * @param context
294      * @param handler
295      * @hide
296      */
CarHvacManager(IBinder service, Context context, Handler handler)297     public CarHvacManager(IBinder service, Context context, Handler handler) {
298         mMgr = new CarPropertyManagerBase(service, handler, DBG, TAG);
299     }
300 
301     /**
302      * Determine if a property is zoned or not.
303      * @param propertyId
304      * @return true if property is a zoned type.
305      */
isZonedProperty(@ropertyId int propertyId)306     public static boolean isZonedProperty(@PropertyId int propertyId) {
307         return propertyId > ID_MAX_GLOBAL_PROPERTY_ID;
308     }
309 
310     /**
311      * Implement wrappers for contained CarPropertyManagerBase object
312      * @param callback
313      * @throws CarNotConnectedException
314      */
registerCallback(CarHvacEventCallback callback)315     public synchronized void registerCallback(CarHvacEventCallback callback) throws
316             CarNotConnectedException {
317         if (mCallbacks.isEmpty()) {
318             mListenerToBase = new CarPropertyEventListenerToBase(this);
319             mMgr.registerCallback(mListenerToBase);
320         }
321         mCallbacks.add(callback);
322     }
323 
324     /**
325      * Stop getting property updates for the given callback. If there are multiple registrations for
326      * this listener, all listening will be stopped.
327      * @param callback
328      */
unregisterCallback(CarHvacEventCallback callback)329     public synchronized void unregisterCallback(CarHvacEventCallback callback) {
330         mCallbacks.remove(callback);
331         if (mCallbacks.isEmpty()) {
332             mMgr.unregisterCallback();
333             mListenerToBase = null;
334         }
335     }
336 
337     /**
338      * Get list of properties available to Car Hvac Manager
339      * @return List of CarPropertyConfig objects available via Car Hvac Manager.
340      * @throws CarNotConnectedException if the connection to the car service has been lost.
341      */
getPropertyList()342     public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
343         return mMgr.getPropertyList();
344     }
345 
346     /**
347      * Get value of boolean property
348      * @param propertyId
349      * @param area
350      * @return value of requested boolean property
351      * @throws CarNotConnectedException
352      */
getBooleanProperty(@ropertyId int propertyId, int area)353     public boolean getBooleanProperty(@PropertyId int propertyId, int area)
354             throws CarNotConnectedException {
355         return mMgr.getBooleanProperty(propertyId, area);
356     }
357 
358     /**
359      * Get value of float property
360      * @param propertyId
361      * @param area
362      * @return value of requested float property
363      * @throws CarNotConnectedException
364      */
getFloatProperty(@ropertyId int propertyId, int area)365     public float getFloatProperty(@PropertyId int propertyId, int area)
366             throws CarNotConnectedException {
367         return mMgr.getFloatProperty(propertyId, area);
368     }
369 
370     /**
371      * Get value of integer property
372      * @param propertyId
373      * @param area
374      * @return value of requested integer property
375      * @throws CarNotConnectedException
376      */
getIntProperty(@ropertyId int propertyId, int area)377     public int getIntProperty(@PropertyId int propertyId, int area)
378             throws CarNotConnectedException {
379         return mMgr.getIntProperty(propertyId, area);
380     }
381 
382     /**
383      * Set the value of a boolean property
384      * @param propertyId
385      * @param area
386      * @param val
387      * @throws CarNotConnectedException
388      */
setBooleanProperty(@ropertyId int propertyId, int area, boolean val)389     public void setBooleanProperty(@PropertyId int propertyId, int area, boolean val)
390             throws CarNotConnectedException {
391         mMgr.setBooleanProperty(propertyId, area, val);
392     }
393 
394     /**
395      * Set the value of a float property
396      * @param propertyId
397      * @param area
398      * @param val
399      * @throws CarNotConnectedException
400      */
setFloatProperty(@ropertyId int propertyId, int area, float val)401     public void setFloatProperty(@PropertyId int propertyId, int area, float val)
402             throws CarNotConnectedException {
403         mMgr.setFloatProperty(propertyId, area, val);
404     }
405 
406     /**
407      * Set the value of an integer property
408      * @param propertyId
409      * @param area
410      * @param val
411      * @throws CarNotConnectedException
412      */
setIntProperty(@ropertyId int propertyId, int area, int val)413     public void setIntProperty(@PropertyId int propertyId, int area, int val)
414             throws CarNotConnectedException {
415         mMgr.setIntProperty(propertyId, area, val);
416     }
417 
418     /** @hide */
419     @Override
onCarDisconnected()420     public void onCarDisconnected() {
421         mMgr.onCarDisconnected();
422     }
423 }
424