• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.os.SystemClock.elapsedRealtime;
20 
21 import android.hardware.automotive.vehicle.V2_0.IVehicle;
22 import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
23 import android.hardware.automotive.vehicle.V2_0.StatusCode;
24 import android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
25 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
26 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import com.android.car.CarLog;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 
38 /**
39  * Vehicle HAL client. Interacts directly with Vehicle HAL interface {@link IVehicle}. Contains
40  * some logic for retriable properties, redirects Vehicle notifications into given looper thread.
41  */
42 class  HalClient {
43     /**
44      * If call to vehicle HAL returns StatusCode.TRY_AGAIN, than {@link HalClient} will retry to
45      * invoke that method again for this amount of milliseconds.
46      */
47     private static final int WAIT_CAP_FOR_RETRIABLE_RESULT_MS = 2000;
48 
49     private static final int SLEEP_BETWEEN_RETRIABLE_INVOKES_MS = 50;
50 
51     private final IVehicle mVehicle;
52 
53     private final IVehicleCallback mInternalCallback;
54 
55     /**
56      * Create HalClient object
57      *
58      * @param vehicle interface to the vehicle HAL
59      * @param looper looper that will be used to propagate notifications from vehicle HAL
60      * @param callback to propagate notifications from Vehicle HAL in the provided looper thread
61      */
HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback)62     HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback) {
63         mVehicle = vehicle;
64         Handler handler = new CallbackHandler(looper, callback);
65         mInternalCallback = new VehicleCallback(handler);
66     }
67 
getAllPropConfigs()68     ArrayList<VehiclePropConfig> getAllPropConfigs() throws RemoteException {
69         return mVehicle.getAllPropConfigs();
70     }
71 
subscribe(SubscribeOptions... options)72     public void subscribe(SubscribeOptions... options) throws RemoteException {
73         mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));
74     }
75 
unsubscribe(int prop)76     public void unsubscribe(int prop) throws RemoteException {
77         mVehicle.unsubscribe(mInternalCallback, prop);
78     }
79 
setValue(VehiclePropValue propValue)80     public void setValue(VehiclePropValue propValue) throws PropertyTimeoutException {
81         int status = invokeRetriable(() -> {
82             try {
83                 return mVehicle.set(propValue);
84             } catch (RemoteException e) {
85                 Log.e(CarLog.TAG_HAL, "Failed to set value", e);
86                 return StatusCode.TRY_AGAIN;
87             }
88         }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
89 
90         if (StatusCode.INVALID_ARG == status) {
91             throw new IllegalArgumentException(
92                     String.format("Failed to set value for: 0x%x, areaId: 0x%x",
93                             propValue.prop, propValue.areaId));
94         }
95 
96         if (StatusCode.TRY_AGAIN == status) {
97             throw new PropertyTimeoutException(propValue.prop);
98         }
99 
100         if (StatusCode.OK != status) {
101             throw new IllegalStateException(
102                     String.format("Failed to set property: 0x%x, areaId: 0x%x, "
103                             + "code: %d", propValue.prop, propValue.areaId, status));
104         }
105     }
106 
getValue(VehiclePropValue requestedPropValue)107     VehiclePropValue getValue(VehiclePropValue requestedPropValue) throws PropertyTimeoutException {
108         final ObjectWrapper<VehiclePropValue> valueWrapper = new ObjectWrapper<>();
109         int status = invokeRetriable(() -> {
110             ValueResult res = internalGet(requestedPropValue);
111             valueWrapper.object = res.propValue;
112             return res.status;
113         }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
114 
115         int propId = requestedPropValue.prop;
116         int areaId = requestedPropValue.areaId;
117         if (StatusCode.INVALID_ARG == status) {
118             throw new IllegalArgumentException(
119                     String.format("Failed to get value for: 0x%x, areaId: 0x%x", propId, areaId));
120         }
121 
122         if (StatusCode.TRY_AGAIN == status) {
123             throw new PropertyTimeoutException(propId);
124         }
125 
126         if (StatusCode.OK != status || valueWrapper.object == null) {
127             throw new IllegalStateException(
128                     String.format("Failed to get property: 0x%x, areaId: 0x%x, "
129                             + "code: %d", propId, areaId, status));
130         }
131 
132         return valueWrapper.object;
133     }
134 
internalGet(VehiclePropValue requestedPropValue)135     private ValueResult internalGet(VehiclePropValue requestedPropValue) {
136         final ValueResult result = new ValueResult();
137         try {
138             mVehicle.get(requestedPropValue,
139                     (status, propValue) -> {
140                         result.status = status;
141                         result.propValue = propValue;
142                     });
143         } catch (RemoteException e) {
144             Log.e(CarLog.TAG_HAL, "Failed to get value from vehicle HAL", e);
145             result.status = StatusCode.TRY_AGAIN;
146         }
147 
148         return result;
149     }
150 
151     interface RetriableCallback {
152         /** Returns {@link StatusCode} */
action()153         int action();
154     }
155 
invokeRetriable(RetriableCallback callback, long timeoutMs, long sleepMs)156     private static int invokeRetriable(RetriableCallback callback, long timeoutMs, long sleepMs) {
157         int status = callback.action();
158         long startTime = elapsedRealtime();
159         while (StatusCode.TRY_AGAIN == status && (elapsedRealtime() - startTime) < timeoutMs) {
160             try {
161                 Thread.sleep(sleepMs);
162             } catch (InterruptedException e) {
163                 Log.e(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.", e);
164                 break;
165             }
166 
167             status = callback.action();
168         }
169         return status;
170     }
171 
172     private static class ObjectWrapper<T> {
173         T object;
174     }
175 
176     private static class ValueResult {
177         int status;
178         VehiclePropValue propValue;
179     }
180 
181     private static class PropertySetError {
182         final int errorCode;
183         final int propId;
184         final int areaId;
185 
PropertySetError(int errorCode, int propId, int areaId)186         PropertySetError(int errorCode, int propId, int areaId) {
187             this.errorCode = errorCode;
188             this.propId = propId;
189             this.areaId = areaId;
190         }
191     }
192 
193     private static class CallbackHandler extends Handler {
194         private static final int MSG_ON_PROPERTY_SET = 1;
195         private static final int MSG_ON_PROPERTY_EVENT = 2;
196         private static final int MSG_ON_SET_ERROR = 3;
197 
198         private final IVehicleCallback mCallback;
199 
CallbackHandler(Looper looper, IVehicleCallback callback)200         CallbackHandler(Looper looper, IVehicleCallback callback) {
201             super(looper);
202             mCallback = callback;
203         }
204 
205         @Override
handleMessage(Message msg)206         public void handleMessage(Message msg) {
207             super.handleMessage(msg);
208 
209             try {
210                 switch (msg.what) {
211                     case MSG_ON_PROPERTY_EVENT:
212                         mCallback.onPropertyEvent((ArrayList<VehiclePropValue>) msg.obj);
213                         break;
214                     case MSG_ON_PROPERTY_SET:
215                         mCallback.onPropertySet((VehiclePropValue) msg.obj);
216                         break;
217                     case MSG_ON_SET_ERROR:
218                         PropertySetError obj = (PropertySetError) msg.obj;
219                         mCallback.onPropertySetError(obj.errorCode, obj.propId, obj.areaId);
220                         break;
221                     default:
222                         Log.e(CarLog.TAG_HAL, "Unexpected message: " + msg.what);
223                 }
224             } catch (RemoteException e) {
225                 Log.e(CarLog.TAG_HAL, "Message failed: " + msg.what);
226             }
227         }
228     }
229 
230     private static class VehicleCallback extends IVehicleCallback.Stub {
231         private Handler mHandler;
232 
VehicleCallback(Handler handler)233         VehicleCallback(Handler handler) {
234             mHandler = handler;
235         }
236 
237         @Override
onPropertyEvent(ArrayList<VehiclePropValue> propValues)238         public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
239             mHandler.sendMessage(Message.obtain(
240                     mHandler, CallbackHandler.MSG_ON_PROPERTY_EVENT, propValues));
241         }
242 
243         @Override
onPropertySet(VehiclePropValue propValue)244         public void onPropertySet(VehiclePropValue propValue) {
245             mHandler.sendMessage(Message.obtain(
246                     mHandler, CallbackHandler.MSG_ON_PROPERTY_SET, propValue));
247         }
248 
249         @Override
onPropertySetError(int errorCode, int propId, int areaId)250         public void onPropertySetError(int errorCode, int propId, int areaId) {
251             mHandler.sendMessage(Message.obtain(
252                     mHandler, CallbackHandler.MSG_ON_SET_ERROR,
253                     new PropertySetError(errorCode, propId, areaId)));
254         }
255     }
256 }
257