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