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