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