blob: 3a02010783871c9889fac11b3b8d185849c2db54 [file] [log] [blame]
Enrico Granata5c56d2a2017-02-07 15:38:12 -08001/*
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
17package com.android.car;
18
19import android.annotation.Nullable;
20import android.car.annotation.FutureFeature;
21import android.car.hardware.CarDiagnosticEvent;
22import android.car.hardware.CarDiagnosticManager;
23import android.car.hardware.ICarDiagnostic;
24import android.car.hardware.ICarDiagnosticEventListener;
25import android.content.Context;
26import android.content.pm.PackageManager;
27import android.os.Binder;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
33import android.os.RemoteException;
34import android.os.SystemClock;
35import android.util.ArrayMap;
36import android.util.Log;
37import com.android.car.Listeners.ClientWithRate;
38import com.android.car.hal.DiagnosticHalService;
39import com.android.internal.annotations.GuardedBy;
40import java.io.PrintWriter;
41import java.util.ArrayList;
42import java.util.Arrays;
43import java.util.ConcurrentModificationException;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.LinkedList;
47import java.util.List;
48import java.util.Objects;
49import java.util.Set;
50import java.util.concurrent.locks.ReentrantLock;
51
52@FutureFeature
53/** @hide */
54public class CarDiagnosticService extends ICarDiagnostic.Stub
55 implements CarServiceBase, DiagnosticHalService.DiagnosticListener {
56 /** {@link #mDiagnosticLock} is not waited forever for handling disconnection */
57 private static final long MAX_DIAGNOSTIC_LOCK_WAIT_MS = 1000;
58
59 /** lock to access diagnostic structures */
60 private final ReentrantLock mDiagnosticLock = new ReentrantLock();
61 /** hold clients callback */
62 @GuardedBy("mDiagnosticLock")
63 private final LinkedList<DiagnosticClient> mClients = new LinkedList<>();
64
65 /** key: diagnostic type. */
66 @GuardedBy("mDiagnosticLock")
67 private final HashMap<Integer, Listeners<DiagnosticClient>> mDiagnosticListeners =
68 new HashMap<>();
69
70 /** the latest live frame data. */
71 @GuardedBy("mDiagnosticLock")
72 private final LiveFrameRecord mLiveFrameDiagnosticRecord = new LiveFrameRecord(mDiagnosticLock);
73
74 /** the latest freeze frame data (key: DTC) */
75 @GuardedBy("mDiagnosticLock")
76 private final FreezeFrameRecord mFreezeFrameDiagnosticRecords = new FreezeFrameRecord(
77 mDiagnosticLock);
78
79 private final DiagnosticHalService mDiagnosticHal;
80
81 private final Context mContext;
82
83 public CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal) {
84 mContext = context;
85 mDiagnosticHal = diagnosticHal;
86 }
87
88 @Override
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 private CarDiagnosticEvent setInitialLiveFrame() {
101 CarDiagnosticEvent liveFrame = setRecentmostLiveFrame(mDiagnosticHal.getCurrentLiveFrame());
102 return liveFrame;
103 }
104
105 private void setInitialFreezeFrames() {
106 for (long timestamp: mDiagnosticHal.getFreezeFrameTimestamps()) {
107 setRecentmostFreezeFrame(mDiagnosticHal.getFreezeFrame(timestamp));
108 }
109 }
110
111 private CarDiagnosticEvent setRecentmostLiveFrame(final CarDiagnosticEvent event) {
112 return mLiveFrameDiagnosticRecord.update(Objects.requireNonNull(event).checkLiveFrame());
113 }
114
115 private CarDiagnosticEvent setRecentmostFreezeFrame(final CarDiagnosticEvent event) {
116 return mFreezeFrameDiagnosticRecords.update(
117 Objects.requireNonNull(event).checkFreezeFrame());
118 }
119
120 @Override
121 public void release() {
122 mDiagnosticLock.lock();
123 try {
124 mDiagnosticListeners.forEach(
125 (Integer frameType, Listeners diagnosticListeners) ->
126 diagnosticListeners.release());
127 mDiagnosticListeners.clear();
128 mLiveFrameDiagnosticRecord.disableIfNeeded();
129 mFreezeFrameDiagnosticRecords.disableIfNeeded();
130 mClients.clear();
131 } finally {
132 mDiagnosticLock.unlock();
133 }
134 }
135
136 private void processDiagnosticData(List<CarDiagnosticEvent> events) {
137 ArrayMap<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> eventsByClient =
138 new ArrayMap<>();
139
140 Listeners<DiagnosticClient> listeners = null;
141
142 mDiagnosticLock.lock();
143 for (CarDiagnosticEvent event : events) {
144 if (event.isLiveFrame()) {
145 // record recent-most live frame information
146 setRecentmostLiveFrame(event);
147 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
148 } else if (event.isFreezeFrame()) {
149 setRecentmostFreezeFrame(event);
150 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
151 } else {
152 Log.w(
153 CarLog.TAG_DIAGNOSTIC,
154 String.format("received unknown diagnostic event: %s", event));
155 continue;
156 }
157
158 if (null != listeners) {
159 for (ClientWithRate<DiagnosticClient> clientWithRate : listeners.getClients()) {
160 DiagnosticClient client = clientWithRate.getClient();
161 List<CarDiagnosticEvent> clientEvents = eventsByClient.computeIfAbsent(client,
162 (DiagnosticClient diagnosticClient) -> new LinkedList<>());
163 clientEvents.add(event);
164 }
165 }
166 }
167 mDiagnosticLock.unlock();
168
169 for (ArrayMap.Entry<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> entry :
170 eventsByClient.entrySet()) {
171 CarDiagnosticService.DiagnosticClient client = entry.getKey();
172 List<CarDiagnosticEvent> clientEvents = entry.getValue();
173
174 client.dispatchDiagnosticUpdate(clientEvents);
175 }
176 }
177
178 /** Received diagnostic data from car. */
179 @Override
180 public void onDiagnosticEvents(List<CarDiagnosticEvent> events) {
181 processDiagnosticData(events);
182 }
183
184 private List<CarDiagnosticEvent> getCachedEventsLocked(int frameType) {
185 ArrayList<CarDiagnosticEvent> events = new ArrayList<>();
186 switch (frameType) {
187 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
188 mLiveFrameDiagnosticRecord.lock();
189 events.add(mLiveFrameDiagnosticRecord.getLastEvent());
190 mLiveFrameDiagnosticRecord.unlock();
191 break;
192 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
193 mFreezeFrameDiagnosticRecords.lock();
194 mFreezeFrameDiagnosticRecords.getEvents().forEach(events::add);
195 mFreezeFrameDiagnosticRecords.unlock();
196 break;
197 default: break;
198 }
199 return events;
200 }
201
202 private void assertPermission(int frameType) {
203 if (Binder.getCallingUid() != Process.myUid()) {
204 switch (getDiagnosticPermission(frameType)) {
205 case PackageManager.PERMISSION_GRANTED:
206 break;
207 default:
208 throw new SecurityException(
209 "client does not have permission:"
210 + getPermissionName(frameType)
211 + " pid:"
212 + Binder.getCallingPid()
213 + " uid:"
214 + Binder.getCallingUid());
215 }
216 }
217 }
218
219 @Override
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 assertPermission(frameType);
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
299 //TODO(egranata): handle permissions correctly
300 private int getDiagnosticPermission(int frameType) {
301 String permission = getPermissionName(frameType);
302 int result = PackageManager.PERMISSION_GRANTED;
303 if (permission != null) {
304 return mContext.checkCallingOrSelfPermission(permission);
305 }
306 // If no permission is required, return granted.
307 return result;
308 }
309
310 private String getPermissionName(int frameType) {
311 return null;
312 }
313
314 private boolean startDiagnostic(int frameType, int rate) {
315 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("starting diagnostic %s at rate %d",
316 frameType, rate));
317 DiagnosticHalService diagnosticHal = getDiagnosticHal();
318 if (diagnosticHal != null) {
319 if (!diagnosticHal.isReady()) {
320 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
321 return false;
322 }
323 switch (frameType) {
324 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
325 if (mLiveFrameDiagnosticRecord.isEnabled()) {
326 return true;
327 }
328 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
329 rate)) {
330 mLiveFrameDiagnosticRecord.enable();
331 return true;
332 }
333 break;
334 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
335 if (mFreezeFrameDiagnosticRecords.isEnabled()) {
336 return true;
337 }
338 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
339 rate)) {
340 mFreezeFrameDiagnosticRecords.enable();
341 return true;
342 }
343 break;
344 }
345 }
346 return false;
347 }
348
349 @Override
350 public void unregisterDiagnosticListener(
351 int frameType, ICarDiagnosticEventListener listener) {
352 boolean shouldStopDiagnostic = false;
353 boolean shouldRestartDiagnostic = false;
354 int newRate = 0;
355 mDiagnosticLock.lock();
356 try {
357 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener);
358 if (diagnosticClient == null) {
359 Log.i(
360 CarLog.TAG_DIAGNOSTIC,
361 String.format(
362 "trying to unregister diagnostic client %s for %s which is not registered",
363 listener, frameType));
364 // never registered or already unregistered.
365 return;
366 }
367 diagnosticClient.removeDiagnostic(frameType);
368 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) {
369 diagnosticClient.release();
370 mClients.remove(diagnosticClient);
371 }
372 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType);
373 if (diagnosticListeners == null) {
374 // diagnostic not active
375 return;
376 }
377 ClientWithRate<DiagnosticClient> clientWithRate =
378 diagnosticListeners.findClientWithRate(diagnosticClient);
379 if (clientWithRate == null) {
380 return;
381 }
382 diagnosticListeners.removeClientWithRate(clientWithRate);
383 if (diagnosticListeners.getNumberOfClients() == 0) {
384 shouldStopDiagnostic = true;
385 mDiagnosticListeners.remove(frameType);
386 } else if (diagnosticListeners.updateRate()) { // rate changed
387 newRate = diagnosticListeners.getRate();
388 shouldRestartDiagnostic = true;
389 }
390 } finally {
391 mDiagnosticLock.unlock();
392 }
393 Log.i(
394 CarLog.TAG_DIAGNOSTIC,
395 String.format(
396 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s",
397 shouldStopDiagnostic, shouldRestartDiagnostic, frameType));
398 if (shouldStopDiagnostic) {
399 stopDiagnostic(frameType);
400 } else if (shouldRestartDiagnostic) {
401 startDiagnostic(frameType, newRate);
402 }
403 }
404
405 private void stopDiagnostic(int frameType) {
406 DiagnosticHalService diagnosticHal = getDiagnosticHal();
407 if (diagnosticHal == null || !diagnosticHal.isReady()) {
408 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
409 return;
410 }
411 switch (frameType) {
412 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
413 if (mLiveFrameDiagnosticRecord.disableIfNeeded())
414 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
415 break;
416 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
417 if (mFreezeFrameDiagnosticRecords.disableIfNeeded())
418 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
419 break;
420 }
421 }
422
423 private DiagnosticHalService getDiagnosticHal() {
424 return mDiagnosticHal;
425 }
426
427 // ICarDiagnostic implementations
428
429 @Override
430 public CarDiagnosticEvent getLatestLiveFrame() {
431 mLiveFrameDiagnosticRecord.lock();
432 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent();
433 mLiveFrameDiagnosticRecord.unlock();
434 return liveFrame;
435 }
436
437 @Override
438 public long[] getFreezeFrameTimestamps() {
439 mFreezeFrameDiagnosticRecords.lock();
440 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps();
441 mFreezeFrameDiagnosticRecords.unlock();
442 return timestamps;
443 }
444
445 @Override
446 @Nullable
447 public CarDiagnosticEvent getFreezeFrame(long timestamp) {
448 mFreezeFrameDiagnosticRecords.lock();
449 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp);
450 mFreezeFrameDiagnosticRecords.unlock();
451 return freezeFrame;
452 }
453
454 @Override
455 public boolean clearFreezeFrames(long... timestamps) {
456 mFreezeFrameDiagnosticRecords.lock();
457 mDiagnosticHal.clearFreezeFrames(timestamps);
458 mFreezeFrameDiagnosticRecords.clearEvents();
459 mFreezeFrameDiagnosticRecords.unlock();
460 return true;
461 }
462
463 /**
464 * Find DiagnosticClient from client list and return it. This should be called with mClients
465 * locked.
466 *
467 * @param listener
468 * @return null if not found.
469 */
470 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked(
471 ICarDiagnosticEventListener listener) {
472 IBinder binder = listener.asBinder();
473 for (DiagnosticClient diagnosticClient : mClients) {
474 if (diagnosticClient.isHoldingListenerBinder(binder)) {
475 return diagnosticClient;
476 }
477 }
478 return null;
479 }
480
481 private void removeClient(DiagnosticClient diagnosticClient) {
482 mDiagnosticLock.lock();
483 try {
484 for (int diagnostic : diagnosticClient.getDiagnosticArray()) {
485 unregisterDiagnosticListener(
486 diagnostic, diagnosticClient.getICarDiagnosticEventListener());
487 }
488 mClients.remove(diagnosticClient);
489 } finally {
490 mDiagnosticLock.unlock();
491 }
492 }
493
494 private class DiagnosticDispatchHandler extends Handler {
495 private static final long DIAGNOSTIC_DISPATCH_MIN_INTERVAL_MS = 16; // over 60Hz
496
497 private static final int MSG_DIAGNOSTIC_DATA = 0;
498
499 private long mLastDiagnosticDispatchTime = -1;
500 private int mFreeListIndex = 0;
501 private final LinkedList<CarDiagnosticEvent>[] mDiagnosticDataList = new LinkedList[2];
502
503 private DiagnosticDispatchHandler(Looper looper) {
504 super(looper);
505 for (int i = 0; i < mDiagnosticDataList.length; i++) {
506 mDiagnosticDataList[i] = new LinkedList<CarDiagnosticEvent>();
507 }
508 }
509
510 private synchronized void handleDiagnosticEvents(List<CarDiagnosticEvent> data) {
511 LinkedList<CarDiagnosticEvent> list = mDiagnosticDataList[mFreeListIndex];
512 list.addAll(data);
513 requestDispatchLocked();
514 }
515
516 private synchronized void handleDiagnosticEvent(CarDiagnosticEvent event) {
517 LinkedList<CarDiagnosticEvent> list = mDiagnosticDataList[mFreeListIndex];
518 list.add(event);
519 requestDispatchLocked();
520 }
521
522 private void requestDispatchLocked() {
523 Message msg = obtainMessage(MSG_DIAGNOSTIC_DATA);
524 long now = SystemClock.uptimeMillis();
525 long delta = now - mLastDiagnosticDispatchTime;
526 if (delta > DIAGNOSTIC_DISPATCH_MIN_INTERVAL_MS) {
527 sendMessage(msg);
528 } else {
529 sendMessageDelayed(msg, DIAGNOSTIC_DISPATCH_MIN_INTERVAL_MS - delta);
530 }
531 }
532
533 @Override
534 public void handleMessage(Message msg) {
535 switch (msg.what) {
536 case MSG_DIAGNOSTIC_DATA:
537 doHandleDiagnosticData();
538 break;
539 default:
540 break;
541 }
542 }
543
544 private void doHandleDiagnosticData() {
545 List<CarDiagnosticEvent> listToDispatch = null;
546 synchronized (this) {
547 mLastDiagnosticDispatchTime = SystemClock.uptimeMillis();
548 int nonFreeListIndex = mFreeListIndex ^ 0x1;
549 List<CarDiagnosticEvent> nonFreeList = mDiagnosticDataList[nonFreeListIndex];
550 List<CarDiagnosticEvent> freeList = mDiagnosticDataList[mFreeListIndex];
551 if (nonFreeList.size() > 0) {
552 // copy again, but this should not be normal case
553 nonFreeList.addAll(freeList);
554 listToDispatch = nonFreeList;
555 freeList.clear();
556 } else if (freeList.size() > 0) {
557 listToDispatch = freeList;
558 mFreeListIndex = nonFreeListIndex;
559 }
560 }
561 // leave this part outside lock so that time-taking dispatching can be done without
562 // blocking diagnostic event notification.
563 if (listToDispatch != null) {
564 processDiagnosticData(listToDispatch);
565 listToDispatch.clear();
566 }
567 }
568 }
569
570 /** internal instance for pending client request */
571 private class DiagnosticClient implements Listeners.IListener {
572 /** callback for diagnostic events */
573 private final ICarDiagnosticEventListener mListener;
574
575 private final Set<Integer> mActiveDiagnostics = new HashSet<>();
576
577 /** when false, it is already released */
578 private volatile boolean mActive = true;
579
580 DiagnosticClient(ICarDiagnosticEventListener listener) {
581 this.mListener = listener;
582 }
583
584 @Override
585 public boolean equals(Object o) {
586 if (o instanceof CarDiagnosticService.DiagnosticClient
587 && mListener.asBinder()
588 == ((CarDiagnosticService.DiagnosticClient) o).mListener.asBinder()) {
589 return true;
590 }
591 return false;
592 }
593
594 boolean isHoldingListenerBinder(IBinder listenerBinder) {
595 return mListener.asBinder() == listenerBinder;
596 }
597
598 void addDiagnostic(int frameType) {
599 mActiveDiagnostics.add(frameType);
600 }
601
602 void removeDiagnostic(int frameType) {
603 mActiveDiagnostics.remove(frameType);
604 }
605
606 int getNumberOfActiveDiagnostic() {
607 return mActiveDiagnostics.size();
608 }
609
610 int[] getDiagnosticArray() {
611 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray();
612 }
613
614 ICarDiagnosticEventListener getICarDiagnosticEventListener() {
615 return mListener;
616 }
617
618 /** Client dead. should remove all diagnostic requests from client */
619 @Override
620 public void binderDied() {
621 mListener.asBinder().unlinkToDeath(this, 0);
622 removeClient(this);
623 }
624
625 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
626 if (events.size() == 0) {
627 return;
628 }
629 if (mActive) {
630 try {
631 mListener.onDiagnosticEvents(events);
632 } catch (RemoteException e) {
633 //ignore. crash will be handled by death handler
634 }
635 } else {
636 }
637 }
638
639 @Override
640 public void release() {
641 if (mActive) {
642 mListener.asBinder().unlinkToDeath(this, 0);
643 mActiveDiagnostics.clear();
644 mActive = false;
645 }
646 }
647 }
648
649 private static abstract class DiagnosticRecord {
650 private final ReentrantLock mLock;
651 protected boolean mEnabled = false;
652
653 DiagnosticRecord(ReentrantLock lock) {
654 mLock = lock;
655 }
656
657 void lock() {
658 mLock.lock();
659 }
660
661 void unlock() {
662 mLock.unlock();
663 }
664
665 boolean isEnabled() {
666 return mEnabled;
667 }
668
669 void enable() {
670 mEnabled = true;
671 }
672
673 abstract boolean disableIfNeeded();
674 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent);
675 }
676
677 private static class LiveFrameRecord extends DiagnosticRecord {
678 /** Store the most recent live-frame. */
679 CarDiagnosticEvent mLastEvent = null;
680
681 LiveFrameRecord(ReentrantLock lock) {
682 super(lock);
683 }
684
685 @Override
686 boolean disableIfNeeded() {
687 if (!mEnabled) return false;
688 mEnabled = false;
689 mLastEvent = null;
690 return true;
691 }
692
693 @Override
694 CarDiagnosticEvent update(CarDiagnosticEvent newEvent) {
695 newEvent = Objects.requireNonNull(newEvent);
696 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent))
697 mLastEvent = newEvent;
698 return mLastEvent;
699 }
700
701 CarDiagnosticEvent getLastEvent() {
702 return mLastEvent;
703 }
704 }
705
706 private static class FreezeFrameRecord extends DiagnosticRecord {
707 /** Store the timestamp --> freeze frame mapping. */
708 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>();
709
710 FreezeFrameRecord(ReentrantLock lock) {
711 super(lock);
712 }
713
714 @Override
715 boolean disableIfNeeded() {
716 if (!mEnabled) return false;
717 mEnabled = false;
718 clearEvents();
719 return true;
720 }
721
722 void clearEvents() {
723 mEvents.clear();
724 }
725
726 @Override
727 CarDiagnosticEvent update(CarDiagnosticEvent newEvent) {
728 mEvents.put(newEvent.timestamp, newEvent);
729 return newEvent;
730 }
731
732 long[] getFreezeFrameTimestamps() {
733 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray();
734 }
735
736 CarDiagnosticEvent getEvent(long timestamp) {
737 return mEvents.get(timestamp);
738 }
739
740 Iterable<CarDiagnosticEvent> getEvents() {
741 return mEvents.values();
742 }
743 }
744
745 @Override
746 public void dump(PrintWriter writer) {
747 writer.println("*CarDiagnosticService*");
748 writer.println("**last events for diagnostics**");
749 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) {
750 writer.println("last live frame event: ");
751 writer.println(mLiveFrameDiagnosticRecord.getLastEvent());
752 }
753 writer.println("freeze frame events: ");
754 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println);
755 writer.println("**clients**");
756 try {
757 for (DiagnosticClient client : mClients) {
758 if (client != null) {
759 try {
760 writer.println(
761 "binder:"
762 + client.mListener
763 + " active diagnostics:"
764 + Arrays.toString(client.getDiagnosticArray()));
765 } catch (ConcurrentModificationException e) {
766 writer.println("concurrent modification happened");
767 }
768 } else {
769 writer.println("null client");
770 }
771 }
772 } catch (ConcurrentModificationException e) {
773 writer.println("concurrent modification happened");
774 }
775 writer.println("**diagnostic listeners**");
776 try {
777 for (int diagnostic : mDiagnosticListeners.keySet()) {
778 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic);
779 if (diagnosticListeners != null) {
780 writer.println(
781 " Diagnostic:"
782 + diagnostic
783 + " num client:"
784 + diagnosticListeners.getNumberOfClients()
785 + " rate:"
786 + diagnosticListeners.getRate());
787 }
788 }
789 } catch (ConcurrentModificationException e) {
790 writer.println("concurrent modification happened");
791 }
792 }
793}