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