blob: 30429eda7be72082d6735f23192ab49285b01b8e [file] [log] [blame]
Brad Stenning38b46f82018-03-27 13:57:29 -07001/*
2 * Copyright (C) 2018 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 com.android.systemui.statusbar.car.hvac;
18
Brad Stenning1cd11252018-10-12 07:25:28 -070019import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
20import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
21
Brad Stenning38b46f82018-03-27 13:57:29 -070022import android.car.Car;
Brad Stenning1cd11252018-10-12 07:25:28 -070023import android.car.VehicleUnit;
Brad Stenning38b46f82018-03-27 13:57:29 -070024import android.car.hardware.CarPropertyValue;
25import android.car.hardware.hvac.CarHvacManager;
26import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.ServiceConnection;
30import android.os.Handler;
31import android.os.IBinder;
32import android.util.Log;
33
Brad Stenning2f3444f2018-04-18 10:28:24 -070034import java.util.ArrayList;
Brad Stenning38b46f82018-03-27 13:57:29 -070035import java.util.HashMap;
36import java.util.Iterator;
Brad Stenning2f3444f2018-04-18 10:28:24 -070037import java.util.List;
Brad Stenning38b46f82018-03-27 13:57:29 -070038import java.util.Map;
39import java.util.Objects;
40
41/**
42 * Manages the connection to the Car service and delegates value changes to the registered
43 * {@link TemperatureView}s
44 */
45public class HvacController {
46
47 public static final String TAG = "HvacController";
Brad Stenning8d1a51c2018-11-20 17:34:16 -080048 public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
Brad Stenning38b46f82018-03-27 13:57:29 -070049
50 private Context mContext;
51 private Handler mHandler;
52 private Car mCar;
53 private CarHvacManager mHvacManager;
Brad Stenning2f3444f2018-04-18 10:28:24 -070054 private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
Brad Stenning38b46f82018-03-27 13:57:29 -070055 /**
56 * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
57 * match.
58 */
59 private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
60 @Override
61 public void onChangeEvent(final CarPropertyValue val) {
62 try {
63 int areaId = val.getAreaId();
64 int propertyId = val.getPropertyId();
Brad Stenning2f3444f2018-04-18 10:28:24 -070065 List<TemperatureView> temperatureViews = mTempComponents.get(
Brad Stenning38b46f82018-03-27 13:57:29 -070066 new HvacKey(propertyId, areaId));
Brad Stenning2f3444f2018-04-18 10:28:24 -070067 if (temperatureViews != null && !temperatureViews.isEmpty()) {
Brad Stenning38b46f82018-03-27 13:57:29 -070068 float value = (float) val.getValue();
Brad Stenning2f3444f2018-04-18 10:28:24 -070069 for (TemperatureView tempView : temperatureViews) {
70 tempView.setTemp(value);
71 }
Brad Stenning38b46f82018-03-27 13:57:29 -070072 } // else the data is not of interest
73 } catch (Exception e) {
74 // catch all so we don't take down the sysui if a new data type is
75 // introduced.
76 Log.e(TAG, "Failed handling hvac change event", e);
77 }
78 }
79
80 @Override
81 public void onErrorEvent(final int propertyId, final int zone) {
Brad Stenning8d1a51c2018-11-20 17:34:16 -080082 Log.d(TAG, "HVAC error event, propertyId: " + propertyId
83 + " zone: " + zone);
Brad Stenning38b46f82018-03-27 13:57:29 -070084 }
85 };
Brad Stenning8d1a51c2018-11-20 17:34:16 -080086 /**
87 * If the connection to car service goes away then restart it.
88 */
89 private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
90 @Override
91 public void binderDied() {
92 Log.d(TAG, "Death of HVAC triggering a restart");
93 if (mCar != null) {
94 mCar.disconnect();
95 }
96 destroyHvacManager();
97 mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
98 }
99 };
100 /**
101 * Registers callbacks and initializes components upon connection.
102 */
103 private ServiceConnection mServiceConnection = new ServiceConnection() {
104 @Override
105 public void onServiceConnected(ComponentName name, IBinder service) {
106 try {
107 service.linkToDeath(mRestart, 0);
108 mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
109 mHvacManager.registerCallback(mHardwareCallback);
110 initComponents();
111 } catch (Exception e) {
112 Log.e(TAG, "Failed to correctly connect to HVAC", e);
113 }
114 }
115
116 @Override
117 public void onServiceDisconnected(ComponentName name) {
118 destroyHvacManager();
119 }
120 };
121
122 public HvacController(Context context) {
123 mContext = context;
124 }
125
126 /**
127 * Create connection to the Car service. Note: call backs from the Car service
128 * ({@link CarHvacManager}) will happen on the same thread this method was called from.
129 */
130 public void connectToCarService() {
131 mHandler = new Handler();
132 mCar = Car.createCar(mContext, mServiceConnection, mHandler);
133 if (mCar != null) {
134 // note: this connect call handles the retries
135 mCar.connect();
136 }
137 }
138
139 private void destroyHvacManager() {
140 if (mHvacManager != null) {
141 mHvacManager.unregisterCallback(mHardwareCallback);
142 mHvacManager = null;
143 }
144 }
145
146 /**
147 * Add component to list and initialize it if the connection is up.
148 */
149 public void addHvacTextView(TemperatureView temperatureView) {
150
151 HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
152 if (!mTempComponents.containsKey(hvacKey)) {
153 mTempComponents.put(hvacKey, new ArrayList<>());
154 }
155 mTempComponents.get(hvacKey).add(temperatureView);
156 initComponent(temperatureView);
157 }
158
159 private void initComponents() {
160 Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
161 mTempComponents.entrySet().iterator();
162 while (iterator.hasNext()) {
163 Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
164 List<TemperatureView> temperatureViews = next.getValue();
165 for (TemperatureView view : temperatureViews) {
166 initComponent(view);
167 }
168 }
169 }
170
171 private void initComponent(TemperatureView view) {
172 int id = view.getPropertyId();
173 int zone = view.getAreaId();
Brad Stenning1cd11252018-10-12 07:25:28 -0700174
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800175 try {
Brad Stenning1cd11252018-10-12 07:25:28 -0700176 if (mHvacManager != null
177 && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
178 VEHICLE_AREA_TYPE_GLOBAL)) {
179 if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
180 VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
181 view.setDisplayInFahrenheit(true);
182 }
183
184 }
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800185 if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
186 view.setTemp(Float.NaN);
187 return;
188 }
189 view.setTemp(mHvacManager.getFloatProperty(id, zone));
190 } catch (Exception e) {
191 view.setTemp(Float.NaN);
192 Log.e(TAG, "Failed to get value from hvac service", e);
193 }
194 }
Brad Stenning38b46f82018-03-27 13:57:29 -0700195
196 /**
Brad Stenning224b5b32018-03-28 21:26:57 -0700197 * Removes all registered components. This is useful if you need to rebuild the UI since
198 * components self register.
199 */
200 public void removeAllComponents() {
201 mTempComponents.clear();
202 }
203
204 /**
Brad Stenning38b46f82018-03-27 13:57:29 -0700205 * Key for storing {@link TemperatureView}s in a hash map
206 */
207 private static class HvacKey {
208
209 int mPropertyId;
210 int mAreaId;
211
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800212 private HvacKey(int propertyId, int areaId) {
Brad Stenning38b46f82018-03-27 13:57:29 -0700213 mPropertyId = propertyId;
214 mAreaId = areaId;
215 }
216
217 @Override
218 public boolean equals(Object o) {
219 if (this == o) return true;
220 if (o == null || getClass() != o.getClass()) return false;
221 HvacKey hvacKey = (HvacKey) o;
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800222 return mPropertyId == hvacKey.mPropertyId
223 && mAreaId == hvacKey.mAreaId;
Brad Stenning38b46f82018-03-27 13:57:29 -0700224 }
225
226 @Override
227 public int hashCode() {
228 return Objects.hash(mPropertyId, mAreaId);
229 }
230 }
231}