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