blob: 81d61918875d767c4611a0b38dd03cef50a5a751 [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
19import android.car.Car;
20import android.car.CarNotConnectedException;
21import android.car.hardware.CarPropertyValue;
22import android.car.hardware.hvac.CarHvacManager;
23import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.ServiceConnection;
27import android.os.Handler;
28import android.os.IBinder;
29import android.util.Log;
30
Brad Stenning2f3444f2018-04-18 10:28:24 -070031import java.util.ArrayList;
Brad Stenning38b46f82018-03-27 13:57:29 -070032import java.util.HashMap;
33import java.util.Iterator;
Brad Stenning2f3444f2018-04-18 10:28:24 -070034import java.util.List;
Brad Stenning38b46f82018-03-27 13:57:29 -070035import java.util.Map;
36import java.util.Objects;
37
38/**
39 * Manages the connection to the Car service and delegates value changes to the registered
40 * {@link TemperatureView}s
41 */
42public class HvacController {
43
44 public static final String TAG = "HvacController";
45 public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
46
47 private Context mContext;
48 private Handler mHandler;
49 private Car mCar;
50 private CarHvacManager mHvacManager;
Brad Stenning2f3444f2018-04-18 10:28:24 -070051 private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
Brad Stenning38b46f82018-03-27 13:57:29 -070052
53 public HvacController(Context context) {
54 mContext = context;
55 }
56
57 /**
58 * Create connection to the Car service. Note: call backs from the Car service
59 * ({@link CarHvacManager}) will happen on the same thread this method was called from.
60 */
61 public void connectToCarService() {
62 mHandler = new Handler();
63 mCar = Car.createCar(mContext, mServiceConnection, mHandler);
64 if (mCar != null) {
65 // note: this connect call handles the retries
66 mCar.connect();
67 }
68 }
69
70 /**
71 * Registers callbacks and initializes components upon connection.
72 */
73 private ServiceConnection mServiceConnection = new ServiceConnection() {
74 @Override
75 public void onServiceConnected(ComponentName name, IBinder service) {
76 try {
77 service.linkToDeath(mRestart, 0);
78 mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
79 mHvacManager.registerCallback(mHardwareCallback);
80 initComponents();
81 } catch (Exception e) {
82 Log.e(TAG, "Failed to correctly connect to HVAC", e);
83 }
84 }
85
86 @Override
87 public void onServiceDisconnected(ComponentName name) {
88 destroyHvacManager();
89 }
90 };
91
92 private void destroyHvacManager() {
93 if (mHvacManager != null) {
94 mHvacManager.unregisterCallback(mHardwareCallback);
95 mHvacManager = null;
96 }
97 }
98
99 /**
100 * If the connection to car service goes away then restart it.
101 */
102 private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
103 @Override
104 public void binderDied() {
105 Log.d(TAG, "Death of HVAC triggering a restart");
106 if (mCar != null) {
107 mCar.disconnect();
108 }
109 destroyHvacManager();
110 mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
111 }
112 };
113
114 /**
115 * Add component to list and initialize it if the connection is up.
116 * @param temperatureView
117 */
118 public void addHvacTextView(TemperatureView temperatureView) {
Brad Stenning2f3444f2018-04-18 10:28:24 -0700119
120 HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
121 if (!mTempComponents.containsKey(hvacKey)) {
122 mTempComponents.put(hvacKey, new ArrayList<>());
123 }
124 mTempComponents.get(hvacKey).add(temperatureView);
Brad Stenning38b46f82018-03-27 13:57:29 -0700125 initComponent(temperatureView);
126 }
127
128 private void initComponents() {
Brad Stenning2f3444f2018-04-18 10:28:24 -0700129 Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
Brad Stenning38b46f82018-03-27 13:57:29 -0700130 mTempComponents.entrySet().iterator();
131 while (iterator.hasNext()) {
Brad Stenning2f3444f2018-04-18 10:28:24 -0700132 Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
133 List<TemperatureView> temperatureViews = next.getValue();
134 for (TemperatureView view : temperatureViews) {
135 initComponent(view);
136 }
Brad Stenning38b46f82018-03-27 13:57:29 -0700137 }
138 }
139
140
141 private void initComponent(TemperatureView view) {
142 int id = view.getPropertyId();
143 int zone = view.getAreaId();
144 try {
145 if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
146 view.setTemp(Float.NaN);
147 return;
148 }
149 view.setTemp(mHvacManager.getFloatProperty(id, zone));
150 } catch (CarNotConnectedException e) {
151 view.setTemp(Float.NaN);
152 Log.e(TAG, "Failed to get value from hvac service", e);
153 }
154 }
155
156 /**
157 * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
158 * match.
159 */
160 private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
161 @Override
162 public void onChangeEvent(final CarPropertyValue val) {
163 try {
164 int areaId = val.getAreaId();
165 int propertyId = val.getPropertyId();
Brad Stenning2f3444f2018-04-18 10:28:24 -0700166 List<TemperatureView> temperatureViews = mTempComponents.get(
Brad Stenning38b46f82018-03-27 13:57:29 -0700167 new HvacKey(propertyId, areaId));
Brad Stenning2f3444f2018-04-18 10:28:24 -0700168 if (temperatureViews != null && !temperatureViews.isEmpty()) {
Brad Stenning38b46f82018-03-27 13:57:29 -0700169 float value = (float) val.getValue();
Brad Stenning2f3444f2018-04-18 10:28:24 -0700170 for (TemperatureView tempView : temperatureViews) {
171 tempView.setTemp(value);
172 }
Brad Stenning38b46f82018-03-27 13:57:29 -0700173 } // else the data is not of interest
174 } catch (Exception e) {
175 // catch all so we don't take down the sysui if a new data type is
176 // introduced.
177 Log.e(TAG, "Failed handling hvac change event", e);
178 }
179 }
180
181 @Override
182 public void onErrorEvent(final int propertyId, final int zone) {
183 Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
184 " zone: " + zone);
185 }
186 };
187
188 /**
Brad Stenning224b5b32018-03-28 21:26:57 -0700189 * Removes all registered components. This is useful if you need to rebuild the UI since
190 * components self register.
191 */
192 public void removeAllComponents() {
193 mTempComponents.clear();
194 }
195
196 /**
Brad Stenning38b46f82018-03-27 13:57:29 -0700197 * Key for storing {@link TemperatureView}s in a hash map
198 */
199 private static class HvacKey {
200
201 int mPropertyId;
202 int mAreaId;
203
204 public HvacKey(int propertyId, int areaId) {
205 mPropertyId = propertyId;
206 mAreaId = areaId;
207 }
208
209 @Override
210 public boolean equals(Object o) {
211 if (this == o) return true;
212 if (o == null || getClass() != o.getClass()) return false;
213 HvacKey hvacKey = (HvacKey) o;
214 return mPropertyId == hvacKey.mPropertyId &&
215 mAreaId == hvacKey.mAreaId;
216 }
217
218 @Override
219 public int hashCode() {
220 return Objects.hash(mPropertyId, mAreaId);
221 }
222 }
223}