• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.android.car.hal;
18 
19 import static android.car.VehiclePropertyIds.CLUSTER_DISPLAY_STATE;
20 import static android.car.VehiclePropertyIds.CLUSTER_NAVIGATION_STATE;
21 import static android.car.VehiclePropertyIds.CLUSTER_REPORT_STATE;
22 import static android.car.VehiclePropertyIds.CLUSTER_REQUEST_DISPLAY;
23 import static android.car.VehiclePropertyIds.CLUSTER_SWITCH_UI;
24 
25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
26 
27 import android.annotation.NonNull;
28 import android.car.builtin.util.Slogf;
29 import android.graphics.Insets;
30 import android.graphics.Rect;
31 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
32 import android.os.ServiceSpecificException;
33 import android.os.SystemClock;
34 
35 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
36 import com.android.car.internal.util.IntArray;
37 import com.android.internal.annotations.GuardedBy;
38 
39 import java.io.PrintWriter;
40 import java.util.Collection;
41 import java.util.List;
42 
43 /**
44  * Translates HAL input events to higher-level semantic information.
45  */
46 public final class ClusterHalService extends HalServiceBase {
47     private static final String TAG = ClusterHalService.class.getSimpleName();
48     private static final boolean DBG = false;
49     public static final int DISPLAY_OFF = 0;
50     public static final int DISPLAY_ON = 1;
51     public static final int DONT_CARE = -1;
52 
53     /**
54      * Interface to receive incoming Cluster HAL events.
55      */
56     public interface ClusterHalEventCallback {
57         /**
58          * Called when CLUSTER_SWITCH_UI message is received.
59          *
60          * @param uiType uiType ClusterOS wants to switch to
61          */
onSwitchUi(int uiType)62         void onSwitchUi(int uiType);
63 
64         /**
65          * Called when CLUSTER_DISPLAY_STATE message is received.
66          *
67          * @param onOff 0 - off, 1 - on
68          * @param bounds the area to render the cluster Activity in pixel
69          * @param insets Insets of the cluster display
70          */
onDisplayState(int onOff, Rect bounds, Insets insets)71         void onDisplayState(int onOff, Rect bounds, Insets insets);
72     }
73 
74     ;
75 
76     private static final int[] SUPPORTED_PROPERTIES = new int[]{
77             CLUSTER_SWITCH_UI,
78             CLUSTER_DISPLAY_STATE,
79             CLUSTER_REPORT_STATE,
80             CLUSTER_REQUEST_DISPLAY,
81             CLUSTER_NAVIGATION_STATE,
82     };
83 
84     private static final int[] CORE_PROPERTIES = new int[]{
85             CLUSTER_SWITCH_UI,
86             CLUSTER_REPORT_STATE,
87             CLUSTER_DISPLAY_STATE,
88             CLUSTER_REQUEST_DISPLAY,
89     };
90 
91     private static final int[] SUBSCRIBABLE_PROPERTIES = new int[]{
92             CLUSTER_SWITCH_UI,
93             CLUSTER_DISPLAY_STATE,
94     };
95 
96     private final Object mLock = new Object();
97 
98     @GuardedBy("mLock")
99     private ClusterHalEventCallback mCallback;
100 
101     private final VehicleHal mHal;
102 
103     private volatile boolean mIsCoreSupported;
104     private volatile boolean mIsNavigationStateSupported;
105 
106     private final HalPropValueBuilder mPropValueBuilder;
107 
ClusterHalService(VehicleHal hal)108     public ClusterHalService(VehicleHal hal) {
109         mHal = hal;
110         mPropValueBuilder = hal.getHalPropValueBuilder();
111     }
112 
113     @Override
init()114     public void init() {
115         Slogf.d(TAG, "initClusterHalService");
116         if (!isCoreSupported()) return;
117 
118         for (int property : SUBSCRIBABLE_PROPERTIES) {
119             mHal.subscribeProperty(this, property);
120         }
121     }
122 
123     @Override
release()124     public void release() {
125         Slogf.d(TAG, "releaseClusterHalService");
126         synchronized (mLock) {
127             mCallback = null;
128         }
129     }
130 
131     /**
132      * Sets the event callback to receive Cluster HAL events.
133      */
setCallback(ClusterHalEventCallback callback)134     public void setCallback(ClusterHalEventCallback callback) {
135         synchronized (mLock) {
136             mCallback = callback;
137         }
138     }
139 
140     @NonNull
141     @Override
getAllSupportedProperties()142     public int[] getAllSupportedProperties() {
143         return SUPPORTED_PROPERTIES;
144     }
145 
146     @Override
takeProperties(@onNull Collection<HalPropConfig> properties)147     public void takeProperties(@NonNull Collection<HalPropConfig> properties) {
148         IntArray supportedProperties = new IntArray(properties.size());
149         for (HalPropConfig property : properties) {
150             supportedProperties.add(property.getPropId());
151         }
152         mIsCoreSupported = true;
153         for (int coreProperty : CORE_PROPERTIES) {
154             if (supportedProperties.indexOf(coreProperty) < 0) {
155                 mIsCoreSupported = false;
156                 break;
157             }
158         }
159         mIsNavigationStateSupported = supportedProperties.indexOf(CLUSTER_NAVIGATION_STATE) >= 0;
160         Slogf.d(TAG, "takeProperties: coreSupported=%s, navigationStateSupported=%s",
161                 mIsCoreSupported, mIsNavigationStateSupported);
162     }
163 
isCoreSupported()164     public boolean isCoreSupported() {
165         return mIsCoreSupported;
166     }
167 
isNavigationStateSupported()168     public boolean isNavigationStateSupported() {
169         return mIsNavigationStateSupported;
170     }
171 
172     @Override
onHalEvents(List<HalPropValue> values)173     public void onHalEvents(List<HalPropValue> values) {
174         Slogf.d(TAG, "handleHalEvents(): %s", values);
175         ClusterHalEventCallback callback;
176         synchronized (mLock) {
177             callback = mCallback;
178         }
179         if (callback == null || !isCoreSupported()) {
180             return;
181         }
182 
183         for (HalPropValue value : values) {
184             switch (value.getPropId()) {
185                 case CLUSTER_SWITCH_UI:
186                     if (value.getInt32ValuesSize() < 1) {
187                         Slogf.e(TAG, "received invalid CLUSTER_SWITCH_UI property from HAL, "
188                                 + "expect at least 1 int value.");
189                         break;
190                     }
191                     int uiType = value.getInt32Value(0);
192                     callback.onSwitchUi(uiType);
193                     break;
194                 case CLUSTER_DISPLAY_STATE:
195                     if (value.getInt32ValuesSize() < 9) {
196                         Slogf.e(TAG, "received invalid CLUSTER_DISPLAY_STATE property from HAL, "
197                                 + "expect at least 9 int value.");
198                         break;
199                     }
200                     int onOff = value.getInt32Value(0);
201                     Rect bounds = null;
202                     if (hasNoDontCare(value, /* start= */ 1, /* length= */ 4, "bounds")) {
203                         bounds =
204                                 new Rect(
205                                         value.getInt32Value(1), value.getInt32Value(2),
206                                         value.getInt32Value(3), value.getInt32Value(4));
207                     }
208                     Insets insets = null;
209                     if (hasNoDontCare(value, /* start= */ 5, /* length= */ 4, "insets")) {
210                         insets =
211                                 Insets.of(
212                                         value.getInt32Value(5), value.getInt32Value(6),
213                                         value.getInt32Value(7), value.getInt32Value(8));
214                     }
215                     callback.onDisplayState(onOff, bounds, insets);
216                     break;
217                 default:
218                     Slogf.w(TAG, "received unsupported event from HAL: %s", value);
219             }
220         }
221     }
222 
hasNoDontCare(HalPropValue value, int start, int length, String fieldName)223     private static boolean hasNoDontCare(HalPropValue value, int start, int length,
224                                          String fieldName) {
225         int count = 0;
226         for (int i = start; i < start + length; ++i) {
227             if (value.getInt32Value(i) == DONT_CARE) {
228                 ++count;
229             }
230         }
231         if (count == 0) {
232             return true;
233         }
234         if (count != length) {
235             Slogf.w(TAG, "Don't care should be set in the whole %s.", fieldName);
236         }
237         return false;
238     }
239 
240     /**
241      * Reports the current display state and ClusterUI state.
242      *
243      * @param onOff 0 - off, 1 - on
244      * @param bounds the area to render the cluster Activity in pixel
245      * @param insets Insets of the cluster display
246      * @param uiTypeMain uiType that ClusterHome tries to show in main area
247      * @param uiTypeSub uiType that ClusterHome tries to show in sub area
248      * @param uiAvailability the byte array to represent the availability of ClusterUI.
249      */
reportState(int onOff, Rect bounds, Insets insets, int uiTypeMain, int uiTypeSub, byte[] uiAvailability)250     public void reportState(int onOff, Rect bounds, Insets insets,
251             int uiTypeMain, int uiTypeSub, byte[] uiAvailability) {
252         if (!isCoreSupported()) {
253             return;
254         }
255         int[] intValues = new int[]{
256             onOff,
257             bounds.left,
258             bounds.top,
259             bounds.right,
260             bounds.bottom,
261             insets.left,
262             insets.top,
263             insets.right,
264             insets.bottom,
265             uiTypeMain,
266             uiTypeSub
267         };
268         HalPropValue request = mPropValueBuilder.build(CLUSTER_REPORT_STATE,
269                 /* areaId= */ 0, SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE,
270                 /* int32Values= */ intValues, /* floatValues= */ new float[0],
271                 /* int64Values= */ new long[0], /* stringValue= */ new String(),
272                 /* byteValues= */ uiAvailability);
273         send(request);
274     }
275 
276     /**
277      * Requests to turn the cluster display on to show some ClusterUI.
278      *
279      * @param uiType uiType that ClusterHome tries to show in main area
280      */
requestDisplay(int uiType)281     public void requestDisplay(int uiType) {
282         if (!isCoreSupported()) {
283             return;
284         }
285         HalPropValue request = mPropValueBuilder.build(CLUSTER_REQUEST_DISPLAY,
286                 /* areaId= */ 0, SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE,
287                 /* value= */ uiType);
288         send(request);
289     }
290 
291 
292     /**
293      * Informs the current navigation state.
294      *
295      * @param navigateState the serialized message of {@code NavigationStateProto}
296      */
sendNavigationState(byte[] navigateState)297     public void sendNavigationState(byte[] navigateState) {
298         if (!isNavigationStateSupported()) {
299             return;
300         }
301         HalPropValue request = mPropValueBuilder.build(CLUSTER_NAVIGATION_STATE,
302                 /* areaId= */ 0, SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE,
303                 /* value= */ navigateState);
304         send(request);
305     }
306 
send(HalPropValue request)307     private void send(HalPropValue request) {
308         try {
309             mHal.set(request);
310         } catch (ServiceSpecificException | IllegalArgumentException e) {
311             Slogf.e(TAG, "Failed to send request: " + request, e);
312         }
313     }
314 
315     @Override
316     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)317     public void dump(PrintWriter writer) {
318         writer.println("*Cluster HAL*");
319         writer.println("mIsCoreSupported:" + isCoreSupported());
320         writer.println("mIsNavigationStateSupported:" + isNavigationStateSupported());
321     }
322 }
323