blob: a4afc35f80bf9f0741ed7f6ef76ef49e54a89650 [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
Enrico Granata5c56d2a2017-02-07 15:38:12 -080049/** @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 Granataae25b922017-07-07 11:28:15 -070083 mDiagnosticReadPermission = new CarPermission(mContext,
84 Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL);
Enrico Granata3c7a6662017-02-23 18:07:59 -080085 mDiagnosticClearPermission = new CarPermission(mContext,
86 Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
Enrico Granata5c56d2a2017-02-07 15:38:12 -080087 }
88
89 @Override
90 public void init() {
91 mDiagnosticLock.lock();
92 try {
93 mDiagnosticHal.setDiagnosticListener(this);
94 setInitialLiveFrame();
95 setInitialFreezeFrames();
96 } finally {
97 mDiagnosticLock.unlock();
98 }
99 }
100
Enrico Granata24790342017-02-15 14:46:34 -0800101 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800102 private CarDiagnosticEvent setInitialLiveFrame() {
Enrico Granata24790342017-02-15 14:46:34 -0800103 CarDiagnosticEvent liveFrame = null;
104 if(mDiagnosticHal.getDiagnosticCapabilities().isLiveFrameSupported()) {
105 liveFrame = setRecentmostLiveFrame(mDiagnosticHal.getCurrentLiveFrame());
106 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800107 return liveFrame;
108 }
109
110 private void setInitialFreezeFrames() {
Enrico Granata24790342017-02-15 14:46:34 -0800111 if(mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameSupported() &&
112 mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameInfoSupported()) {
113 long[] timestamps = mDiagnosticHal.getFreezeFrameTimestamps();
114 if (timestamps != null) {
115 for (long timestamp : timestamps) {
116 setRecentmostFreezeFrame(mDiagnosticHal.getFreezeFrame(timestamp));
117 }
118 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800119 }
120 }
121
Enrico Granata24790342017-02-15 14:46:34 -0800122 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800123 private CarDiagnosticEvent setRecentmostLiveFrame(final CarDiagnosticEvent event) {
Enrico Granata24790342017-02-15 14:46:34 -0800124 if (event != null) {
125 return mLiveFrameDiagnosticRecord.update(event.checkLiveFrame());
126 }
127 return null;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800128 }
129
Enrico Granata24790342017-02-15 14:46:34 -0800130 @Nullable
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800131 private CarDiagnosticEvent setRecentmostFreezeFrame(final CarDiagnosticEvent event) {
Enrico Granata24790342017-02-15 14:46:34 -0800132 if (event != null) {
133 return mFreezeFrameDiagnosticRecords.update(event.checkFreezeFrame());
134 }
135 return null;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800136 }
137
138 @Override
139 public void release() {
140 mDiagnosticLock.lock();
141 try {
142 mDiagnosticListeners.forEach(
143 (Integer frameType, Listeners diagnosticListeners) ->
144 diagnosticListeners.release());
145 mDiagnosticListeners.clear();
146 mLiveFrameDiagnosticRecord.disableIfNeeded();
147 mFreezeFrameDiagnosticRecords.disableIfNeeded();
148 mClients.clear();
149 } finally {
150 mDiagnosticLock.unlock();
151 }
152 }
153
154 private void processDiagnosticData(List<CarDiagnosticEvent> events) {
155 ArrayMap<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> eventsByClient =
156 new ArrayMap<>();
157
158 Listeners<DiagnosticClient> listeners = null;
159
160 mDiagnosticLock.lock();
161 for (CarDiagnosticEvent event : events) {
162 if (event.isLiveFrame()) {
163 // record recent-most live frame information
164 setRecentmostLiveFrame(event);
Enrico Granata5e075da2017-04-28 15:15:44 -0700165 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_LIVE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800166 } else if (event.isFreezeFrame()) {
167 setRecentmostFreezeFrame(event);
Enrico Granata5e075da2017-04-28 15:15:44 -0700168 listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FREEZE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800169 } else {
170 Log.w(
171 CarLog.TAG_DIAGNOSTIC,
172 String.format("received unknown diagnostic event: %s", event));
173 continue;
174 }
175
176 if (null != listeners) {
177 for (ClientWithRate<DiagnosticClient> clientWithRate : listeners.getClients()) {
178 DiagnosticClient client = clientWithRate.getClient();
179 List<CarDiagnosticEvent> clientEvents = eventsByClient.computeIfAbsent(client,
180 (DiagnosticClient diagnosticClient) -> new LinkedList<>());
181 clientEvents.add(event);
182 }
183 }
184 }
185 mDiagnosticLock.unlock();
186
187 for (ArrayMap.Entry<CarDiagnosticService.DiagnosticClient, List<CarDiagnosticEvent>> entry :
188 eventsByClient.entrySet()) {
189 CarDiagnosticService.DiagnosticClient client = entry.getKey();
190 List<CarDiagnosticEvent> clientEvents = entry.getValue();
191
192 client.dispatchDiagnosticUpdate(clientEvents);
193 }
194 }
195
196 /** Received diagnostic data from car. */
197 @Override
198 public void onDiagnosticEvents(List<CarDiagnosticEvent> events) {
199 processDiagnosticData(events);
200 }
201
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800202 @Override
203 public boolean registerOrUpdateDiagnosticListener(int frameType, int rate,
204 ICarDiagnosticEventListener listener) {
205 boolean shouldStartDiagnostics = false;
206 CarDiagnosticService.DiagnosticClient diagnosticClient = null;
207 Integer oldRate = null;
208 Listeners<DiagnosticClient> diagnosticListeners = null;
209 mDiagnosticLock.lock();
210 try {
Enrico Granata3c7a6662017-02-23 18:07:59 -0800211 mDiagnosticReadPermission.assertGranted();
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800212 diagnosticClient = findDiagnosticClientLocked(listener);
213 Listeners.ClientWithRate<DiagnosticClient> diagnosticClientWithRate = null;
214 if (diagnosticClient == null) {
215 diagnosticClient = new DiagnosticClient(listener);
216 try {
217 listener.asBinder().linkToDeath(diagnosticClient, 0);
218 } catch (RemoteException e) {
219 Log.w(
220 CarLog.TAG_DIAGNOSTIC,
221 String.format(
222 "received RemoteException trying to register listener for %s",
223 frameType));
224 return false;
225 }
226 mClients.add(diagnosticClient);
227 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800228 diagnosticListeners = mDiagnosticListeners.get(frameType);
229 if (diagnosticListeners == null) {
230 diagnosticListeners = new Listeners<>(rate);
231 mDiagnosticListeners.put(frameType, diagnosticListeners);
232 shouldStartDiagnostics = true;
233 } else {
234 oldRate = diagnosticListeners.getRate();
235 diagnosticClientWithRate =
236 diagnosticListeners.findClientWithRate(diagnosticClient);
237 }
238 if (diagnosticClientWithRate == null) {
239 diagnosticClientWithRate =
240 new ClientWithRate<>(diagnosticClient, rate);
241 diagnosticListeners.addClientWithRate(diagnosticClientWithRate);
242 } else {
243 diagnosticClientWithRate.setRate(rate);
244 }
245 if (diagnosticListeners.getRate() > rate) {
246 diagnosticListeners.setRate(rate);
247 shouldStartDiagnostics = true;
248 }
249 diagnosticClient.addDiagnostic(frameType);
250 } finally {
251 mDiagnosticLock.unlock();
252 }
253 Log.i(
254 CarLog.TAG_DIAGNOSTIC,
255 String.format(
256 "shouldStartDiagnostics = %s for %s at rate %d",
257 shouldStartDiagnostics, frameType, rate));
258 // start diagnostic outside lock as it can take time.
259 if (shouldStartDiagnostics) {
260 if (!startDiagnostic(frameType, rate)) {
261 // failed. so remove from active diagnostic list.
262 Log.w(CarLog.TAG_DIAGNOSTIC, "startDiagnostic failed");
263 mDiagnosticLock.lock();
264 try {
265 diagnosticClient.removeDiagnostic(frameType);
266 if (oldRate != null) {
267 diagnosticListeners.setRate(oldRate);
268 } else {
269 mDiagnosticListeners.remove(frameType);
270 }
271 } finally {
272 mDiagnosticLock.unlock();
273 }
274 return false;
275 }
276 }
277 return true;
278 }
279
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800280 private boolean startDiagnostic(int frameType, int rate) {
281 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("starting diagnostic %s at rate %d",
282 frameType, rate));
283 DiagnosticHalService diagnosticHal = getDiagnosticHal();
284 if (diagnosticHal != null) {
285 if (!diagnosticHal.isReady()) {
286 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
287 return false;
288 }
289 switch (frameType) {
Enrico Granata5e075da2017-04-28 15:15:44 -0700290 case CarDiagnosticManager.FRAME_TYPE_LIVE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800291 if (mLiveFrameDiagnosticRecord.isEnabled()) {
292 return true;
293 }
Enrico Granata5e075da2017-04-28 15:15:44 -0700294 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_LIVE,
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800295 rate)) {
296 mLiveFrameDiagnosticRecord.enable();
297 return true;
298 }
299 break;
Enrico Granata5e075da2017-04-28 15:15:44 -0700300 case CarDiagnosticManager.FRAME_TYPE_FREEZE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800301 if (mFreezeFrameDiagnosticRecords.isEnabled()) {
302 return true;
303 }
Enrico Granata5e075da2017-04-28 15:15:44 -0700304 if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FREEZE,
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800305 rate)) {
306 mFreezeFrameDiagnosticRecords.enable();
307 return true;
308 }
309 break;
310 }
311 }
312 return false;
313 }
314
315 @Override
316 public void unregisterDiagnosticListener(
317 int frameType, ICarDiagnosticEventListener listener) {
318 boolean shouldStopDiagnostic = false;
319 boolean shouldRestartDiagnostic = false;
320 int newRate = 0;
321 mDiagnosticLock.lock();
322 try {
323 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener);
324 if (diagnosticClient == null) {
325 Log.i(
326 CarLog.TAG_DIAGNOSTIC,
327 String.format(
328 "trying to unregister diagnostic client %s for %s which is not registered",
329 listener, frameType));
330 // never registered or already unregistered.
331 return;
332 }
333 diagnosticClient.removeDiagnostic(frameType);
334 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) {
335 diagnosticClient.release();
336 mClients.remove(diagnosticClient);
337 }
338 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType);
339 if (diagnosticListeners == null) {
340 // diagnostic not active
341 return;
342 }
343 ClientWithRate<DiagnosticClient> clientWithRate =
344 diagnosticListeners.findClientWithRate(diagnosticClient);
345 if (clientWithRate == null) {
346 return;
347 }
348 diagnosticListeners.removeClientWithRate(clientWithRate);
349 if (diagnosticListeners.getNumberOfClients() == 0) {
350 shouldStopDiagnostic = true;
351 mDiagnosticListeners.remove(frameType);
352 } else if (diagnosticListeners.updateRate()) { // rate changed
353 newRate = diagnosticListeners.getRate();
354 shouldRestartDiagnostic = true;
355 }
356 } finally {
357 mDiagnosticLock.unlock();
358 }
359 Log.i(
360 CarLog.TAG_DIAGNOSTIC,
361 String.format(
362 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s",
363 shouldStopDiagnostic, shouldRestartDiagnostic, frameType));
364 if (shouldStopDiagnostic) {
365 stopDiagnostic(frameType);
366 } else if (shouldRestartDiagnostic) {
367 startDiagnostic(frameType, newRate);
368 }
369 }
370
371 private void stopDiagnostic(int frameType) {
372 DiagnosticHalService diagnosticHal = getDiagnosticHal();
373 if (diagnosticHal == null || !diagnosticHal.isReady()) {
374 Log.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
375 return;
376 }
377 switch (frameType) {
Enrico Granata5e075da2017-04-28 15:15:44 -0700378 case CarDiagnosticManager.FRAME_TYPE_LIVE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800379 if (mLiveFrameDiagnosticRecord.disableIfNeeded())
Enrico Granata5e075da2017-04-28 15:15:44 -0700380 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_LIVE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800381 break;
Enrico Granata5e075da2017-04-28 15:15:44 -0700382 case CarDiagnosticManager.FRAME_TYPE_FREEZE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800383 if (mFreezeFrameDiagnosticRecords.disableIfNeeded())
Enrico Granata5e075da2017-04-28 15:15:44 -0700384 diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FREEZE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800385 break;
386 }
387 }
388
389 private DiagnosticHalService getDiagnosticHal() {
390 return mDiagnosticHal;
391 }
392
Enrico Granatac2393682017-05-08 18:09:58 -0700393 // Expose DiagnosticCapabilities
394 public boolean isLiveFrameSupported() {
395 return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported();
396 }
397
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700398 public boolean isFreezeFrameNotificationSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700399 return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported();
400 }
401
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700402 public boolean isGetFreezeFrameSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700403 DiagnosticCapabilities diagnosticCapabilities =
404 getDiagnosticHal().getDiagnosticCapabilities();
405 return diagnosticCapabilities.isFreezeFrameInfoSupported() &&
406 diagnosticCapabilities.isFreezeFrameSupported();
407 }
408
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700409 public boolean isClearFreezeFramesSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700410 DiagnosticCapabilities diagnosticCapabilities =
411 getDiagnosticHal().getDiagnosticCapabilities();
412 return diagnosticCapabilities.isFreezeFrameClearSupported() &&
413 diagnosticCapabilities.isFreezeFrameSupported();
414 }
415
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800416 // ICarDiagnostic implementations
417
418 @Override
419 public CarDiagnosticEvent getLatestLiveFrame() {
420 mLiveFrameDiagnosticRecord.lock();
421 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent();
422 mLiveFrameDiagnosticRecord.unlock();
423 return liveFrame;
424 }
425
426 @Override
427 public long[] getFreezeFrameTimestamps() {
428 mFreezeFrameDiagnosticRecords.lock();
429 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps();
430 mFreezeFrameDiagnosticRecords.unlock();
431 return timestamps;
432 }
433
434 @Override
435 @Nullable
436 public CarDiagnosticEvent getFreezeFrame(long timestamp) {
437 mFreezeFrameDiagnosticRecords.lock();
438 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp);
439 mFreezeFrameDiagnosticRecords.unlock();
440 return freezeFrame;
441 }
442
443 @Override
444 public boolean clearFreezeFrames(long... timestamps) {
Enrico Granata3c7a6662017-02-23 18:07:59 -0800445 mDiagnosticClearPermission.assertGranted();
Enrico Granata24790342017-02-15 14:46:34 -0800446 if (mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameClearSupported()) {
447 mFreezeFrameDiagnosticRecords.lock();
448 mDiagnosticHal.clearFreezeFrames(timestamps);
449 mFreezeFrameDiagnosticRecords.clearEvents();
450 mFreezeFrameDiagnosticRecords.unlock();
451 return true;
452 }
453 return false;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800454 }
455
456 /**
457 * Find DiagnosticClient from client list and return it. This should be called with mClients
458 * locked.
459 *
460 * @param listener
461 * @return null if not found.
462 */
463 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked(
464 ICarDiagnosticEventListener listener) {
465 IBinder binder = listener.asBinder();
466 for (DiagnosticClient diagnosticClient : mClients) {
467 if (diagnosticClient.isHoldingListenerBinder(binder)) {
468 return diagnosticClient;
469 }
470 }
471 return null;
472 }
473
474 private void removeClient(DiagnosticClient diagnosticClient) {
475 mDiagnosticLock.lock();
476 try {
477 for (int diagnostic : diagnosticClient.getDiagnosticArray()) {
478 unregisterDiagnosticListener(
479 diagnostic, diagnosticClient.getICarDiagnosticEventListener());
480 }
481 mClients.remove(diagnosticClient);
482 } finally {
483 mDiagnosticLock.unlock();
484 }
485 }
486
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800487 /** internal instance for pending client request */
488 private class DiagnosticClient implements Listeners.IListener {
489 /** callback for diagnostic events */
490 private final ICarDiagnosticEventListener mListener;
491
492 private final Set<Integer> mActiveDiagnostics = new HashSet<>();
493
494 /** when false, it is already released */
495 private volatile boolean mActive = true;
496
497 DiagnosticClient(ICarDiagnosticEventListener listener) {
498 this.mListener = listener;
499 }
500
501 @Override
502 public boolean equals(Object o) {
Enrico Granata41c5ac62017-07-12 11:00:39 -0700503 return o instanceof DiagnosticClient
504 && mListener.asBinder()
505 == ((DiagnosticClient) o).mListener.asBinder();
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800506 }
507
508 boolean isHoldingListenerBinder(IBinder listenerBinder) {
509 return mListener.asBinder() == listenerBinder;
510 }
511
512 void addDiagnostic(int frameType) {
513 mActiveDiagnostics.add(frameType);
514 }
515
516 void removeDiagnostic(int frameType) {
517 mActiveDiagnostics.remove(frameType);
518 }
519
520 int getNumberOfActiveDiagnostic() {
521 return mActiveDiagnostics.size();
522 }
523
524 int[] getDiagnosticArray() {
525 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray();
526 }
527
528 ICarDiagnosticEventListener getICarDiagnosticEventListener() {
529 return mListener;
530 }
531
532 /** Client dead. should remove all diagnostic requests from client */
533 @Override
534 public void binderDied() {
535 mListener.asBinder().unlinkToDeath(this, 0);
536 removeClient(this);
537 }
538
539 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
Enrico Granata41c5ac62017-07-12 11:00:39 -0700540 if (events.size() != 0 && mActive) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800541 try {
542 mListener.onDiagnosticEvents(events);
543 } catch (RemoteException e) {
544 //ignore. crash will be handled by death handler
545 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800546 }
547 }
548
549 @Override
550 public void release() {
551 if (mActive) {
552 mListener.asBinder().unlinkToDeath(this, 0);
553 mActiveDiagnostics.clear();
554 mActive = false;
555 }
556 }
557 }
558
559 private static abstract class DiagnosticRecord {
560 private final ReentrantLock mLock;
561 protected boolean mEnabled = false;
562
563 DiagnosticRecord(ReentrantLock lock) {
564 mLock = lock;
565 }
566
567 void lock() {
568 mLock.lock();
569 }
570
571 void unlock() {
572 mLock.unlock();
573 }
574
575 boolean isEnabled() {
576 return mEnabled;
577 }
578
579 void enable() {
580 mEnabled = true;
581 }
582
583 abstract boolean disableIfNeeded();
584 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent);
585 }
586
587 private static class LiveFrameRecord extends DiagnosticRecord {
588 /** Store the most recent live-frame. */
589 CarDiagnosticEvent mLastEvent = null;
590
591 LiveFrameRecord(ReentrantLock lock) {
592 super(lock);
593 }
594
595 @Override
596 boolean disableIfNeeded() {
597 if (!mEnabled) return false;
598 mEnabled = false;
599 mLastEvent = null;
600 return true;
601 }
602
603 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800604 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800605 newEvent = Objects.requireNonNull(newEvent);
606 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent))
607 mLastEvent = newEvent;
608 return mLastEvent;
609 }
610
611 CarDiagnosticEvent getLastEvent() {
612 return mLastEvent;
613 }
614 }
615
616 private static class FreezeFrameRecord extends DiagnosticRecord {
617 /** Store the timestamp --> freeze frame mapping. */
618 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>();
619
620 FreezeFrameRecord(ReentrantLock lock) {
621 super(lock);
622 }
623
624 @Override
625 boolean disableIfNeeded() {
626 if (!mEnabled) return false;
627 mEnabled = false;
628 clearEvents();
629 return true;
630 }
631
632 void clearEvents() {
633 mEvents.clear();
634 }
635
636 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800637 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800638 mEvents.put(newEvent.timestamp, newEvent);
639 return newEvent;
640 }
641
642 long[] getFreezeFrameTimestamps() {
643 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray();
644 }
645
646 CarDiagnosticEvent getEvent(long timestamp) {
647 return mEvents.get(timestamp);
648 }
649
650 Iterable<CarDiagnosticEvent> getEvents() {
651 return mEvents.values();
652 }
653 }
654
655 @Override
656 public void dump(PrintWriter writer) {
657 writer.println("*CarDiagnosticService*");
658 writer.println("**last events for diagnostics**");
659 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) {
660 writer.println("last live frame event: ");
661 writer.println(mLiveFrameDiagnosticRecord.getLastEvent());
662 }
663 writer.println("freeze frame events: ");
664 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println);
665 writer.println("**clients**");
666 try {
667 for (DiagnosticClient client : mClients) {
668 if (client != null) {
669 try {
670 writer.println(
671 "binder:"
672 + client.mListener
673 + " active diagnostics:"
674 + Arrays.toString(client.getDiagnosticArray()));
675 } catch (ConcurrentModificationException e) {
676 writer.println("concurrent modification happened");
677 }
678 } else {
679 writer.println("null client");
680 }
681 }
682 } catch (ConcurrentModificationException e) {
683 writer.println("concurrent modification happened");
684 }
685 writer.println("**diagnostic listeners**");
686 try {
687 for (int diagnostic : mDiagnosticListeners.keySet()) {
688 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic);
689 if (diagnosticListeners != null) {
690 writer.println(
691 " Diagnostic:"
692 + diagnostic
693 + " num client:"
694 + diagnosticListeners.getNumberOfClients()
695 + " rate:"
696 + diagnosticListeners.getRate());
697 }
698 }
699 } catch (ConcurrentModificationException e) {
700 writer.println("concurrent modification happened");
701 }
702 }
703}