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 com.android.car; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.car.Car; 22 import android.car.annotation.FutureFeature; 23 import android.car.hardware.CarDiagnosticEvent; 24 import android.car.hardware.CarDiagnosticManager; 25 import android.car.hardware.ICarDiagnostic; 26 import android.car.hardware.ICarDiagnosticEventListener; 27 import android.content.Context; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 import com.android.car.internal.CarPermission; 33 import com.android.car.Listeners.ClientWithRate; 34 import com.android.car.hal.DiagnosticHalService; 35 import com.android.internal.annotations.GuardedBy; 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.ConcurrentModificationException; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.LinkedList; 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.Set; 46 import java.util.concurrent.locks.ReentrantLock; 47 48 @FutureFeature 49 /** @hide */ 50 public class CarDiagnosticService extends ICarDiagnostic.Stub 51 implements CarServiceBase, DiagnosticHalService.DiagnosticListener { 52 /** lock to access diagnostic structures */ 53 private final ReentrantLock mDiagnosticLock = new ReentrantLock(); 54 /** hold clients callback */ 55 @GuardedBy("mDiagnosticLock") 56 private final LinkedList<DiagnosticClient> mClients = new LinkedList<>(); 57 58 /** key: diagnostic type. */ 59 @GuardedBy("mDiagnosticLock") 60 private final HashMap<Integer, Listeners<DiagnosticClient>> mDiagnosticListeners = 61 new HashMap<>(); 62 63 /** the latest live frame data. */ 64 @GuardedBy("mDiagnosticLock") 65 private final LiveFrameRecord mLiveFrameDiagnosticRecord = new LiveFrameRecord(mDiagnosticLock); 66 67 /** the latest freeze frame data (key: DTC) */ 68 @GuardedBy("mDiagnosticLock") 69 private final FreezeFrameRecord mFreezeFrameDiagnosticRecords = new FreezeFrameRecord( 70 mDiagnosticLock); 71 72 private final DiagnosticHalService mDiagnosticHal; 73 74 private final Context mContext; 75 76 private final CarPermission mDiagnosticReadPermission; 77 78 private final CarPermission mDiagnosticClearPermission; 79 CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal)80 public CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal) { 81 mContext = context; 82 mDiagnosticHal = diagnosticHal; 83 mDiagnosticReadPermission = new CarPermission(mContext, Car.PERMISSION_CAR_DIAGNOSTIC_READ); 84 mDiagnosticClearPermission = new CarPermission(mContext, 85 Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR); 86 } 87 88 @Override init()89 public void init() { 90 mDiagnosticLock.lock(); 91 try { 92 mDiagnosticHal.setDiagnosticListener(this); 93 setInitialLiveFrame(); 94 setInitialFreezeFrames(); 95 } finally { 96 mDiagnosticLock.unlock(); 97 } 98 } 99 100 @Nullable setInitialLiveFrame()101 private CarDiagnosticEvent setInitialLiveFrame() { 102 CarDiagnosticEvent liveFrame = null; 103 if(mDiagnosticHal.getDiagnosticCapabilities().isLiveFrameSupported()) { 104 liveFrame = setRecentmostLiveFrame(mDiagnosticHal.getCurrentLiveFrame()); 105 } 106 return liveFrame; 107 } 108 setInitialFreezeFrames()109 private void setInitialFreezeFrames() { 110 if(mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameSupported() && 111 mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameInfoSupported()) { 112 long[] timestamps = mDiagnosticHal.getFreezeFrameTimestamps(); 113 if (timestamps != null) { 114 for (long timestamp : timestamps) { 115 setRecentmostFreezeFrame(mDiagnosticHal.getFreezeFrame(timestamp)); 116 } 117 } 118 } 119 } 120 121 @Nullable setRecentmostLiveFrame(final CarDiagnosticEvent event)122 private CarDiagnosticEvent setRecentmostLiveFrame(final CarDiagnosticEvent event) { 123 if (event != null) { 124 return mLiveFrameDiagnosticRecord.update(event.checkLiveFrame()); 125 } 126 return null; 127 } 128 129 @Nullable setRecentmostFreezeFrame(final CarDiagnosticEvent event)130 private CarDiagnosticEvent setRecentmostFreezeFrame(final CarDiagnosticEvent event) { 131 if (event != null) { 132 return mFreezeFrameDiagnosticRecords.update(event.checkFreezeFrame()); 133 } 134 return null; 135 } 136 137 @Override release()138 public void release() { 139 mDiagnosticLock.lock(); 140 try { 141 mDiagnosticListeners.forEach( 142 (Integer frameType, Listeners diagnosticListeners) -> 143 diagnosticListeners.release()); 144 mDiagnosticListeners.clear(); 145 mLiveFrameDiagnosticRecord.disableIfNeeded(); 146 mFreezeFrameDiagnosticRecords.disableIfNeeded(); 147 mClients.clear(); 148 } finally { 149 mDiagnosticLock.unlock(); 150 } 151 } 152 processDiagnosticData(List<CarDiagnosticEvent> events)153 private void processDiagnosticData(List<CarDiagnosticEvent> events) { 154 ArrayMap<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> eventsByClient = 155 new ArrayMap<>(); 156 157 Listeners<DiagnosticClient> listeners = null; 158 159 mDiagnosticLock.lock(); 160 for (CarDiagnosticEvent event : events) { 161 if (event.isLiveFrame()) { 162 // record recent-most live frame information 163 setRecentmostLiveFrame(event); 164 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE); 165 } else if (event.isFreezeFrame()) { 166 setRecentmostFreezeFrame(event); 167 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE); 168 } else { 169 Log.w( 170 CarLog.TAG_DIAGNOSTIC, 171 String.format("received unknown diagnostic event: %s", event)); 172 continue; 173 } 174 175 if (null != listeners) { 176 for (ClientWithRate<DiagnosticClient> clientWithRate : listeners.getClients()) { 177 DiagnosticClient client = clientWithRate.getClient(); 178 List<CarDiagnosticEvent> clientEvents = eventsByClient.computeIfAbsent(client, 179 (DiagnosticClient diagnosticClient) -> new LinkedList<>()); 180 clientEvents.add(event); 181 } 182 } 183 } 184 mDiagnosticLock.unlock(); 185 186 for (ArrayMap.Entry<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> entry : 187 eventsByClient.entrySet()) { 188 CarDiagnosticService.DiagnosticClient client = entry.getKey(); 189 List<CarDiagnosticEvent> clientEvents = entry.getValue(); 190 191 client.dispatchDiagnosticUpdate(clientEvents); 192 } 193 } 194 195 /** Received diagnostic data from car. */ 196 @Override onDiagnosticEvents(List<CarDiagnosticEvent> events)197 public void onDiagnosticEvents(List<CarDiagnosticEvent> events) { 198 processDiagnosticData(events); 199 } 200 getCachedEventsLocked(int frameType)201 private List<CarDiagnosticEvent> getCachedEventsLocked(int frameType) { 202 ArrayList<CarDiagnosticEvent> events = new ArrayList<>(); 203 switch (frameType) { 204 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE: 205 mLiveFrameDiagnosticRecord.lock(); 206 events.add(mLiveFrameDiagnosticRecord.getLastEvent()); 207 mLiveFrameDiagnosticRecord.unlock(); 208 break; 209 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE: 210 mFreezeFrameDiagnosticRecords.lock(); 211 mFreezeFrameDiagnosticRecords.getEvents().forEach(events::add); 212 mFreezeFrameDiagnosticRecords.unlock(); 213 break; 214 default: break; 215 } 216 return events; 217 } 218 219 @Override registerOrUpdateDiagnosticListener(int frameType, int rate, ICarDiagnosticEventListener listener)220 public boolean registerOrUpdateDiagnosticListener(int frameType, int rate, 221 ICarDiagnosticEventListener listener) { 222 boolean shouldStartDiagnostics = false; 223 CarDiagnosticService.DiagnosticClient diagnosticClient = null; 224 Integer oldRate = null; 225 Listeners<DiagnosticClient> diagnosticListeners = null; 226 mDiagnosticLock.lock(); 227 try { 228 mDiagnosticReadPermission.assertGranted(); 229 diagnosticClient = findDiagnosticClientLocked(listener); 230 Listeners.ClientWithRate<DiagnosticClient> diagnosticClientWithRate = null; 231 if (diagnosticClient == null) { 232 diagnosticClient = new DiagnosticClient(listener); 233 try { 234 listener.asBinder().linkToDeath(diagnosticClient, 0); 235 } catch (RemoteException e) { 236 Log.w( 237 CarLog.TAG_DIAGNOSTIC, 238 String.format( 239 "received RemoteException trying to register listener for %s", 240 frameType)); 241 return false; 242 } 243 mClients.add(diagnosticClient); 244 } 245 // If we have a cached event for this diagnostic, send the event. 246 diagnosticClient.dispatchDiagnosticUpdate(getCachedEventsLocked(frameType)); 247 diagnosticListeners = mDiagnosticListeners.get(frameType); 248 if (diagnosticListeners == null) { 249 diagnosticListeners = new Listeners<>(rate); 250 mDiagnosticListeners.put(frameType, diagnosticListeners); 251 shouldStartDiagnostics = true; 252 } else { 253 oldRate = diagnosticListeners.getRate(); 254 diagnosticClientWithRate = 255 diagnosticListeners.findClientWithRate(diagnosticClient); 256 } 257 if (diagnosticClientWithRate == null) { 258 diagnosticClientWithRate = 259 new ClientWithRate<>(diagnosticClient, rate); 260 diagnosticListeners.addClientWithRate(diagnosticClientWithRate); 261 } else { 262 diagnosticClientWithRate.setRate(rate); 263 } 264 if (diagnosticListeners.getRate() > rate) { 265 diagnosticListeners.setRate(rate); 266 shouldStartDiagnostics = true; 267 } 268 diagnosticClient.addDiagnostic(frameType); 269 } finally { 270 mDiagnosticLock.unlock(); 271 } 272 Log.i( 273 CarLog.TAG_DIAGNOSTIC, 274 String.format( 275 "shouldStartDiagnostics = %s for %s at rate %d", 276 shouldStartDiagnostics, frameType, rate)); 277 // start diagnostic outside lock as it can take time. 278 if (shouldStartDiagnostics) { 279 if (!startDiagnostic(frameType, rate)) { 280 // failed. so remove from active diagnostic list. 281 Log.w(CarLog.TAG_DIAGNOSTIC, "startDiagnostic failed"); 282 mDiagnosticLock.lock(); 283 try { 284 diagnosticClient.removeDiagnostic(frameType); 285 if (oldRate != null) { 286 diagnosticListeners.setRate(oldRate); 287 } else { 288 mDiagnosticListeners.remove(frameType); 289 } 290 } finally { 291 mDiagnosticLock.unlock(); 292 } 293 return false; 294 } 295 } 296 return true; 297 } 298 startDiagnostic(int frameType, int rate)299 private boolean startDiagnostic(int frameType, int rate) { 300 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("starting diagnostic %s at rate %d", 301 frameType, rate)); 302 DiagnosticHalService diagnosticHal = getDiagnosticHal(); 303 if (diagnosticHal != null) { 304 if (!diagnosticHal.isReady()) { 305 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready"); 306 return false; 307 } 308 switch (frameType) { 309 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE: 310 if (mLiveFrameDiagnosticRecord.isEnabled()) { 311 return true; 312 } 313 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE, 314 rate)) { 315 mLiveFrameDiagnosticRecord.enable(); 316 return true; 317 } 318 break; 319 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE: 320 if (mFreezeFrameDiagnosticRecords.isEnabled()) { 321 return true; 322 } 323 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE, 324 rate)) { 325 mFreezeFrameDiagnosticRecords.enable(); 326 return true; 327 } 328 break; 329 } 330 } 331 return false; 332 } 333 334 @Override unregisterDiagnosticListener( int frameType, ICarDiagnosticEventListener listener)335 public void unregisterDiagnosticListener( 336 int frameType, ICarDiagnosticEventListener listener) { 337 boolean shouldStopDiagnostic = false; 338 boolean shouldRestartDiagnostic = false; 339 int newRate = 0; 340 mDiagnosticLock.lock(); 341 try { 342 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener); 343 if (diagnosticClient == null) { 344 Log.i( 345 CarLog.TAG_DIAGNOSTIC, 346 String.format( 347 "trying to unregister diagnostic client %s for %s which is not registered", 348 listener, frameType)); 349 // never registered or already unregistered. 350 return; 351 } 352 diagnosticClient.removeDiagnostic(frameType); 353 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) { 354 diagnosticClient.release(); 355 mClients.remove(diagnosticClient); 356 } 357 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType); 358 if (diagnosticListeners == null) { 359 // diagnostic not active 360 return; 361 } 362 ClientWithRate<DiagnosticClient> clientWithRate = 363 diagnosticListeners.findClientWithRate(diagnosticClient); 364 if (clientWithRate == null) { 365 return; 366 } 367 diagnosticListeners.removeClientWithRate(clientWithRate); 368 if (diagnosticListeners.getNumberOfClients() == 0) { 369 shouldStopDiagnostic = true; 370 mDiagnosticListeners.remove(frameType); 371 } else if (diagnosticListeners.updateRate()) { // rate changed 372 newRate = diagnosticListeners.getRate(); 373 shouldRestartDiagnostic = true; 374 } 375 } finally { 376 mDiagnosticLock.unlock(); 377 } 378 Log.i( 379 CarLog.TAG_DIAGNOSTIC, 380 String.format( 381 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s", 382 shouldStopDiagnostic, shouldRestartDiagnostic, frameType)); 383 if (shouldStopDiagnostic) { 384 stopDiagnostic(frameType); 385 } else if (shouldRestartDiagnostic) { 386 startDiagnostic(frameType, newRate); 387 } 388 } 389 stopDiagnostic(int frameType)390 private void stopDiagnostic(int frameType) { 391 DiagnosticHalService diagnosticHal = getDiagnosticHal(); 392 if (diagnosticHal == null || !diagnosticHal.isReady()) { 393 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready"); 394 return; 395 } 396 switch (frameType) { 397 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE: 398 if (mLiveFrameDiagnosticRecord.disableIfNeeded()) 399 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE); 400 break; 401 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE: 402 if (mFreezeFrameDiagnosticRecords.disableIfNeeded()) 403 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE); 404 break; 405 } 406 } 407 getDiagnosticHal()408 private DiagnosticHalService getDiagnosticHal() { 409 return mDiagnosticHal; 410 } 411 412 // ICarDiagnostic implementations 413 414 @Override getLatestLiveFrame()415 public CarDiagnosticEvent getLatestLiveFrame() { 416 mLiveFrameDiagnosticRecord.lock(); 417 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent(); 418 mLiveFrameDiagnosticRecord.unlock(); 419 return liveFrame; 420 } 421 422 @Override getFreezeFrameTimestamps()423 public long[] getFreezeFrameTimestamps() { 424 mFreezeFrameDiagnosticRecords.lock(); 425 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps(); 426 mFreezeFrameDiagnosticRecords.unlock(); 427 return timestamps; 428 } 429 430 @Override 431 @Nullable getFreezeFrame(long timestamp)432 public CarDiagnosticEvent getFreezeFrame(long timestamp) { 433 mFreezeFrameDiagnosticRecords.lock(); 434 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp); 435 mFreezeFrameDiagnosticRecords.unlock(); 436 return freezeFrame; 437 } 438 439 @Override clearFreezeFrames(long... timestamps)440 public boolean clearFreezeFrames(long... timestamps) { 441 mDiagnosticClearPermission.assertGranted(); 442 if (mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameClearSupported()) { 443 mFreezeFrameDiagnosticRecords.lock(); 444 mDiagnosticHal.clearFreezeFrames(timestamps); 445 mFreezeFrameDiagnosticRecords.clearEvents(); 446 mFreezeFrameDiagnosticRecords.unlock(); 447 return true; 448 } 449 return false; 450 } 451 452 /** 453 * Find DiagnosticClient from client list and return it. This should be called with mClients 454 * locked. 455 * 456 * @param listener 457 * @return null if not found. 458 */ findDiagnosticClientLocked( ICarDiagnosticEventListener listener)459 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked( 460 ICarDiagnosticEventListener listener) { 461 IBinder binder = listener.asBinder(); 462 for (DiagnosticClient diagnosticClient : mClients) { 463 if (diagnosticClient.isHoldingListenerBinder(binder)) { 464 return diagnosticClient; 465 } 466 } 467 return null; 468 } 469 removeClient(DiagnosticClient diagnosticClient)470 private void removeClient(DiagnosticClient diagnosticClient) { 471 mDiagnosticLock.lock(); 472 try { 473 for (int diagnostic : diagnosticClient.getDiagnosticArray()) { 474 unregisterDiagnosticListener( 475 diagnostic, diagnosticClient.getICarDiagnosticEventListener()); 476 } 477 mClients.remove(diagnosticClient); 478 } finally { 479 mDiagnosticLock.unlock(); 480 } 481 } 482 483 /** internal instance for pending client request */ 484 private class DiagnosticClient implements Listeners.IListener { 485 /** callback for diagnostic events */ 486 private final ICarDiagnosticEventListener mListener; 487 488 private final Set<Integer> mActiveDiagnostics = new HashSet<>(); 489 490 /** when false, it is already released */ 491 private volatile boolean mActive = true; 492 DiagnosticClient(ICarDiagnosticEventListener listener)493 DiagnosticClient(ICarDiagnosticEventListener listener) { 494 this.mListener = listener; 495 } 496 497 @Override equals(Object o)498 public boolean equals(Object o) { 499 if (o instanceof CarDiagnosticService.DiagnosticClient 500 && mListener.asBinder() 501 == ((CarDiagnosticService.DiagnosticClient) o).mListener.asBinder()) { 502 return true; 503 } 504 return false; 505 } 506 isHoldingListenerBinder(IBinder listenerBinder)507 boolean isHoldingListenerBinder(IBinder listenerBinder) { 508 return mListener.asBinder() == listenerBinder; 509 } 510 addDiagnostic(int frameType)511 void addDiagnostic(int frameType) { 512 mActiveDiagnostics.add(frameType); 513 } 514 removeDiagnostic(int frameType)515 void removeDiagnostic(int frameType) { 516 mActiveDiagnostics.remove(frameType); 517 } 518 getNumberOfActiveDiagnostic()519 int getNumberOfActiveDiagnostic() { 520 return mActiveDiagnostics.size(); 521 } 522 getDiagnosticArray()523 int[] getDiagnosticArray() { 524 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray(); 525 } 526 getICarDiagnosticEventListener()527 ICarDiagnosticEventListener getICarDiagnosticEventListener() { 528 return mListener; 529 } 530 531 /** Client dead. should remove all diagnostic requests from client */ 532 @Override binderDied()533 public void binderDied() { 534 mListener.asBinder().unlinkToDeath(this, 0); 535 removeClient(this); 536 } 537 dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events)538 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) { 539 if (events.size() == 0) { 540 return; 541 } 542 if (mActive) { 543 try { 544 mListener.onDiagnosticEvents(events); 545 } catch (RemoteException e) { 546 //ignore. crash will be handled by death handler 547 } 548 } else { 549 } 550 } 551 552 @Override release()553 public void release() { 554 if (mActive) { 555 mListener.asBinder().unlinkToDeath(this, 0); 556 mActiveDiagnostics.clear(); 557 mActive = false; 558 } 559 } 560 } 561 562 private static abstract class DiagnosticRecord { 563 private final ReentrantLock mLock; 564 protected boolean mEnabled = false; 565 DiagnosticRecord(ReentrantLock lock)566 DiagnosticRecord(ReentrantLock lock) { 567 mLock = lock; 568 } 569 lock()570 void lock() { 571 mLock.lock(); 572 } 573 unlock()574 void unlock() { 575 mLock.unlock(); 576 } 577 isEnabled()578 boolean isEnabled() { 579 return mEnabled; 580 } 581 enable()582 void enable() { 583 mEnabled = true; 584 } 585 disableIfNeeded()586 abstract boolean disableIfNeeded(); update(CarDiagnosticEvent newEvent)587 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent); 588 } 589 590 private static class LiveFrameRecord extends DiagnosticRecord { 591 /** Store the most recent live-frame. */ 592 CarDiagnosticEvent mLastEvent = null; 593 LiveFrameRecord(ReentrantLock lock)594 LiveFrameRecord(ReentrantLock lock) { 595 super(lock); 596 } 597 598 @Override disableIfNeeded()599 boolean disableIfNeeded() { 600 if (!mEnabled) return false; 601 mEnabled = false; 602 mLastEvent = null; 603 return true; 604 } 605 606 @Override update(@onNull CarDiagnosticEvent newEvent)607 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) { 608 newEvent = Objects.requireNonNull(newEvent); 609 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent)) 610 mLastEvent = newEvent; 611 return mLastEvent; 612 } 613 getLastEvent()614 CarDiagnosticEvent getLastEvent() { 615 return mLastEvent; 616 } 617 } 618 619 private static class FreezeFrameRecord extends DiagnosticRecord { 620 /** Store the timestamp --> freeze frame mapping. */ 621 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>(); 622 FreezeFrameRecord(ReentrantLock lock)623 FreezeFrameRecord(ReentrantLock lock) { 624 super(lock); 625 } 626 627 @Override disableIfNeeded()628 boolean disableIfNeeded() { 629 if (!mEnabled) return false; 630 mEnabled = false; 631 clearEvents(); 632 return true; 633 } 634 clearEvents()635 void clearEvents() { 636 mEvents.clear(); 637 } 638 639 @Override update(@onNull CarDiagnosticEvent newEvent)640 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) { 641 mEvents.put(newEvent.timestamp, newEvent); 642 return newEvent; 643 } 644 getFreezeFrameTimestamps()645 long[] getFreezeFrameTimestamps() { 646 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray(); 647 } 648 getEvent(long timestamp)649 CarDiagnosticEvent getEvent(long timestamp) { 650 return mEvents.get(timestamp); 651 } 652 getEvents()653 Iterable<CarDiagnosticEvent> getEvents() { 654 return mEvents.values(); 655 } 656 } 657 658 @Override dump(PrintWriter writer)659 public void dump(PrintWriter writer) { 660 writer.println("*CarDiagnosticService*"); 661 writer.println("**last events for diagnostics**"); 662 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) { 663 writer.println("last live frame event: "); 664 writer.println(mLiveFrameDiagnosticRecord.getLastEvent()); 665 } 666 writer.println("freeze frame events: "); 667 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println); 668 writer.println("**clients**"); 669 try { 670 for (DiagnosticClient client : mClients) { 671 if (client != null) { 672 try { 673 writer.println( 674 "binder:" 675 + client.mListener 676 + " active diagnostics:" 677 + Arrays.toString(client.getDiagnosticArray())); 678 } catch (ConcurrentModificationException e) { 679 writer.println("concurrent modification happened"); 680 } 681 } else { 682 writer.println("null client"); 683 } 684 } 685 } catch (ConcurrentModificationException e) { 686 writer.println("concurrent modification happened"); 687 } 688 writer.println("**diagnostic listeners**"); 689 try { 690 for (int diagnostic : mDiagnosticListeners.keySet()) { 691 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic); 692 if (diagnosticListeners != null) { 693 writer.println( 694 " Diagnostic:" 695 + diagnostic 696 + " num client:" 697 + diagnosticListeners.getNumberOfClients() 698 + " rate:" 699 + diagnosticListeners.getRate()); 700 } 701 } 702 } catch (ConcurrentModificationException e) { 703 writer.println("concurrent modification happened"); 704 } 705 } 706 } 707