1 /* 2 * Copyright (C) 2017 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 android.car.diagnostic; 18 19 import android.annotation.IntDef; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.car.Car; 23 import android.car.CarLibLog; 24 import android.car.CarManagerBase; 25 import android.car.annotation.AddedInOrBefore; 26 import android.car.diagnostic.ICarDiagnosticEventListener.Stub; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import com.android.car.internal.CarPermission; 33 import com.android.car.internal.CarRatedListeners; 34 import com.android.car.internal.SingleMessageHandler; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.lang.ref.WeakReference; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.function.Consumer; 42 43 /** 44 * API for monitoring car diagnostic data. 45 * 46 * @hide 47 */ 48 @SystemApi 49 public final class CarDiagnosticManager extends CarManagerBase { 50 @AddedInOrBefore(majorVersion = 33) 51 public static final int FRAME_TYPE_LIVE = 0; 52 @AddedInOrBefore(majorVersion = 33) 53 public static final int FRAME_TYPE_FREEZE = 1; 54 55 @Retention(RetentionPolicy.SOURCE) 56 @IntDef({FRAME_TYPE_LIVE, FRAME_TYPE_FREEZE}) 57 public @interface FrameType {} 58 59 /** @hide */ 60 @AddedInOrBefore(majorVersion = 33) 61 public static final @FrameType int[] FRAME_TYPES = { 62 FRAME_TYPE_LIVE, 63 FRAME_TYPE_FREEZE 64 }; 65 66 private static final int MSG_DIAGNOSTIC_EVENTS = 0; 67 68 private final ICarDiagnostic mService; 69 private final SparseArray<CarDiagnosticListeners> mActiveListeners = new SparseArray<>(); 70 71 /** Handles call back into clients. */ 72 private final SingleMessageHandler<CarDiagnosticEvent> mHandlerCallback; 73 74 private final CarDiagnosticEventListenerToService mListenerToService; 75 76 private final CarPermission mVendorExtensionPermission; 77 78 /** @hide */ CarDiagnosticManager(Car car, IBinder service)79 public CarDiagnosticManager(Car car, IBinder service) { 80 super(car); 81 mService = ICarDiagnostic.Stub.asInterface(service); 82 mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>( 83 getEventHandler().getLooper(), MSG_DIAGNOSTIC_EVENTS) { 84 @Override 85 protected void handleEvent(CarDiagnosticEvent event) { 86 CarDiagnosticListeners listeners; 87 synchronized (mActiveListeners) { 88 listeners = mActiveListeners.get(event.frameType); 89 } 90 if (listeners != null) { 91 listeners.onDiagnosticEvent(event); 92 } 93 } 94 }; 95 mVendorExtensionPermission = new CarPermission(getContext(), 96 Car.PERMISSION_VENDOR_EXTENSION); 97 mListenerToService = new CarDiagnosticEventListenerToService(this); 98 } 99 100 @Override 101 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()102 public void onCarDisconnected() { 103 synchronized (mActiveListeners) { 104 mActiveListeners.clear(); 105 } 106 } 107 108 /** Listener for diagnostic events. Callbacks are called in the Looper context. */ 109 public interface OnDiagnosticEventListener { 110 /** 111 * Called when there is a diagnostic event from the car. 112 * 113 * @param carDiagnosticEvent 114 */ 115 @AddedInOrBefore(majorVersion = 33) onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent)116 void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent); 117 } 118 119 // OnDiagnosticEventListener registration 120 assertFrameType(@rameType int frameType)121 private void assertFrameType(@FrameType int frameType) { 122 switch(frameType) { 123 case FRAME_TYPE_FREEZE: 124 case FRAME_TYPE_LIVE: 125 return; 126 default: 127 throw new IllegalArgumentException(String.format( 128 "%d is not a valid diagnostic frame type", frameType)); 129 } 130 } 131 132 /** 133 * Register a new listener for events of a given frame type and rate. 134 * @param listener 135 * @param frameType 136 * @param rate 137 * @return true if the registration was successful; false otherwise 138 * @throws IllegalArgumentException 139 */ 140 @AddedInOrBefore(majorVersion = 33) registerListener( OnDiagnosticEventListener listener, @FrameType int frameType, int rate)141 public boolean registerListener( 142 OnDiagnosticEventListener listener, @FrameType int frameType, int rate) { 143 assertFrameType(frameType); 144 synchronized (mActiveListeners) { 145 boolean needsServerUpdate = false; 146 CarDiagnosticListeners listeners = mActiveListeners.get(frameType); 147 if (listeners == null) { 148 listeners = new CarDiagnosticListeners(rate); 149 mActiveListeners.put(frameType, listeners); 150 needsServerUpdate = true; 151 } 152 if (listeners.addAndUpdateRate(listener, rate)) { 153 needsServerUpdate = true; 154 } 155 if (needsServerUpdate) { 156 if (!registerOrUpdateDiagnosticListener(frameType, rate)) { 157 return false; 158 } 159 } 160 } 161 return true; 162 } 163 164 /** 165 * Unregister a listener, causing it to stop receiving all diagnostic events. 166 * @param listener 167 */ 168 @AddedInOrBefore(majorVersion = 33) unregisterListener(OnDiagnosticEventListener listener)169 public void unregisterListener(OnDiagnosticEventListener listener) { 170 synchronized (mActiveListeners) { 171 for (@FrameType int frameType : FRAME_TYPES) { 172 doUnregisterListenerLocked(listener, frameType); 173 } 174 } 175 } 176 doUnregisterListenerLocked(OnDiagnosticEventListener listener, @FrameType int frameType)177 private void doUnregisterListenerLocked(OnDiagnosticEventListener listener, 178 @FrameType int frameType) { 179 CarDiagnosticListeners listeners = mActiveListeners.get(frameType); 180 if (listeners != null) { 181 boolean needsServerUpdate = false; 182 if (listeners.contains(listener)) { 183 needsServerUpdate = listeners.remove(listener); 184 } 185 if (listeners.isEmpty()) { 186 try { 187 mService.unregisterDiagnosticListener(frameType, 188 mListenerToService); 189 } catch (RemoteException e) { 190 handleRemoteExceptionFromCarService(e); 191 // continue for local clean-up 192 } 193 mActiveListeners.remove(frameType); 194 } else if (needsServerUpdate) { 195 registerOrUpdateDiagnosticListener(frameType, listeners.getRate()); 196 } 197 } 198 } 199 registerOrUpdateDiagnosticListener(@rameType int frameType, int rate)200 private boolean registerOrUpdateDiagnosticListener(@FrameType int frameType, int rate) { 201 try { 202 return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService); 203 } catch (RemoteException e) { 204 return handleRemoteExceptionFromCarService(e, false); 205 } 206 } 207 208 // ICarDiagnostic forwards 209 210 /** 211 * Retrieve the most-recently acquired live frame data from the car. 212 * @return A CarDiagnostic event for the most recently known live frame if one is present. 213 * null if no live frame has been recorded by the vehicle. 214 */ 215 @AddedInOrBefore(majorVersion = 33) getLatestLiveFrame()216 public @Nullable CarDiagnosticEvent getLatestLiveFrame() { 217 try { 218 return mService.getLatestLiveFrame(); 219 } catch (RemoteException e) { 220 return handleRemoteExceptionFromCarService(e, null); 221 } 222 } 223 224 /** 225 * Return the list of the timestamps for which a freeze frame is currently stored. 226 * @return An array containing timestamps at which, at the current time, the vehicle has 227 * a freeze frame stored. If no freeze frames are currently stored, an empty 228 * array will be returned. 229 * Because vehicles might have a limited amount of storage for frames, clients cannot 230 * assume that a timestamp obtained via this call will be indefinitely valid for retrieval 231 * of the actual diagnostic data, and must be prepared to handle a missing frame. 232 */ 233 @AddedInOrBefore(majorVersion = 33) getFreezeFrameTimestamps()234 public long[] getFreezeFrameTimestamps() { 235 try { 236 return mService.getFreezeFrameTimestamps(); 237 } catch (RemoteException e) { 238 return handleRemoteExceptionFromCarService(e, new long[0]); 239 } 240 } 241 242 /** 243 * Retrieve the freeze frame event data for a given timestamp, if available. 244 * @param timestamp 245 * @return A CarDiagnostic event for the frame at the given timestamp, if one is 246 * available. null is returned otherwise. 247 * Storage constraints might cause frames to be deleted from vehicle memory. 248 * For this reason it cannot be assumed that a timestamp will yield a valid frame, 249 * even if it was initially obtained via a call to getFreezeFrameTimestamps(). 250 */ 251 @AddedInOrBefore(majorVersion = 33) getFreezeFrame(long timestamp)252 public @Nullable CarDiagnosticEvent getFreezeFrame(long timestamp) { 253 try { 254 return mService.getFreezeFrame(timestamp); 255 } catch (RemoteException e) { 256 return handleRemoteExceptionFromCarService(e, null); 257 } 258 } 259 260 /** 261 * Clear the freeze frame information from vehicle memory at the given timestamps. 262 * @param timestamps A list of timestamps to delete freeze frames at, or an empty array 263 * to delete all freeze frames from vehicle memory. 264 * @return true if all the required frames were deleted (including if no timestamps are 265 * provided and all frames were cleared); false otherwise. 266 * Due to storage constraints, timestamps cannot be assumed to be indefinitely valid, and 267 * a false return from this method should be used by the client as cause for invalidating 268 * its local knowledge of the vehicle diagnostic state. 269 */ 270 @AddedInOrBefore(majorVersion = 33) clearFreezeFrames(long... timestamps)271 public boolean clearFreezeFrames(long... timestamps) { 272 try { 273 return mService.clearFreezeFrames(timestamps); 274 } catch (RemoteException e) { 275 return handleRemoteExceptionFromCarService(e, false); 276 } 277 } 278 279 /** 280 * Returns true if this vehicle supports sending live frame information. 281 * @return 282 */ 283 @AddedInOrBefore(majorVersion = 33) isLiveFrameSupported()284 public boolean isLiveFrameSupported() { 285 try { 286 return mService.isLiveFrameSupported(); 287 } catch (RemoteException e) { 288 return handleRemoteExceptionFromCarService(e, false); 289 } 290 } 291 292 /** 293 * Returns true if this vehicle supports supports sending notifications to 294 * registered listeners when new freeze frames happen. 295 */ 296 @AddedInOrBefore(majorVersion = 33) isFreezeFrameNotificationSupported()297 public boolean isFreezeFrameNotificationSupported() { 298 try { 299 return mService.isFreezeFrameNotificationSupported(); 300 } catch (RemoteException e) { 301 return handleRemoteExceptionFromCarService(e, false); 302 } 303 } 304 305 /** 306 * Returns whether the underlying HAL supports retrieving freeze frames 307 * stored in vehicle memory using timestamp. 308 */ 309 @AddedInOrBefore(majorVersion = 33) isGetFreezeFrameSupported()310 public boolean isGetFreezeFrameSupported() { 311 try { 312 return mService.isGetFreezeFrameSupported(); 313 } catch (RemoteException e) { 314 return handleRemoteExceptionFromCarService(e, false); 315 } 316 } 317 318 /** 319 * Returns true if this vehicle supports clearing all freeze frames. 320 * This is only meaningful if freeze frame data is also supported. 321 * 322 * A return value of true for this method indicates that it is supported to call 323 * carDiagnosticManager.clearFreezeFrames() 324 * to delete all freeze frames stored in vehicle memory. 325 * 326 * @return 327 */ 328 @AddedInOrBefore(majorVersion = 33) isClearFreezeFramesSupported()329 public boolean isClearFreezeFramesSupported() { 330 try { 331 return mService.isClearFreezeFramesSupported(); 332 } catch (RemoteException e) { 333 return handleRemoteExceptionFromCarService(e, false); 334 } 335 } 336 337 /** 338 * Returns true if this vehicle supports clearing specific freeze frames by timestamp. 339 * This is only meaningful if freeze frame data is also supported. 340 * 341 * A return value of true for this method indicates that it is supported to call 342 * carDiagnosticManager.clearFreezeFrames(timestamp1, timestamp2, ...) 343 * to delete the freeze frames stored for the provided input timestamps, provided any exist. 344 * 345 * @return 346 */ 347 @AddedInOrBefore(majorVersion = 33) isSelectiveClearFreezeFramesSupported()348 public boolean isSelectiveClearFreezeFramesSupported() { 349 try { 350 return mService.isSelectiveClearFreezeFramesSupported(); 351 } catch (RemoteException e) { 352 return handleRemoteExceptionFromCarService(e, false); 353 } 354 } 355 356 private static class CarDiagnosticEventListenerToService 357 extends Stub { 358 private final WeakReference<CarDiagnosticManager> mManager; 359 CarDiagnosticEventListenerToService(CarDiagnosticManager manager)360 CarDiagnosticEventListenerToService(CarDiagnosticManager manager) { 361 mManager = new WeakReference<>(manager); 362 } 363 handleOnDiagnosticEvents(CarDiagnosticManager manager, List<CarDiagnosticEvent> events)364 private void handleOnDiagnosticEvents(CarDiagnosticManager manager, 365 List<CarDiagnosticEvent> events) { 366 manager.mHandlerCallback.sendEvents(events); 367 } 368 369 @Override onDiagnosticEvents(List<CarDiagnosticEvent> events)370 public void onDiagnosticEvents(List<CarDiagnosticEvent> events) { 371 CarDiagnosticManager manager = mManager.get(); 372 if (manager != null) { 373 handleOnDiagnosticEvents(manager, events); 374 } 375 } 376 } 377 378 private class CarDiagnosticListeners extends CarRatedListeners<OnDiagnosticEventListener> { CarDiagnosticListeners(int rate)379 CarDiagnosticListeners(int rate) { 380 super(rate); 381 } 382 onDiagnosticEvent(final CarDiagnosticEvent event)383 void onDiagnosticEvent(final CarDiagnosticEvent event) { 384 // throw away old data as oneway binder call can change order. 385 long updateTime = event.timestamp; 386 if (updateTime < mLastUpdateTime) { 387 Log.w(CarLibLog.TAG_DIAGNOSTIC, "dropping old data"); 388 return; 389 } 390 mLastUpdateTime = updateTime; 391 final boolean hasVendorExtensionPermission = mVendorExtensionPermission.checkGranted(); 392 final CarDiagnosticEvent eventToDispatch = hasVendorExtensionPermission 393 ? event : 394 event.withVendorSensorsRemoved(); 395 List<OnDiagnosticEventListener> listeners; 396 synchronized (mActiveListeners) { 397 listeners = new ArrayList<>(getListeners()); 398 } 399 listeners.forEach(new Consumer<OnDiagnosticEventListener>() { 400 401 @Override 402 public void accept(OnDiagnosticEventListener listener) { 403 listener.onDiagnosticEvent(eventToDispatch); 404 } 405 }); 406 } 407 } 408 } 409