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