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.hardware.CarPropertyConfig;
24 import android.car.hardware.CarPropertyValue;
25 import android.car.hardware.property.CarPropertyManager;
26 import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
27 import android.car.hardware.property.ICarProperty;
28 import android.os.IBinder;
29 import android.util.ArraySet;
30 import android.util.Slog;
31 
32 import com.android.internal.annotations.GuardedBy;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.lang.ref.WeakReference;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.List;
40 
41 /**
42  * API for controlling HVAC system in cars
43  * @hide
44  * @deprecated Use {@link CarPropertyManager} instead.
45  */
46 @Deprecated
47 @SystemApi
48 public final class CarHvacManager extends CarManagerBase {
49     private static final String TAG = "CarHvacManager";
50     private final CarPropertyManager mCarPropertyMgr;
51     @GuardedBy("mLock")
52     private CarPropertyEventListenerToBase mListenerToBase = null;
53 
54     private final Object mLock = new Object();
55 
56     @GuardedBy("mLock")
57     private final ArraySet<CarHvacEventCallback> mCallbacks = new ArraySet<>();
58 
59     /**
60      * HVAC property IDs for get/set methods
61      */
62     /**
63      * Mirror defrosters state, int type
64      * Positive values indicate mirror defroster is on
65      */
66     public static final int ID_MIRROR_DEFROSTER_ON = 0x1440050c;
67     /**
68      * Steering wheel temp, int type
69      * Positive values indicate heating.
70      * Negative values indicate cooling
71      */
72     public static final int ID_STEERING_WHEEL_HEAT = 0x1140050d;
73     /**
74      * Outside air temperature, float type
75      * Value is in degrees Celsius
76      */
77     public static final int ID_OUTSIDE_AIR_TEMP = 0x11600703;
78     /**
79      * Temperature units being used, int type
80      *  0x30 = Celsius
81      *  0x31 = Fahrenheit
82      */
83     public static final int ID_TEMPERATURE_DISPLAY_UNITS = 0x1140050e;
84 
85     /**
86      * Temperature setpoint, float type
87      * Temperature set by the user, units are in degrees Celsius.
88      */
89     public static final int ID_ZONED_TEMP_SETPOINT = 0x15600503;
90     /**
91      * Actual temperature, float type
92      * Actual zone temperature is read only value, in terms of F or C.
93      */
94     public static final int ID_ZONED_TEMP_ACTUAL = 0x15600502;
95     /**
96      * HVAC system powered on / off, bool type
97      * In many vehicles, if the HVAC system is powered off, the SET and GET command will
98      * throw an IllegalStateException.  To correct this, need to turn on the HVAC module first
99      * before manipulating a parameter.
100      */
101     public static final int ID_ZONED_HVAC_POWER_ON = 0x15200510;
102     /**
103      * Fan speed setpoint, int type
104      * Fan speed is an integer from 0-n, depending on number of fan speeds available.
105      */
106     public static final int ID_ZONED_FAN_SPEED_SETPOINT = 0x15400500;
107     /**
108      * Actual fan speed, int type
109      * Actual fan speed is a read-only value, expressed in RPM.
110      */
111     public static final int ID_ZONED_FAN_SPEED_RPM = 0x1540050f;
112     /**
113      *  Fan direction available, int vector type
114      *  Fan direction is a bitmask of directions available for each zone.
115      */
116     public static final int ID_ZONED_FAN_DIRECTION_AVAILABLE = 0x15410511;
117     /**
118      * Current fan direction setting, int type. The value must be one of the FAN_DIRECTION_AVAILABLE
119      * values declared above.
120      */
121     public static final int ID_ZONED_FAN_DIRECTION = 0x15400501;
122     /**
123      * Seat temperature, int type
124      * Seat temperature is negative for cooling, positive for heating.  Temperature is a
125      * setting, i.e. -3 to 3 for 3 levels of cooling and 3 levels of heating.
126      */
127     public static final int ID_ZONED_SEAT_TEMP = 0x1540050b;
128     /**
129      * Air ON, bool type
130      * true indicates AC is ON.
131      */
132     public static final int ID_ZONED_AC_ON = 0x15200505;
133     /**
134      * Automatic Mode ON, bool type
135      * true indicates HVAC is in automatic mode
136      */
137     public static final int ID_ZONED_AUTOMATIC_MODE_ON = 0x1520050A;
138     /**
139      * Air recirculation ON, bool type
140      * true indicates recirculation is active.
141      */
142     public static final int ID_ZONED_AIR_RECIRCULATION_ON = 0x15200508;
143     /**
144      * Max AC ON, bool type
145      * true indicates MAX AC is ON
146      */
147     public static final int ID_ZONED_MAX_AC_ON = 0x15200506;
148     /** Dual zone ON, bool type
149      * true indicates dual zone mode is ON
150      */
151     public static final int ID_ZONED_DUAL_ZONE_ON = 0x15200509;
152     /**
153      * Max Defrost ON, bool type
154      * true indicates max defrost is active.
155      */
156     public static final int ID_ZONED_MAX_DEFROST_ON = 0x15200507;
157     /**
158      * Automatic recirculation mode ON
159      * true indicates recirculation is in automatic mode
160      */
161     public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 0x15200512;
162     /**
163      * Defroster ON, bool type
164      * Defroster controls are based on window position.
165      * True indicates the defroster is ON.
166      */
167     public static final int ID_WINDOW_DEFROSTER_ON = 0x13200504;
168 
169     /** @hide */
170     @IntDef({
171             ID_MIRROR_DEFROSTER_ON,
172             ID_STEERING_WHEEL_HEAT,
173             ID_OUTSIDE_AIR_TEMP,
174             ID_TEMPERATURE_DISPLAY_UNITS,
175             ID_ZONED_TEMP_SETPOINT,
176             ID_ZONED_TEMP_ACTUAL,
177             ID_ZONED_FAN_SPEED_SETPOINT,
178             ID_ZONED_FAN_SPEED_RPM,
179             ID_ZONED_FAN_DIRECTION_AVAILABLE,
180             ID_ZONED_FAN_DIRECTION,
181             ID_ZONED_SEAT_TEMP,
182             ID_ZONED_AC_ON,
183             ID_ZONED_AUTOMATIC_MODE_ON,
184             ID_ZONED_AIR_RECIRCULATION_ON,
185             ID_ZONED_MAX_AC_ON,
186             ID_ZONED_DUAL_ZONE_ON,
187             ID_ZONED_MAX_DEFROST_ON,
188             ID_ZONED_HVAC_POWER_ON,
189             ID_ZONED_HVAC_AUTO_RECIRC_ON,
190             ID_WINDOW_DEFROSTER_ON
191     })
192     @Retention(RetentionPolicy.SOURCE)
193     public @interface PropertyId {}
194     private final ArraySet<Integer> mHvacPropertyIds = new ArraySet<>(Arrays.asList(new Integer [] {
195             ID_MIRROR_DEFROSTER_ON,
196             ID_STEERING_WHEEL_HEAT,
197             ID_OUTSIDE_AIR_TEMP,
198             ID_TEMPERATURE_DISPLAY_UNITS,
199             ID_ZONED_TEMP_SETPOINT,
200             ID_ZONED_TEMP_ACTUAL,
201             ID_ZONED_FAN_SPEED_SETPOINT,
202             ID_ZONED_FAN_SPEED_RPM,
203             ID_ZONED_FAN_DIRECTION_AVAILABLE,
204             ID_ZONED_FAN_DIRECTION,
205             ID_ZONED_SEAT_TEMP,
206             ID_ZONED_AC_ON,
207             ID_ZONED_AUTOMATIC_MODE_ON,
208             ID_ZONED_AIR_RECIRCULATION_ON,
209             ID_ZONED_MAX_AC_ON,
210             ID_ZONED_DUAL_ZONE_ON,
211             ID_ZONED_MAX_DEFROST_ON,
212             ID_ZONED_HVAC_POWER_ON,
213             ID_ZONED_HVAC_AUTO_RECIRC_ON,
214             ID_WINDOW_DEFROSTER_ON
215     }));
216 
217 
218     /**
219      * Use {@link android.car.hardware.CarHvacFanDirection#FACE} instead.
220      * Represents fan direction when air flows through face directed vents.
221      * This constant must be used with {@link #ID_ZONED_FAN_DIRECTION} property.
222      */
223     public static final int FAN_DIRECTION_FACE = 0x1;
224     /**
225      * Use {@link android.car.hardware.CarHvacFanDirection#FLOOR} instead.
226      * Represents fan direction when air flows through floor directed vents.
227      * This constant must be used with {@link #ID_ZONED_FAN_DIRECTION} property.
228      */
229     public static final int FAN_DIRECTION_FLOOR = 0x2;
230     /**
231      * Use {@link android.car.hardware.CarHvacFanDirection#DEFROST} instead.
232      * Represents fan direction when air flows through defrost vents.
233      * This constant must be used with {@link #ID_ZONED_FAN_DIRECTION} property.
234      */
235     public static final int FAN_DIRECTION_DEFROST = 0x4;
236 
237     /**
238      * Application registers {@link CarHvacEventCallback} object to receive updates and changes to
239      * subscribed Car HVAC properties.
240      */
241     public interface CarHvacEventCallback {
242         /**
243          * Called when a property is updated
244          * @param value Property that has been updated.
245          */
onChangeEvent(CarPropertyValue value)246         void onChangeEvent(CarPropertyValue value);
247 
248         /**
249          * Called when an error is detected with a property
250          * @param propertyId
251          * @param zone
252          */
onErrorEvent(@ropertyId int propertyId, int zone)253         void onErrorEvent(@PropertyId int propertyId, int zone);
254     }
255 
256     private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
257         private final WeakReference<CarHvacManager> mManager;
258 
CarPropertyEventListenerToBase(CarHvacManager manager)259         CarPropertyEventListenerToBase(CarHvacManager manager) {
260             mManager = new WeakReference<>(manager);
261         }
262 
263         @Override
onChangeEvent(CarPropertyValue value)264         public void onChangeEvent(CarPropertyValue value) {
265             CarHvacManager manager = mManager.get();
266             if (manager != null) {
267                 manager.handleOnChangeEvent(value);
268             }
269         }
270 
271         @Override
onErrorEvent(int propertyId, int zone)272         public void onErrorEvent(int propertyId, int zone) {
273             CarHvacManager manager = mManager.get();
274             if (manager != null) {
275                 manager.handleOnErrorEvent(propertyId, zone);
276             }
277         }
278     }
279 
handleOnChangeEvent(CarPropertyValue value)280     private void handleOnChangeEvent(CarPropertyValue value) {
281         Collection<CarHvacEventCallback> callbacks;
282         synchronized (mLock) {
283             callbacks = new ArraySet<>(mCallbacks);
284         }
285         if (!callbacks.isEmpty()) {
286             for (CarHvacEventCallback l: callbacks) {
287                 l.onChangeEvent(value);
288             }
289         }
290     }
291 
handleOnErrorEvent(int propertyId, int zone)292     private void handleOnErrorEvent(int propertyId, int zone) {
293         Collection<CarHvacEventCallback> callbacks;
294         synchronized (mLock) {
295             callbacks = new ArraySet<>(mCallbacks);
296         }
297         if (!callbacks.isEmpty()) {
298             for (CarHvacEventCallback l: callbacks) {
299                 l.onErrorEvent(propertyId, zone);
300             }
301         }
302     }
303 
304     /**
305      * Get an instance of the CarHvacManager.
306      *
307      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
308      * @param service
309      *
310      * @hide
311      */
CarHvacManager(Car car, IBinder service)312     public CarHvacManager(Car car, IBinder service) {
313         super(car);
314         ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
315         mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
316     }
317 
318     /**
319      * Implement wrappers for contained CarPropertyManager object
320      * @param callback
321      */
registerCallback(CarHvacEventCallback callback)322     public void registerCallback(CarHvacEventCallback callback) {
323         synchronized (mLock) {
324             if (mCallbacks.isEmpty()) {
325                 mListenerToBase = new CarPropertyEventListenerToBase(this);
326             }
327             List<CarPropertyConfig> configs = getPropertyList();
328             for (CarPropertyConfig c : configs) {
329                 // Register each individual propertyId
330                 mCarPropertyMgr.registerCallback(mListenerToBase, c.getPropertyId(), 0);
331             }
332             mCallbacks.add(callback);
333         }
334     }
335 
336     /**
337      * Stop getting property updates for the given callback. If there are multiple registrations for
338      * this listener, all listening will be stopped.
339      * @param callback
340      */
unregisterCallback(CarHvacEventCallback callback)341     public void unregisterCallback(CarHvacEventCallback callback) {
342         synchronized (mLock) {
343             mCallbacks.remove(callback);
344             try {
345                 List<CarPropertyConfig> configs = getPropertyList();
346                 for (CarPropertyConfig c : configs) {
347                     // Register each individual propertyId
348                     mCarPropertyMgr.unregisterCallback(mListenerToBase, c.getPropertyId());
349 
350                 }
351             } catch (RuntimeException e) {
352                 Slog.e(TAG, "getPropertyList exception ", e);
353             }
354             if (mCallbacks.isEmpty()) {
355                 mCarPropertyMgr.unregisterCallback(mListenerToBase);
356                 mListenerToBase = null;
357             }
358         }
359     }
360 
361     /**
362      * Get list of properties represented by Car Hvac Manager for this car.
363      * @return List of CarPropertyConfig objects available via Car Hvac Manager.
364      */
getPropertyList()365     public List<CarPropertyConfig> getPropertyList() {
366         return mCarPropertyMgr.getPropertyList(mHvacPropertyIds);
367     }
368 
369     /**
370      * Check whether a given property is available or disabled based on the cars current state.
371      * @return true if the property is AVAILABLE, false otherwise
372      */
isPropertyAvailable(@ropertyId int propertyId, int area)373     public boolean isPropertyAvailable(@PropertyId int propertyId, int area) {
374         return mCarPropertyMgr.isPropertyAvailable(propertyId, area);
375     }
376 
377     /**
378      * Get value of boolean property
379      * @param propertyId
380      * @param area
381      * @return value of requested boolean property
382      */
getBooleanProperty(@ropertyId int propertyId, int area)383     public boolean getBooleanProperty(@PropertyId int propertyId, int area) {
384         return mCarPropertyMgr.getBooleanProperty(propertyId, area);
385     }
386 
387     /**
388      * Get value of float property
389      * @param propertyId
390      * @param area
391      * @return value of requested float property
392      */
getFloatProperty(@ropertyId int propertyId, int area)393     public float getFloatProperty(@PropertyId int propertyId, int area) {
394         return mCarPropertyMgr.getFloatProperty(propertyId, area);
395     }
396 
397     /**
398      * Get value of integer property
399      * @param propertyId
400      * @param area
401      * @return value of requested integer property
402      */
getIntProperty(@ropertyId int propertyId, int area)403     public int getIntProperty(@PropertyId int propertyId, int area) {
404         return mCarPropertyMgr.getIntProperty(propertyId, area);
405     }
406 
407     /**
408      * Set the value of a boolean property
409      * @param propertyId
410      * @param area
411      * @param val
412      */
setBooleanProperty(@ropertyId int propertyId, int area, boolean val)413     public void setBooleanProperty(@PropertyId int propertyId, int area, boolean val) {
414         if (mHvacPropertyIds.contains(propertyId)) {
415             mCarPropertyMgr.setBooleanProperty(propertyId, area, val);
416         }
417     }
418 
419     /**
420      * Set the value of a float property
421      * @param propertyId
422      * @param area
423      * @param val
424      */
setFloatProperty(@ropertyId int propertyId, int area, float val)425     public void setFloatProperty(@PropertyId int propertyId, int area, float val) {
426         if (mHvacPropertyIds.contains(propertyId)) {
427             mCarPropertyMgr.setFloatProperty(propertyId, area, val);
428         }
429     }
430 
431     /**
432      * Set the value of an integer property
433      * @param propertyId
434      * @param area
435      * @param val
436      */
setIntProperty(@ropertyId int propertyId, int area, int val)437     public void setIntProperty(@PropertyId int propertyId, int area, int val) {
438         if (mHvacPropertyIds.contains(propertyId)) {
439             mCarPropertyMgr.setIntProperty(propertyId, area, val);
440         }
441     }
442 
443     /** @hide */
onCarDisconnected()444     public void onCarDisconnected() {
445         synchronized (mLock) {
446             mCallbacks.clear();
447         }
448         mCarPropertyMgr.onCarDisconnected();
449     }
450 }
451