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.Log; 31 32 import com.android.car.Listeners.ClientWithRate; 33 import com.android.car.hal.DiagnosticHalService; 34 import com.android.car.hal.DiagnosticHalService.DiagnosticCapabilities; 35 import com.android.car.internal.CarPermission; 36 import com.android.internal.annotations.GuardedBy; 37 38 import java.io.PrintWriter; 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 Log.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 Log.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 Log.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 Log.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 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("starting diagnostic %s at rate %d", 282 frameType, rate)); 283 DiagnosticHalService diagnosticHal = getDiagnosticHal(); 284 if (diagnosticHal != null) { 285 if (!diagnosticHal.isReady()) { 286 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready"); 287 return false; 288 } 289 switch (frameType) { 290 case CarDiagnosticManager.FRAME_TYPE_LIVE: 291 if (mLiveFrameDiagnosticRecord.isEnabled()) { 292 return true; 293 } 294 if (diagnosticHal.requestDiagnosticStart(CarDiagnosticManager.FRAME_TYPE_LIVE, 295 rate)) { 296 mLiveFrameDiagnosticRecord.enable(); 297 return true; 298 } 299 break; 300 case CarDiagnosticManager.FRAME_TYPE_FREEZE: 301 if (mFreezeFrameDiagnosticRecords.isEnabled()) { 302 return true; 303 } 304 if (diagnosticHal.requestDiagnosticStart(CarDiagnosticManager.FRAME_TYPE_FREEZE, 305 rate)) { 306 mFreezeFrameDiagnosticRecords.enable(); 307 return true; 308 } 309 break; 310 } 311 } 312 return false; 313 } 314 315 @Override unregisterDiagnosticListener( int frameType, ICarDiagnosticEventListener listener)316 public void unregisterDiagnosticListener( 317 int frameType, ICarDiagnosticEventListener listener) { 318 boolean shouldStopDiagnostic = false; 319 boolean shouldRestartDiagnostic = false; 320 int newRate = 0; 321 mDiagnosticLock.lock(); 322 try { 323 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener); 324 if (diagnosticClient == null) { 325 Log.i( 326 CarLog.TAG_DIAGNOSTIC, 327 String.format( 328 "trying to unregister diagnostic client %s for %s which is not registered", 329 listener, frameType)); 330 // never registered or already unregistered. 331 return; 332 } 333 diagnosticClient.removeDiagnostic(frameType); 334 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) { 335 diagnosticClient.release(); 336 mClients.remove(diagnosticClient); 337 } 338 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType); 339 if (diagnosticListeners == null) { 340 // diagnostic not active 341 return; 342 } 343 ClientWithRate<DiagnosticClient> clientWithRate = 344 diagnosticListeners.findClientWithRate(diagnosticClient); 345 if (clientWithRate == null) { 346 return; 347 } 348 diagnosticListeners.removeClientWithRate(clientWithRate); 349 if (diagnosticListeners.getNumberOfClients() == 0) { 350 shouldStopDiagnostic = true; 351 mDiagnosticListeners.remove(frameType); 352 } else if (diagnosticListeners.updateRate()) { // rate changed 353 newRate = diagnosticListeners.getRate(); 354 shouldRestartDiagnostic = true; 355 } 356 } finally { 357 mDiagnosticLock.unlock(); 358 } 359 Log.i( 360 CarLog.TAG_DIAGNOSTIC, 361 String.format( 362 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s", 363 shouldStopDiagnostic, shouldRestartDiagnostic, frameType)); 364 if (shouldStopDiagnostic) { 365 stopDiagnostic(frameType); 366 } else if (shouldRestartDiagnostic) { 367 startDiagnostic(frameType, newRate); 368 } 369 } 370 stopDiagnostic(int frameType)371 private void stopDiagnostic(int frameType) { 372 DiagnosticHalService diagnosticHal = getDiagnosticHal(); 373 if (diagnosticHal == null || !diagnosticHal.isReady()) { 374 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready"); 375 return; 376 } 377 switch (frameType) { 378 case CarDiagnosticManager.FRAME_TYPE_LIVE: 379 if (mLiveFrameDiagnosticRecord.disableIfNeeded()) 380 diagnosticHal.requestDiagnosticStop(CarDiagnosticManager.FRAME_TYPE_LIVE); 381 break; 382 case CarDiagnosticManager.FRAME_TYPE_FREEZE: 383 if (mFreezeFrameDiagnosticRecords.disableIfNeeded()) 384 diagnosticHal.requestDiagnosticStop(CarDiagnosticManager.FRAME_TYPE_FREEZE); 385 break; 386 } 387 } 388 getDiagnosticHal()389 private DiagnosticHalService getDiagnosticHal() { 390 return mDiagnosticHal; 391 } 392 393 // Expose DiagnosticCapabilities isLiveFrameSupported()394 public boolean isLiveFrameSupported() { 395 return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported(); 396 } 397 isFreezeFrameNotificationSupported()398 public boolean isFreezeFrameNotificationSupported() { 399 return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported(); 400 } 401 isGetFreezeFrameSupported()402 public boolean isGetFreezeFrameSupported() { 403 DiagnosticCapabilities diagnosticCapabilities = 404 getDiagnosticHal().getDiagnosticCapabilities(); 405 return diagnosticCapabilities.isFreezeFrameInfoSupported() && 406 diagnosticCapabilities.isFreezeFrameSupported(); 407 } 408 isClearFreezeFramesSupported()409 public boolean isClearFreezeFramesSupported() { 410 DiagnosticCapabilities diagnosticCapabilities = 411 getDiagnosticHal().getDiagnosticCapabilities(); 412 return diagnosticCapabilities.isFreezeFrameClearSupported() && 413 diagnosticCapabilities.isFreezeFrameSupported(); 414 } 415 isSelectiveClearFreezeFramesSupported()416 public boolean isSelectiveClearFreezeFramesSupported() { 417 DiagnosticCapabilities diagnosticCapabilities = 418 getDiagnosticHal().getDiagnosticCapabilities(); 419 return isClearFreezeFramesSupported() && 420 diagnosticCapabilities.isSelectiveClearFreezeFramesSupported(); 421 } 422 423 // ICarDiagnostic implementations 424 425 @Override getLatestLiveFrame()426 public CarDiagnosticEvent getLatestLiveFrame() { 427 mLiveFrameDiagnosticRecord.lock(); 428 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent(); 429 mLiveFrameDiagnosticRecord.unlock(); 430 return liveFrame; 431 } 432 433 @Override getFreezeFrameTimestamps()434 public long[] getFreezeFrameTimestamps() { 435 mFreezeFrameDiagnosticRecords.lock(); 436 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps(); 437 mFreezeFrameDiagnosticRecords.unlock(); 438 return timestamps; 439 } 440 441 @Override 442 @Nullable getFreezeFrame(long timestamp)443 public CarDiagnosticEvent getFreezeFrame(long timestamp) { 444 mFreezeFrameDiagnosticRecords.lock(); 445 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp); 446 mFreezeFrameDiagnosticRecords.unlock(); 447 return freezeFrame; 448 } 449 450 @Override clearFreezeFrames(long... timestamps)451 public boolean clearFreezeFrames(long... timestamps) { 452 mDiagnosticClearPermission.assertGranted(); 453 if (!isClearFreezeFramesSupported()) 454 return false; 455 if (timestamps != null && timestamps.length != 0) { 456 if (!isSelectiveClearFreezeFramesSupported()) { 457 return false; 458 } 459 } 460 mFreezeFrameDiagnosticRecords.lock(); 461 mDiagnosticHal.clearFreezeFrames(timestamps); 462 mFreezeFrameDiagnosticRecords.clearEvents(); 463 mFreezeFrameDiagnosticRecords.unlock(); 464 return true; 465 } 466 467 /** 468 * Find DiagnosticClient from client list and return it. This should be called with mClients 469 * locked. 470 * 471 * @param listener 472 * @return null if not found. 473 */ 474 @GuardedBy("mDiagnosticLock") findDiagnosticClientLocked( ICarDiagnosticEventListener listener)475 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked( 476 ICarDiagnosticEventListener listener) { 477 IBinder binder = listener.asBinder(); 478 for (DiagnosticClient diagnosticClient : mClients) { 479 if (diagnosticClient.isHoldingListenerBinder(binder)) { 480 return diagnosticClient; 481 } 482 } 483 return null; 484 } 485 removeClient(DiagnosticClient diagnosticClient)486 private void removeClient(DiagnosticClient diagnosticClient) { 487 mDiagnosticLock.lock(); 488 try { 489 for (int diagnostic : diagnosticClient.getDiagnosticArray()) { 490 unregisterDiagnosticListener( 491 diagnostic, diagnosticClient.getICarDiagnosticEventListener()); 492 } 493 mClients.remove(diagnosticClient); 494 } finally { 495 mDiagnosticLock.unlock(); 496 } 497 } 498 499 /** internal instance for pending client request */ 500 private class DiagnosticClient implements Listeners.IListener { 501 /** callback for diagnostic events */ 502 private final ICarDiagnosticEventListener mListener; 503 504 private final Set<Integer> mActiveDiagnostics = new HashSet<>(); 505 506 /** when false, it is already released */ 507 private volatile boolean mActive = true; 508 DiagnosticClient(ICarDiagnosticEventListener listener)509 DiagnosticClient(ICarDiagnosticEventListener listener) { 510 this.mListener = listener; 511 } 512 513 @Override equals(Object o)514 public boolean equals(Object o) { 515 return o instanceof DiagnosticClient 516 && mListener.asBinder() 517 == ((DiagnosticClient) o).mListener.asBinder(); 518 } 519 isHoldingListenerBinder(IBinder listenerBinder)520 boolean isHoldingListenerBinder(IBinder listenerBinder) { 521 return mListener.asBinder() == listenerBinder; 522 } 523 addDiagnostic(int frameType)524 void addDiagnostic(int frameType) { 525 mActiveDiagnostics.add(frameType); 526 } 527 removeDiagnostic(int frameType)528 void removeDiagnostic(int frameType) { 529 mActiveDiagnostics.remove(frameType); 530 } 531 getNumberOfActiveDiagnostic()532 int getNumberOfActiveDiagnostic() { 533 return mActiveDiagnostics.size(); 534 } 535 getDiagnosticArray()536 int[] getDiagnosticArray() { 537 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray(); 538 } 539 getICarDiagnosticEventListener()540 ICarDiagnosticEventListener getICarDiagnosticEventListener() { 541 return mListener; 542 } 543 544 /** Client dead. should remove all diagnostic requests from client */ 545 @Override binderDied()546 public void binderDied() { 547 mListener.asBinder().unlinkToDeath(this, 0); 548 removeClient(this); 549 } 550 dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events)551 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) { 552 if (events.size() != 0 && mActive) { 553 try { 554 mListener.onDiagnosticEvents(events); 555 } catch (RemoteException e) { 556 //ignore. crash will be handled by death handler 557 } 558 } 559 } 560 561 @Override release()562 public void release() { 563 if (mActive) { 564 mListener.asBinder().unlinkToDeath(this, 0); 565 mActiveDiagnostics.clear(); 566 mActive = false; 567 } 568 } 569 } 570 571 private static abstract class DiagnosticRecord { 572 private final ReentrantLock mLock; 573 protected boolean mEnabled = false; 574 DiagnosticRecord(ReentrantLock lock)575 DiagnosticRecord(ReentrantLock lock) { 576 mLock = lock; 577 } 578 lock()579 void lock() { 580 mLock.lock(); 581 } 582 unlock()583 void unlock() { 584 mLock.unlock(); 585 } 586 isEnabled()587 boolean isEnabled() { 588 return mEnabled; 589 } 590 enable()591 void enable() { 592 mEnabled = true; 593 } 594 disableIfNeeded()595 abstract boolean disableIfNeeded(); update(CarDiagnosticEvent newEvent)596 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent); 597 } 598 599 private static class LiveFrameRecord extends DiagnosticRecord { 600 /** Store the most recent live-frame. */ 601 CarDiagnosticEvent mLastEvent = null; 602 LiveFrameRecord(ReentrantLock lock)603 LiveFrameRecord(ReentrantLock lock) { 604 super(lock); 605 } 606 607 @Override disableIfNeeded()608 boolean disableIfNeeded() { 609 if (!mEnabled) return false; 610 mEnabled = false; 611 mLastEvent = null; 612 return true; 613 } 614 615 @Override update(@onNull CarDiagnosticEvent newEvent)616 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) { 617 Objects.requireNonNull(newEvent); 618 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent)) 619 mLastEvent = newEvent; 620 return mLastEvent; 621 } 622 getLastEvent()623 CarDiagnosticEvent getLastEvent() { 624 return mLastEvent; 625 } 626 } 627 628 private static class FreezeFrameRecord extends DiagnosticRecord { 629 /** Store the timestamp --> freeze frame mapping. */ 630 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>(); 631 FreezeFrameRecord(ReentrantLock lock)632 FreezeFrameRecord(ReentrantLock lock) { 633 super(lock); 634 } 635 636 @Override disableIfNeeded()637 boolean disableIfNeeded() { 638 if (!mEnabled) return false; 639 mEnabled = false; 640 clearEvents(); 641 return true; 642 } 643 clearEvents()644 void clearEvents() { 645 mEvents.clear(); 646 } 647 648 @Override update(@onNull CarDiagnosticEvent newEvent)649 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) { 650 mEvents.put(newEvent.timestamp, newEvent); 651 return newEvent; 652 } 653 getFreezeFrameTimestamps()654 long[] getFreezeFrameTimestamps() { 655 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray(); 656 } 657 getEvent(long timestamp)658 CarDiagnosticEvent getEvent(long timestamp) { 659 return mEvents.get(timestamp); 660 } 661 getEvents()662 Iterable<CarDiagnosticEvent> getEvents() { 663 return mEvents.values(); 664 } 665 } 666 667 @Override dump(PrintWriter writer)668 public void dump(PrintWriter writer) { 669 writer.println("*CarDiagnosticService*"); 670 writer.println("**last events for diagnostics**"); 671 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) { 672 writer.println("last live frame event: "); 673 writer.println(mLiveFrameDiagnosticRecord.getLastEvent()); 674 } 675 writer.println("freeze frame events: "); 676 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println); 677 writer.println("**clients**"); 678 try { 679 for (DiagnosticClient client : mClients) { 680 if (client != null) { 681 try { 682 writer.println( 683 "binder:" 684 + client.mListener 685 + " active diagnostics:" 686 + Arrays.toString(client.getDiagnosticArray())); 687 } catch (ConcurrentModificationException e) { 688 writer.println("concurrent modification happened"); 689 } 690 } else { 691 writer.println("null client"); 692 } 693 } 694 } catch (ConcurrentModificationException e) { 695 writer.println("concurrent modification happened"); 696 } 697 writer.println("**diagnostic listeners**"); 698 try { 699 for (int diagnostic : mDiagnosticListeners.keySet()) { 700 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic); 701 if (diagnosticListeners != null) { 702 writer.println( 703 " Diagnostic:" 704 + diagnostic 705 + " num client:" 706 + diagnosticListeners.getNumberOfClients() 707 + " rate:" 708 + diagnosticListeners.getRate()); 709 } 710 } 711 } catch (ConcurrentModificationException e) { 712 writer.println("concurrent modification happened"); 713 } 714 } 715 } 716