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