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
17package android.car.hardware.hvac;
18
19import android.annotation.IntDef;
20import android.annotation.SystemApi;
21import android.car.Car;
22import android.car.CarManagerBase;
23import android.car.CarNotConnectedException;
24import android.car.hardware.CarPropertyConfig;
25import android.car.hardware.CarPropertyValue;
26import android.car.hardware.property.CarPropertyManagerBase;
27import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventCallback;
28import android.content.Context;
29import android.os.Handler;
30import android.os.IBinder;
31import android.util.ArraySet;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.lang.ref.WeakReference;
36import java.util.Collection;
37import java.util.List;
38
39/**
40 * API for controlling HVAC system in cars
41 * @hide
42 */
43@SystemApi
44public 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         */
230        void onChangeEvent(CarPropertyValue value);
231
232        /**
233         * Called when an error is detected with a property
234         * @param propertyId
235         * @param zone
236         */
237        void onErrorEvent(@PropertyId int propertyId, int zone);
238    }
239
240    private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
241        private final WeakReference<CarHvacManager> mManager;
242
243        public CarPropertyEventListenerToBase(CarHvacManager manager) {
244            mManager = new WeakReference<>(manager);
245        }
246
247        @Override
248        public void onChangeEvent(CarPropertyValue value) {
249            CarHvacManager manager = mManager.get();
250            if (manager != null) {
251                manager.handleOnChangeEvent(value);
252            }
253        }
254
255        @Override
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
420    public void onCarDisconnected() {
421        mMgr.onCarDisconnected();
422    }
423}
424