blob: c14a3851eef2503b9b31ed3d8ec95b585ad77d18 [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
Enrico Granata24790342017-02-15 14:46:34 -080019import android.annotation.NonNull;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080020import android.annotation.Nullable;
Enrico Granata3c7a6662017-02-23 18:07:59 -080021import android.car.Car;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080022import android.car.annotation.FutureFeature;
23import android.car.hardware.CarDiagnosticEvent;
24import android.car.hardware.CarDiagnosticManager;
25import android.car.hardware.ICarDiagnostic;
26import android.car.hardware.ICarDiagnosticEventListener;
27import android.content.Context;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080028import android.os.IBinder;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080029import android.os.RemoteException;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080030import android.util.ArrayMap;
31import android.util.Log;
Enrico Granata3c7a6662017-02-23 18:07:59 -080032import com.android.car.internal.CarPermission;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080033import com.android.car.Listeners.ClientWithRate;
34import com.android.car.hal.DiagnosticHalService;
35import com.android.internal.annotations.GuardedBy;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.ConcurrentModificationException;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.LinkedList;
43import java.util.List;
44import java.util.Objects;
45import java.util.Set;
46import java.util.concurrent.locks.ReentrantLock;
47
48@FutureFeature
49/** @hide */
50public class CarDiagnosticService extends ICarDiagnostic.Stub
51 implements CarServiceBase, DiagnosticHalService.DiagnosticListener {
Enrico Granata5c56d2a2017-02-07 15:38:12 -080052 /** 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
Enrico Granata3c7a6662017-02-23 18:07:59 -080076 private final CarPermission mDiagnosticReadPermission;
77
78 private final CarPermission mDiagnosticClearPermission;
79
Enrico Granata5c56d2a2017-02-07 15:38:12 -080080 public CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal) {
81 mContext = context;
82 mDiagnosticHal = diagnosticHal;
Enrico Granata3c7a6662017-02-23 18:07:59 -080083 mDiagnosticReadPermission = new CarPermission(mContext, Car.PERMISSION_CAR_DIAGNOSTIC_READ);
84 mDiagnosticClearPermission = new CarPermission(mContext,
85 Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
Enrico Granata5c56d2a2017-02-07 15:38:12 -080086 }
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
Enrico Granata24790342017-02-15 14:46:34 -0800100 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800101 private CarDiagnosticEvent setInitialLiveFrame() {
Enrico Granata24790342017-02-15 14:46:34 -0800102 CarDiagnosticEvent liveFrame = null;
103 if(mDiagnosticHal.getDiagnosticCapabilities().isLiveFrameSupported()) {
104 liveFrame = setRecentmostLiveFrame(mDiagnosticHal.getCurrentLiveFrame());
105 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800106 return liveFrame;
107 }
108
109 private void setInitialFreezeFrames() {
Enrico Granata24790342017-02-15 14:46:34 -0800110 if(mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameSupported() &&
111 mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameInfoSupported()) {
112 long[] timestamps = mDiagnosticHal.getFreezeFrameTimestamps();
113 if (timestamps != null) {
114 for (long timestamp : timestamps) {
115 setRecentmostFreezeFrame(mDiagnosticHal.getFreezeFrame(timestamp));
116 }
117 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800118 }
119 }
120
Enrico Granata24790342017-02-15 14:46:34 -0800121 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800122 private CarDiagnosticEvent setRecentmostLiveFrame(final CarDiagnosticEvent event) {
Enrico Granata24790342017-02-15 14:46:34 -0800123 if (event != null) {
124 return mLiveFrameDiagnosticRecord.update(event.checkLiveFrame());
125 }
126 return null;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800127 }
128
Enrico Granata24790342017-02-15 14:46:34 -0800129 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800130 private CarDiagnosticEvent setRecentmostFreezeFrame(final CarDiagnosticEvent event) {
Enrico Granata24790342017-02-15 14:46:34 -0800131 if (event != null) {
132 return mFreezeFrameDiagnosticRecords.update(event.checkFreezeFrame());
133 }
134 return null;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800135 }
136
137 @Override
138 public void release() {
139 mDiagnosticLock.lock();
140 try {
141 mDiagnosticListeners.forEach(
142 (Integer frameType, Listeners diagnosticListeners) ->
143 diagnosticListeners.release());
144 mDiagnosticListeners.clear();
145 mLiveFrameDiagnosticRecord.disableIfNeeded();
146 mFreezeFrameDiagnosticRecords.disableIfNeeded();
147 mClients.clear();
148 } finally {
149 mDiagnosticLock.unlock();
150 }
151 }
152
153 private void processDiagnosticData(List<CarDiagnosticEvent> events) {
154 ArrayMap<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> eventsByClient =
155 new ArrayMap<>();
156
157 Listeners<DiagnosticClient> listeners = null;
158
159 mDiagnosticLock.lock();
160 for (CarDiagnosticEvent event : events) {
161 if (event.isLiveFrame()) {
162 // record recent-most live frame information
163 setRecentmostLiveFrame(event);
164 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
165 } else if (event.isFreezeFrame()) {
166 setRecentmostFreezeFrame(event);
167 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
168 } else {
169 Log.w(
170 CarLog.TAG_DIAGNOSTIC,
171 String.format("received unknown diagnostic event: %s", event));
172 continue;
173 }
174
175 if (null != listeners) {
176 for (ClientWithRate<DiagnosticClient> clientWithRate : listeners.getClients()) {
177 DiagnosticClient client = clientWithRate.getClient();
178 List<CarDiagnosticEvent> clientEvents = eventsByClient.computeIfAbsent(client,
179 (DiagnosticClient diagnosticClient) -> new LinkedList<>());
180 clientEvents.add(event);
181 }
182 }
183 }
184 mDiagnosticLock.unlock();
185
186 for (ArrayMap.Entry<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> entry :
187 eventsByClient.entrySet()) {
188 CarDiagnosticService.DiagnosticClient client = entry.getKey();
189 List<CarDiagnosticEvent> clientEvents = entry.getValue();
190
191 client.dispatchDiagnosticUpdate(clientEvents);
192 }
193 }
194
195 /** Received diagnostic data from car. */
196 @Override
197 public void onDiagnosticEvents(List<CarDiagnosticEvent> events) {
198 processDiagnosticData(events);
199 }
200
201 private List<CarDiagnosticEvent> getCachedEventsLocked(int frameType) {
202 ArrayList<CarDiagnosticEvent> events = new ArrayList<>();
203 switch (frameType) {
204 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
205 mLiveFrameDiagnosticRecord.lock();
206 events.add(mLiveFrameDiagnosticRecord.getLastEvent());
207 mLiveFrameDiagnosticRecord.unlock();
208 break;
209 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
210 mFreezeFrameDiagnosticRecords.lock();
211 mFreezeFrameDiagnosticRecords.getEvents().forEach(events::add);
212 mFreezeFrameDiagnosticRecords.unlock();
213 break;
214 default: break;
215 }
216 return events;
217 }
218
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800219 @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 {
Enrico Granata3c7a6662017-02-23 18:07:59 -0800228 mDiagnosticReadPermission.assertGranted();
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800229 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
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800299 private boolean startDiagnostic(int frameType, int rate) {
300 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("starting diagnostic %s at rate %d",
301 frameType, rate));
302 DiagnosticHalService diagnosticHal = getDiagnosticHal();
303 if (diagnosticHal != null) {
304 if (!diagnosticHal.isReady()) {
305 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
306 return false;
307 }
308 switch (frameType) {
309 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
310 if (mLiveFrameDiagnosticRecord.isEnabled()) {
311 return true;
312 }
313 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
314 rate)) {
315 mLiveFrameDiagnosticRecord.enable();
316 return true;
317 }
318 break;
319 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
320 if (mFreezeFrameDiagnosticRecords.isEnabled()) {
321 return true;
322 }
323 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
324 rate)) {
325 mFreezeFrameDiagnosticRecords.enable();
326 return true;
327 }
328 break;
329 }
330 }
331 return false;
332 }
333
334 @Override
335 public void unregisterDiagnosticListener(
336 int frameType, ICarDiagnosticEventListener listener) {
337 boolean shouldStopDiagnostic = false;
338 boolean shouldRestartDiagnostic = false;
339 int newRate = 0;
340 mDiagnosticLock.lock();
341 try {
342 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener);
343 if (diagnosticClient == null) {
344 Log.i(
345 CarLog.TAG_DIAGNOSTIC,
346 String.format(
347 "trying to unregister diagnostic client %s for %s which is not registered",
348 listener, frameType));
349 // never registered or already unregistered.
350 return;
351 }
352 diagnosticClient.removeDiagnostic(frameType);
353 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) {
354 diagnosticClient.release();
355 mClients.remove(diagnosticClient);
356 }
357 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType);
358 if (diagnosticListeners == null) {
359 // diagnostic not active
360 return;
361 }
362 ClientWithRate<DiagnosticClient> clientWithRate =
363 diagnosticListeners.findClientWithRate(diagnosticClient);
364 if (clientWithRate == null) {
365 return;
366 }
367 diagnosticListeners.removeClientWithRate(clientWithRate);
368 if (diagnosticListeners.getNumberOfClients() == 0) {
369 shouldStopDiagnostic = true;
370 mDiagnosticListeners.remove(frameType);
371 } else if (diagnosticListeners.updateRate()) { // rate changed
372 newRate = diagnosticListeners.getRate();
373 shouldRestartDiagnostic = true;
374 }
375 } finally {
376 mDiagnosticLock.unlock();
377 }
378 Log.i(
379 CarLog.TAG_DIAGNOSTIC,
380 String.format(
381 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s",
382 shouldStopDiagnostic, shouldRestartDiagnostic, frameType));
383 if (shouldStopDiagnostic) {
384 stopDiagnostic(frameType);
385 } else if (shouldRestartDiagnostic) {
386 startDiagnostic(frameType, newRate);
387 }
388 }
389
390 private void stopDiagnostic(int frameType) {
391 DiagnosticHalService diagnosticHal = getDiagnosticHal();
392 if (diagnosticHal == null || !diagnosticHal.isReady()) {
393 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
394 return;
395 }
396 switch (frameType) {
397 case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
398 if (mLiveFrameDiagnosticRecord.disableIfNeeded())
399 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
400 break;
401 case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
402 if (mFreezeFrameDiagnosticRecords.disableIfNeeded())
403 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
404 break;
405 }
406 }
407
408 private DiagnosticHalService getDiagnosticHal() {
409 return mDiagnosticHal;
410 }
411
412 // ICarDiagnostic implementations
413
414 @Override
415 public CarDiagnosticEvent getLatestLiveFrame() {
416 mLiveFrameDiagnosticRecord.lock();
417 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent();
418 mLiveFrameDiagnosticRecord.unlock();
419 return liveFrame;
420 }
421
422 @Override
423 public long[] getFreezeFrameTimestamps() {
424 mFreezeFrameDiagnosticRecords.lock();
425 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps();
426 mFreezeFrameDiagnosticRecords.unlock();
427 return timestamps;
428 }
429
430 @Override
431 @Nullable
432 public CarDiagnosticEvent getFreezeFrame(long timestamp) {
433 mFreezeFrameDiagnosticRecords.lock();
434 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp);
435 mFreezeFrameDiagnosticRecords.unlock();
436 return freezeFrame;
437 }
438
439 @Override
440 public boolean clearFreezeFrames(long... timestamps) {
Enrico Granata3c7a6662017-02-23 18:07:59 -0800441 mDiagnosticClearPermission.assertGranted();
Enrico Granata24790342017-02-15 14:46:34 -0800442 if (mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameClearSupported()) {
443 mFreezeFrameDiagnosticRecords.lock();
444 mDiagnosticHal.clearFreezeFrames(timestamps);
445 mFreezeFrameDiagnosticRecords.clearEvents();
446 mFreezeFrameDiagnosticRecords.unlock();
447 return true;
448 }
449 return false;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800450 }
451
452 /**
453 * Find DiagnosticClient from client list and return it. This should be called with mClients
454 * locked.
455 *
456 * @param listener
457 * @return null if not found.
458 */
459 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked(
460 ICarDiagnosticEventListener listener) {
461 IBinder binder = listener.asBinder();
462 for (DiagnosticClient diagnosticClient : mClients) {
463 if (diagnosticClient.isHoldingListenerBinder(binder)) {
464 return diagnosticClient;
465 }
466 }
467 return null;
468 }
469
470 private void removeClient(DiagnosticClient diagnosticClient) {
471 mDiagnosticLock.lock();
472 try {
473 for (int diagnostic : diagnosticClient.getDiagnosticArray()) {
474 unregisterDiagnosticListener(
475 diagnostic, diagnosticClient.getICarDiagnosticEventListener());
476 }
477 mClients.remove(diagnosticClient);
478 } finally {
479 mDiagnosticLock.unlock();
480 }
481 }
482
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800483 /** internal instance for pending client request */
484 private class DiagnosticClient implements Listeners.IListener {
485 /** callback for diagnostic events */
486 private final ICarDiagnosticEventListener mListener;
487
488 private final Set<Integer> mActiveDiagnostics = new HashSet<>();
489
490 /** when false, it is already released */
491 private volatile boolean mActive = true;
492
493 DiagnosticClient(ICarDiagnosticEventListener listener) {
494 this.mListener = listener;
495 }
496
497 @Override
498 public boolean equals(Object o) {
499 if (o instanceof CarDiagnosticService.DiagnosticClient
500 && mListener.asBinder()
501 == ((CarDiagnosticService.DiagnosticClient) o).mListener.asBinder()) {
502 return true;
503 }
504 return false;
505 }
506
507 boolean isHoldingListenerBinder(IBinder listenerBinder) {
508 return mListener.asBinder() == listenerBinder;
509 }
510
511 void addDiagnostic(int frameType) {
512 mActiveDiagnostics.add(frameType);
513 }
514
515 void removeDiagnostic(int frameType) {
516 mActiveDiagnostics.remove(frameType);
517 }
518
519 int getNumberOfActiveDiagnostic() {
520 return mActiveDiagnostics.size();
521 }
522
523 int[] getDiagnosticArray() {
524 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray();
525 }
526
527 ICarDiagnosticEventListener getICarDiagnosticEventListener() {
528 return mListener;
529 }
530
531 /** Client dead. should remove all diagnostic requests from client */
532 @Override
533 public void binderDied() {
534 mListener.asBinder().unlinkToDeath(this, 0);
535 removeClient(this);
536 }
537
538 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
539 if (events.size() == 0) {
540 return;
541 }
542 if (mActive) {
543 try {
544 mListener.onDiagnosticEvents(events);
545 } catch (RemoteException e) {
546 //ignore. crash will be handled by death handler
547 }
548 } else {
549 }
550 }
551
552 @Override
553 public void release() {
554 if (mActive) {
555 mListener.asBinder().unlinkToDeath(this, 0);
556 mActiveDiagnostics.clear();
557 mActive = false;
558 }
559 }
560 }
561
562 private static abstract class DiagnosticRecord {
563 private final ReentrantLock mLock;
564 protected boolean mEnabled = false;
565
566 DiagnosticRecord(ReentrantLock lock) {
567 mLock = lock;
568 }
569
570 void lock() {
571 mLock.lock();
572 }
573
574 void unlock() {
575 mLock.unlock();
576 }
577
578 boolean isEnabled() {
579 return mEnabled;
580 }
581
582 void enable() {
583 mEnabled = true;
584 }
585
586 abstract boolean disableIfNeeded();
587 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent);
588 }
589
590 private static class LiveFrameRecord extends DiagnosticRecord {
591 /** Store the most recent live-frame. */
592 CarDiagnosticEvent mLastEvent = null;
593
594 LiveFrameRecord(ReentrantLock lock) {
595 super(lock);
596 }
597
598 @Override
599 boolean disableIfNeeded() {
600 if (!mEnabled) return false;
601 mEnabled = false;
602 mLastEvent = null;
603 return true;
604 }
605
606 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800607 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800608 newEvent = Objects.requireNonNull(newEvent);
609 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent))
610 mLastEvent = newEvent;
611 return mLastEvent;
612 }
613
614 CarDiagnosticEvent getLastEvent() {
615 return mLastEvent;
616 }
617 }
618
619 private static class FreezeFrameRecord extends DiagnosticRecord {
620 /** Store the timestamp --> freeze frame mapping. */
621 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>();
622
623 FreezeFrameRecord(ReentrantLock lock) {
624 super(lock);
625 }
626
627 @Override
628 boolean disableIfNeeded() {
629 if (!mEnabled) return false;
630 mEnabled = false;
631 clearEvents();
632 return true;
633 }
634
635 void clearEvents() {
636 mEvents.clear();
637 }
638
639 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800640 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800641 mEvents.put(newEvent.timestamp, newEvent);
642 return newEvent;
643 }
644
645 long[] getFreezeFrameTimestamps() {
646 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray();
647 }
648
649 CarDiagnosticEvent getEvent(long timestamp) {
650 return mEvents.get(timestamp);
651 }
652
653 Iterable<CarDiagnosticEvent> getEvents() {
654 return mEvents.values();
655 }
656 }
657
658 @Override
659 public void dump(PrintWriter writer) {
660 writer.println("*CarDiagnosticService*");
661 writer.println("**last events for diagnostics**");
662 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) {
663 writer.println("last live frame event: ");
664 writer.println(mLiveFrameDiagnosticRecord.getLastEvent());
665 }
666 writer.println("freeze frame events: ");
667 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println);
668 writer.println("**clients**");
669 try {
670 for (DiagnosticClient client : mClients) {
671 if (client != null) {
672 try {
673 writer.println(
674 "binder:"
675 + client.mListener
676 + " active diagnostics:"
677 + Arrays.toString(client.getDiagnosticArray()));
678 } catch (ConcurrentModificationException e) {
679 writer.println("concurrent modification happened");
680 }
681 } else {
682 writer.println("null client");
683 }
684 }
685 } catch (ConcurrentModificationException e) {
686 writer.println("concurrent modification happened");
687 }
688 writer.println("**diagnostic listeners**");
689 try {
690 for (int diagnostic : mDiagnosticListeners.keySet()) {
691 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic);
692 if (diagnosticListeners != null) {
693 writer.println(
694 " Diagnostic:"
695 + diagnostic
696 + " num client:"
697 + diagnosticListeners.getNumberOfClients()
698 + " rate:"
699 + diagnosticListeners.getRate());
700 }
701 }
702 } catch (ConcurrentModificationException e) {
703 writer.println("concurrent modification happened");
704 }
705 }
706}