blob: f532d656dbf2d3f4fd49106d64bff247e5b054f3 [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 Granata25e89462017-08-02 11:41:31 -070022import android.car.diagnostic.CarDiagnosticEvent;
23import android.car.diagnostic.CarDiagnosticManager;
24import android.car.diagnostic.ICarDiagnostic;
25import android.car.diagnostic.ICarDiagnosticEventListener;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080026import android.content.Context;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080027import android.os.IBinder;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080028import android.os.RemoteException;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080029import android.util.ArrayMap;
Felipe Leme176a5fd2021-01-20 15:48:33 -080030import android.util.IndentingPrintWriter;
Eric Jeongbd5fb562020-12-21 13:49:40 -080031import android.util.Slog;
Kai47e40622019-03-22 12:10:23 -070032
Enrico Granata5c56d2a2017-02-07 15:38:12 -080033import com.android.car.Listeners.ClientWithRate;
34import com.android.car.hal.DiagnosticHalService;
Kai47e40622019-03-22 12:10:23 -070035import com.android.car.hal.DiagnosticHalService.DiagnosticCapabilities;
36import com.android.car.internal.CarPermission;
Enrico Granata5c56d2a2017-02-07 15:38:12 -080037import com.android.internal.annotations.GuardedBy;
Kai47e40622019-03-22 12:10:23 -070038
Enrico Granata5c56d2a2017-02-07 15:38:12 -080039import 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 {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800170 Slog.w(
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800171 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) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800219 Slog.w(
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800220 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 }
Eric Jeongbd5fb562020-12-21 13:49:40 -0800253 Slog.i(
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800254 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.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800262 Slog.w(CarLog.TAG_DIAGNOSTIC, "startDiagnostic failed");
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800263 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) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800281 Slog.i(CarLog.TAG_DIAGNOSTIC, "starting diagnostic " + frameType + " at rate " + rate);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800282 DiagnosticHalService diagnosticHal = getDiagnosticHal();
283 if (diagnosticHal != null) {
284 if (!diagnosticHal.isReady()) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800285 Slog.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800286 return false;
287 }
288 switch (frameType) {
Enrico Granata5e075da2017-04-28 15:15:44 -0700289 case CarDiagnosticManager.FRAME_TYPE_LIVE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800290 if (mLiveFrameDiagnosticRecord.isEnabled()) {
291 return true;
292 }
Kai47e40622019-03-22 12:10:23 -0700293 if (diagnosticHal.requestDiagnosticStart(CarDiagnosticManager.FRAME_TYPE_LIVE,
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800294 rate)) {
295 mLiveFrameDiagnosticRecord.enable();
296 return true;
297 }
298 break;
Enrico Granata5e075da2017-04-28 15:15:44 -0700299 case CarDiagnosticManager.FRAME_TYPE_FREEZE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800300 if (mFreezeFrameDiagnosticRecords.isEnabled()) {
301 return true;
302 }
Kai47e40622019-03-22 12:10:23 -0700303 if (diagnosticHal.requestDiagnosticStart(CarDiagnosticManager.FRAME_TYPE_FREEZE,
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800304 rate)) {
305 mFreezeFrameDiagnosticRecords.enable();
306 return true;
307 }
308 break;
309 }
310 }
311 return false;
312 }
313
314 @Override
315 public void unregisterDiagnosticListener(
316 int frameType, ICarDiagnosticEventListener listener) {
317 boolean shouldStopDiagnostic = false;
318 boolean shouldRestartDiagnostic = false;
319 int newRate = 0;
320 mDiagnosticLock.lock();
321 try {
322 DiagnosticClient diagnosticClient = findDiagnosticClientLocked(listener);
323 if (diagnosticClient == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800324 Slog.i(
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800325 CarLog.TAG_DIAGNOSTIC,
326 String.format(
327 "trying to unregister diagnostic client %s for %s which is not registered",
328 listener, frameType));
329 // never registered or already unregistered.
330 return;
331 }
332 diagnosticClient.removeDiagnostic(frameType);
333 if (diagnosticClient.getNumberOfActiveDiagnostic() == 0) {
334 diagnosticClient.release();
335 mClients.remove(diagnosticClient);
336 }
337 Listeners<DiagnosticClient> diagnosticListeners = mDiagnosticListeners.get(frameType);
338 if (diagnosticListeners == null) {
339 // diagnostic not active
340 return;
341 }
342 ClientWithRate<DiagnosticClient> clientWithRate =
343 diagnosticListeners.findClientWithRate(diagnosticClient);
344 if (clientWithRate == null) {
345 return;
346 }
347 diagnosticListeners.removeClientWithRate(clientWithRate);
348 if (diagnosticListeners.getNumberOfClients() == 0) {
349 shouldStopDiagnostic = true;
350 mDiagnosticListeners.remove(frameType);
351 } else if (diagnosticListeners.updateRate()) { // rate changed
352 newRate = diagnosticListeners.getRate();
353 shouldRestartDiagnostic = true;
354 }
355 } finally {
356 mDiagnosticLock.unlock();
357 }
Eric Jeongbd5fb562020-12-21 13:49:40 -0800358 Slog.i(
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800359 CarLog.TAG_DIAGNOSTIC,
360 String.format(
361 "shouldStopDiagnostic = %s, shouldRestartDiagnostic = %s for type %s",
362 shouldStopDiagnostic, shouldRestartDiagnostic, frameType));
363 if (shouldStopDiagnostic) {
364 stopDiagnostic(frameType);
365 } else if (shouldRestartDiagnostic) {
366 startDiagnostic(frameType, newRate);
367 }
368 }
369
370 private void stopDiagnostic(int frameType) {
371 DiagnosticHalService diagnosticHal = getDiagnosticHal();
372 if (diagnosticHal == null || !diagnosticHal.isReady()) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800373 Slog.w(CarLog.TAG_DIAGNOSTIC, "diagnosticHal not ready");
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800374 return;
375 }
376 switch (frameType) {
Enrico Granata5e075da2017-04-28 15:15:44 -0700377 case CarDiagnosticManager.FRAME_TYPE_LIVE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800378 if (mLiveFrameDiagnosticRecord.disableIfNeeded())
Kai47e40622019-03-22 12:10:23 -0700379 diagnosticHal.requestDiagnosticStop(CarDiagnosticManager.FRAME_TYPE_LIVE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800380 break;
Enrico Granata5e075da2017-04-28 15:15:44 -0700381 case CarDiagnosticManager.FRAME_TYPE_FREEZE:
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800382 if (mFreezeFrameDiagnosticRecords.disableIfNeeded())
Kai47e40622019-03-22 12:10:23 -0700383 diagnosticHal.requestDiagnosticStop(CarDiagnosticManager.FRAME_TYPE_FREEZE);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800384 break;
385 }
386 }
387
388 private DiagnosticHalService getDiagnosticHal() {
389 return mDiagnosticHal;
390 }
391
Enrico Granatac2393682017-05-08 18:09:58 -0700392 // Expose DiagnosticCapabilities
393 public boolean isLiveFrameSupported() {
394 return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported();
395 }
396
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700397 public boolean isFreezeFrameNotificationSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700398 return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported();
399 }
400
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700401 public boolean isGetFreezeFrameSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700402 DiagnosticCapabilities diagnosticCapabilities =
403 getDiagnosticHal().getDiagnosticCapabilities();
404 return diagnosticCapabilities.isFreezeFrameInfoSupported() &&
405 diagnosticCapabilities.isFreezeFrameSupported();
406 }
407
Enrico Granata2e3b49a2017-07-27 14:51:52 -0700408 public boolean isClearFreezeFramesSupported() {
Enrico Granatac2393682017-05-08 18:09:58 -0700409 DiagnosticCapabilities diagnosticCapabilities =
410 getDiagnosticHal().getDiagnosticCapabilities();
411 return diagnosticCapabilities.isFreezeFrameClearSupported() &&
412 diagnosticCapabilities.isFreezeFrameSupported();
413 }
414
Enrico Granata57ec2702017-08-11 11:06:35 -0700415 public boolean isSelectiveClearFreezeFramesSupported() {
416 DiagnosticCapabilities diagnosticCapabilities =
417 getDiagnosticHal().getDiagnosticCapabilities();
418 return isClearFreezeFramesSupported() &&
419 diagnosticCapabilities.isSelectiveClearFreezeFramesSupported();
420 }
421
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800422 // ICarDiagnostic implementations
423
424 @Override
425 public CarDiagnosticEvent getLatestLiveFrame() {
426 mLiveFrameDiagnosticRecord.lock();
427 CarDiagnosticEvent liveFrame = mLiveFrameDiagnosticRecord.getLastEvent();
428 mLiveFrameDiagnosticRecord.unlock();
429 return liveFrame;
430 }
431
432 @Override
433 public long[] getFreezeFrameTimestamps() {
434 mFreezeFrameDiagnosticRecords.lock();
435 long[] timestamps = mFreezeFrameDiagnosticRecords.getFreezeFrameTimestamps();
436 mFreezeFrameDiagnosticRecords.unlock();
437 return timestamps;
438 }
439
440 @Override
441 @Nullable
442 public CarDiagnosticEvent getFreezeFrame(long timestamp) {
443 mFreezeFrameDiagnosticRecords.lock();
444 CarDiagnosticEvent freezeFrame = mFreezeFrameDiagnosticRecords.getEvent(timestamp);
445 mFreezeFrameDiagnosticRecords.unlock();
446 return freezeFrame;
447 }
448
449 @Override
450 public boolean clearFreezeFrames(long... timestamps) {
Enrico Granata3c7a6662017-02-23 18:07:59 -0800451 mDiagnosticClearPermission.assertGranted();
Enrico Granata57ec2702017-08-11 11:06:35 -0700452 if (!isClearFreezeFramesSupported())
453 return false;
454 if (timestamps != null && timestamps.length != 0) {
455 if (!isSelectiveClearFreezeFramesSupported()) {
456 return false;
457 }
Enrico Granata24790342017-02-15 14:46:34 -0800458 }
Enrico Granata57ec2702017-08-11 11:06:35 -0700459 mFreezeFrameDiagnosticRecords.lock();
460 mDiagnosticHal.clearFreezeFrames(timestamps);
461 mFreezeFrameDiagnosticRecords.clearEvents();
462 mFreezeFrameDiagnosticRecords.unlock();
463 return true;
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800464 }
465
466 /**
467 * Find DiagnosticClient from client list and return it. This should be called with mClients
468 * locked.
469 *
470 * @param listener
471 * @return null if not found.
472 */
Andreas Gampe985ca2f2018-02-09 12:57:54 -0800473 @GuardedBy("mDiagnosticLock")
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800474 private CarDiagnosticService.DiagnosticClient findDiagnosticClientLocked(
475 ICarDiagnosticEventListener listener) {
476 IBinder binder = listener.asBinder();
477 for (DiagnosticClient diagnosticClient : mClients) {
478 if (diagnosticClient.isHoldingListenerBinder(binder)) {
479 return diagnosticClient;
480 }
481 }
482 return null;
483 }
484
485 private void removeClient(DiagnosticClient diagnosticClient) {
486 mDiagnosticLock.lock();
487 try {
488 for (int diagnostic : diagnosticClient.getDiagnosticArray()) {
489 unregisterDiagnosticListener(
490 diagnostic, diagnosticClient.getICarDiagnosticEventListener());
491 }
492 mClients.remove(diagnosticClient);
493 } finally {
494 mDiagnosticLock.unlock();
495 }
496 }
497
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800498 /** internal instance for pending client request */
499 private class DiagnosticClient implements Listeners.IListener {
500 /** callback for diagnostic events */
501 private final ICarDiagnosticEventListener mListener;
502
503 private final Set<Integer> mActiveDiagnostics = new HashSet<>();
504
505 /** when false, it is already released */
506 private volatile boolean mActive = true;
507
508 DiagnosticClient(ICarDiagnosticEventListener listener) {
509 this.mListener = listener;
510 }
511
512 @Override
513 public boolean equals(Object o) {
Enrico Granata41c5ac62017-07-12 11:00:39 -0700514 return o instanceof DiagnosticClient
515 && mListener.asBinder()
516 == ((DiagnosticClient) o).mListener.asBinder();
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800517 }
518
519 boolean isHoldingListenerBinder(IBinder listenerBinder) {
520 return mListener.asBinder() == listenerBinder;
521 }
522
523 void addDiagnostic(int frameType) {
524 mActiveDiagnostics.add(frameType);
525 }
526
527 void removeDiagnostic(int frameType) {
528 mActiveDiagnostics.remove(frameType);
529 }
530
531 int getNumberOfActiveDiagnostic() {
532 return mActiveDiagnostics.size();
533 }
534
535 int[] getDiagnosticArray() {
536 return mActiveDiagnostics.stream().mapToInt(Integer::intValue).toArray();
537 }
538
539 ICarDiagnosticEventListener getICarDiagnosticEventListener() {
540 return mListener;
541 }
542
543 /** Client dead. should remove all diagnostic requests from client */
544 @Override
545 public void binderDied() {
546 mListener.asBinder().unlinkToDeath(this, 0);
547 removeClient(this);
548 }
549
550 void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
Enrico Granata41c5ac62017-07-12 11:00:39 -0700551 if (events.size() != 0 && mActive) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800552 try {
553 mListener.onDiagnosticEvents(events);
554 } catch (RemoteException e) {
555 //ignore. crash will be handled by death handler
556 }
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800557 }
558 }
559
560 @Override
561 public void release() {
562 if (mActive) {
563 mListener.asBinder().unlinkToDeath(this, 0);
564 mActiveDiagnostics.clear();
565 mActive = false;
566 }
567 }
568 }
569
570 private static abstract class DiagnosticRecord {
571 private final ReentrantLock mLock;
572 protected boolean mEnabled = false;
573
574 DiagnosticRecord(ReentrantLock lock) {
575 mLock = lock;
576 }
577
578 void lock() {
579 mLock.lock();
580 }
581
582 void unlock() {
583 mLock.unlock();
584 }
585
586 boolean isEnabled() {
587 return mEnabled;
588 }
589
590 void enable() {
591 mEnabled = true;
592 }
593
594 abstract boolean disableIfNeeded();
595 abstract CarDiagnosticEvent update(CarDiagnosticEvent newEvent);
596 }
597
598 private static class LiveFrameRecord extends DiagnosticRecord {
599 /** Store the most recent live-frame. */
600 CarDiagnosticEvent mLastEvent = null;
601
602 LiveFrameRecord(ReentrantLock lock) {
603 super(lock);
604 }
605
606 @Override
607 boolean disableIfNeeded() {
608 if (!mEnabled) return false;
609 mEnabled = false;
610 mLastEvent = null;
611 return true;
612 }
613
614 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800615 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Andreas Gampee72397b2018-01-26 09:22:50 -0800616 Objects.requireNonNull(newEvent);
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800617 if((null == mLastEvent) || mLastEvent.isEarlierThan(newEvent))
618 mLastEvent = newEvent;
619 return mLastEvent;
620 }
621
622 CarDiagnosticEvent getLastEvent() {
623 return mLastEvent;
624 }
625 }
626
627 private static class FreezeFrameRecord extends DiagnosticRecord {
628 /** Store the timestamp --> freeze frame mapping. */
629 HashMap<Long, CarDiagnosticEvent> mEvents = new HashMap<>();
630
631 FreezeFrameRecord(ReentrantLock lock) {
632 super(lock);
633 }
634
635 @Override
636 boolean disableIfNeeded() {
637 if (!mEnabled) return false;
638 mEnabled = false;
639 clearEvents();
640 return true;
641 }
642
643 void clearEvents() {
644 mEvents.clear();
645 }
646
647 @Override
Enrico Granata24790342017-02-15 14:46:34 -0800648 CarDiagnosticEvent update(@NonNull CarDiagnosticEvent newEvent) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800649 mEvents.put(newEvent.timestamp, newEvent);
650 return newEvent;
651 }
652
653 long[] getFreezeFrameTimestamps() {
654 return mEvents.keySet().stream().mapToLong(Long::longValue).toArray();
655 }
656
657 CarDiagnosticEvent getEvent(long timestamp) {
658 return mEvents.get(timestamp);
659 }
660
661 Iterable<CarDiagnosticEvent> getEvents() {
662 return mEvents.values();
663 }
664 }
665
666 @Override
Felipe Leme176a5fd2021-01-20 15:48:33 -0800667 public void dump(IndentingPrintWriter writer) {
Enrico Granata5c56d2a2017-02-07 15:38:12 -0800668 writer.println("*CarDiagnosticService*");
669 writer.println("**last events for diagnostics**");
670 if (null != mLiveFrameDiagnosticRecord.getLastEvent()) {
671 writer.println("last live frame event: ");
672 writer.println(mLiveFrameDiagnosticRecord.getLastEvent());
673 }
674 writer.println("freeze frame events: ");
675 mFreezeFrameDiagnosticRecords.getEvents().forEach(writer::println);
676 writer.println("**clients**");
677 try {
678 for (DiagnosticClient client : mClients) {
679 if (client != null) {
680 try {
681 writer.println(
682 "binder:"
683 + client.mListener
684 + " active diagnostics:"
685 + Arrays.toString(client.getDiagnosticArray()));
686 } catch (ConcurrentModificationException e) {
687 writer.println("concurrent modification happened");
688 }
689 } else {
690 writer.println("null client");
691 }
692 }
693 } catch (ConcurrentModificationException e) {
694 writer.println("concurrent modification happened");
695 }
696 writer.println("**diagnostic listeners**");
697 try {
698 for (int diagnostic : mDiagnosticListeners.keySet()) {
699 Listeners diagnosticListeners = mDiagnosticListeners.get(diagnostic);
700 if (diagnosticListeners != null) {
701 writer.println(
702 " Diagnostic:"
703 + diagnostic
704 + " num client:"
705 + diagnosticListeners.getNumberOfClients()
706 + " rate:"
707 + diagnosticListeners.getRate());
708 }
709 }
710 } catch (ConcurrentModificationException e) {
711 writer.println("concurrent modification happened");
712 }
713 }
714}