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