1 /*
2  * Copyright (C) 2024 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 com.google.android.car.kitchensink;
18 
19 import android.car.Car;
20 import android.car.CarOccupantZoneManager;
21 import android.car.CarProjectionManager;
22 import android.car.hardware.CarSensorManager;
23 import android.car.hardware.hvac.CarHvacManager;
24 import android.car.hardware.power.CarPowerManager;
25 import android.car.hardware.property.CarPropertyManager;
26 import android.car.os.CarPerformanceManager;
27 import android.car.telemetry.CarTelemetryManager;
28 import android.car.watchdog.CarWatchdogManager;
29 import android.content.Context;
30 import android.content.pm.PackageManager;
31 import android.os.Handler;
32 import android.util.Log;
33 
34 import androidx.annotation.GuardedBy;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 public class KitchenSinkHelperImpl implements KitchenSinkHelper {
40     private static final String TAG = "KitchenSinkHelperImpl";
41 
42     private final Object mLock = new Object();
43     @GuardedBy("mLock")
44     private Car mCarApi;
45     @GuardedBy("mLock")
46     private CarHvacManager mHvacManager;
47     @GuardedBy("mLock")
48     private CarOccupantZoneManager mOccupantZoneManager;
49     @GuardedBy("mLock")
50     private CarPowerManager mPowerManager;
51     @GuardedBy("mLock")
52     private CarPropertyManager mPropertyManager;
53     @GuardedBy("mLock")
54     private CarSensorManager mSensorManager;
55     @GuardedBy("mLock")
56     private CarProjectionManager mCarProjectionManager;
57     @GuardedBy("mLock")
58     private CarTelemetryManager mCarTelemetryManager;
59     @GuardedBy("mLock")
60     private CarWatchdogManager mCarWatchdogManager;
61     @GuardedBy("mLock")
62     private CarPerformanceManager mCarPerformanceManager;
63     @GuardedBy("mLock")
64     private boolean mCarReady = false;
65 
HandlerRunnable(Handler h, Runnable r)66     private record HandlerRunnable(Handler h, Runnable r) {}
67 
68     // A list of callbacks that would be posted to handlers every time car service is connected
69     // or reconnected.
70     @GuardedBy("mLock")
71     private final List<HandlerRunnable> mCarReadyCallbacks = new ArrayList<>();
72 
73     @Override
getCar()74     public Car getCar() {
75         synchronized (mLock) {
76             return mCarApi;
77         }
78     }
79 
80     @Override
requestRefreshManager(final Runnable r, final Handler h)81     public void requestRefreshManager(final Runnable r, final Handler h) {
82         synchronized (mLock) {
83             mCarReadyCallbacks.add(new HandlerRunnable(h, r));
84 
85             if (mCarReady) {
86                 // If car service is already ready, we already missed the initial callback so
87                 // we invoke the runnable now.
88                 h.post(r);
89             }
90         }
91     }
92 
93     @Override
getPropertyManager()94     public CarPropertyManager getPropertyManager() {
95         synchronized (mLock) {
96             if (mPropertyManager == null) {
97                 mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
98                         android.car.Car.PROPERTY_SERVICE);
99             }
100             return mPropertyManager;
101         }
102     }
103 
104     @Override
getHvacManager()105     public CarHvacManager getHvacManager() {
106         synchronized (mLock) {
107             if (mHvacManager == null) {
108                 mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
109             }
110             return mHvacManager;
111         }
112     }
113 
114     @Override
getOccupantZoneManager()115     public CarOccupantZoneManager getOccupantZoneManager() {
116         synchronized (mLock) {
117             if (mOccupantZoneManager == null) {
118                 mOccupantZoneManager = (CarOccupantZoneManager) mCarApi.getCarManager(
119                         android.car.Car.CAR_OCCUPANT_ZONE_SERVICE);
120             }
121             return mOccupantZoneManager;
122         }
123     }
124 
125     @Override
getPowerManager()126     public CarPowerManager getPowerManager() {
127         synchronized (mLock) {
128             if (mPowerManager == null) {
129                 mPowerManager = (CarPowerManager) mCarApi.getCarManager(
130                         android.car.Car.POWER_SERVICE);
131             }
132             return mPowerManager;
133         }
134     }
135 
136     @Override
getSensorManager()137     public CarSensorManager getSensorManager() {
138         synchronized (mLock) {
139             if (mSensorManager == null) {
140                 mSensorManager = (CarSensorManager) mCarApi.getCarManager(
141                         android.car.Car.SENSOR_SERVICE);
142             }
143             return mSensorManager;
144         }
145     }
146 
147     @Override
getProjectionManager()148     public CarProjectionManager getProjectionManager() {
149         synchronized (mLock) {
150             if (mCarProjectionManager == null) {
151                 mCarProjectionManager =
152                         (CarProjectionManager) mCarApi.getCarManager(Car.PROJECTION_SERVICE);
153             }
154             return mCarProjectionManager;
155         }
156     }
157 
158     @Override
getCarTelemetryManager()159     public CarTelemetryManager getCarTelemetryManager() {
160         synchronized (mLock) {
161             if (mCarTelemetryManager == null) {
162                 mCarTelemetryManager =
163                         (CarTelemetryManager) mCarApi.getCarManager(Car.CAR_TELEMETRY_SERVICE);
164             }
165             return mCarTelemetryManager;
166         }
167     }
168 
169     @Override
getCarWatchdogManager()170     public CarWatchdogManager getCarWatchdogManager() {
171         synchronized (mLock) {
172             if (mCarWatchdogManager == null) {
173                 mCarWatchdogManager =
174                         (CarWatchdogManager) mCarApi.getCarManager(Car.CAR_WATCHDOG_SERVICE);
175             }
176             return mCarWatchdogManager;
177         }
178     }
179 
180     @Override
getPerformanceManager()181     public CarPerformanceManager getPerformanceManager() {
182         synchronized (mLock) {
183             if (mCarPerformanceManager == null) {
184                 mCarPerformanceManager =
185                         (CarPerformanceManager) mCarApi.getCarManager(Car.CAR_PERFORMANCE_SERVICE);
186             }
187             return mCarPerformanceManager;
188         }
189     }
190 
191     /**
192      * Initialized Car API if in automotive. Must be called during onCreate.
193      */
initCarApiIfAutomotive(Context context)194     public void initCarApiIfAutomotive(Context context) {
195         // Connection to Car Service does not work for non-automotive yet.
196         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
197             initCarApi(context);
198         }
199     }
200 
201     /**
202      * Disconnects from car service. Must be called during onDestroy.
203      */
disconnect()204     public void disconnect() {
205         synchronized (mLock) {
206             if (mCarApi != null) {
207                 mCarApi.disconnect();
208                 mCarApi = null;
209             }
210         }
211     }
212 
initCarApi(Context context)213     private void initCarApi(Context context) {
214         synchronized (mLock) {
215             if (mCarApi != null && mCarApi.isConnected()) {
216                 mCarApi.disconnect();
217                 mCarApi = null;
218             }
219         }
220         Car.createCar(context, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
221                 (Car car, boolean ready) -> {
222                     synchronized (mLock) {
223                         mCarApi = car;
224                         mCarReady = ready;
225                         if (!mCarReady) {
226                             return;
227                         }
228 
229                         Log.d(TAG, "Car service connected");
230 
231                         mHvacManager = null;
232                         mOccupantZoneManager = null;
233                         mPowerManager = null;
234                         mPropertyManager = null;
235                         mSensorManager = null;
236                         mCarProjectionManager = null;
237                         mCarTelemetryManager = null;
238                         mCarWatchdogManager = null;
239                         mCarPerformanceManager = null;
240 
241                         for (int i = 0; i < mCarReadyCallbacks.size(); i++) {
242                             var handlerRunnable = mCarReadyCallbacks.get(i);
243                             handlerRunnable.h.post(handlerRunnable.r);
244                         }
245                     }
246                 });
247     }
248 }
249