• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.car.diagnostic;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.car.Car;
23 import android.car.CarLibLog;
24 import android.car.CarManagerBase;
25 import android.car.annotation.AddedInOrBefore;
26 import android.car.diagnostic.ICarDiagnosticEventListener.Stub;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import com.android.car.internal.CarPermission;
33 import com.android.car.internal.CarRatedListeners;
34 import com.android.car.internal.SingleMessageHandler;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.lang.ref.WeakReference;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.function.Consumer;
42 
43 /**
44  * API for monitoring car diagnostic data.
45  *
46  * @hide
47  */
48 @SystemApi
49 public final class CarDiagnosticManager extends CarManagerBase {
50     @AddedInOrBefore(majorVersion = 33)
51     public static final int FRAME_TYPE_LIVE = 0;
52     @AddedInOrBefore(majorVersion = 33)
53     public static final int FRAME_TYPE_FREEZE = 1;
54 
55     @Retention(RetentionPolicy.SOURCE)
56     @IntDef({FRAME_TYPE_LIVE, FRAME_TYPE_FREEZE})
57     public @interface FrameType {}
58 
59     /** @hide */
60     @AddedInOrBefore(majorVersion = 33)
61     public static final @FrameType int[] FRAME_TYPES = {
62         FRAME_TYPE_LIVE,
63         FRAME_TYPE_FREEZE
64     };
65 
66     private static final int MSG_DIAGNOSTIC_EVENTS = 0;
67 
68     private final ICarDiagnostic mService;
69     private final SparseArray<CarDiagnosticListeners> mActiveListeners = new SparseArray<>();
70 
71     /** Handles call back into clients. */
72     private final SingleMessageHandler<CarDiagnosticEvent> mHandlerCallback;
73 
74     private final CarDiagnosticEventListenerToService mListenerToService;
75 
76     private final CarPermission mVendorExtensionPermission;
77 
78     /** @hide */
CarDiagnosticManager(Car car, IBinder service)79     public CarDiagnosticManager(Car car, IBinder service) {
80         super(car);
81         mService = ICarDiagnostic.Stub.asInterface(service);
82         mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(
83                 getEventHandler().getLooper(), MSG_DIAGNOSTIC_EVENTS) {
84             @Override
85             protected void handleEvent(CarDiagnosticEvent event) {
86                 CarDiagnosticListeners listeners;
87                 synchronized (mActiveListeners) {
88                     listeners = mActiveListeners.get(event.frameType);
89                 }
90                 if (listeners != null) {
91                     listeners.onDiagnosticEvent(event);
92                 }
93             }
94         };
95         mVendorExtensionPermission = new CarPermission(getContext(),
96                 Car.PERMISSION_VENDOR_EXTENSION);
97         mListenerToService = new CarDiagnosticEventListenerToService(this);
98     }
99 
100     @Override
101     @AddedInOrBefore(majorVersion = 33)
onCarDisconnected()102     public void onCarDisconnected() {
103         synchronized (mActiveListeners) {
104             mActiveListeners.clear();
105         }
106     }
107 
108     /** Listener for diagnostic events. Callbacks are called in the Looper context. */
109     public interface OnDiagnosticEventListener {
110         /**
111          * Called when there is a diagnostic event from the car.
112          *
113          * @param carDiagnosticEvent
114          */
115         @AddedInOrBefore(majorVersion = 33)
onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent)116         void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent);
117     }
118 
119     // OnDiagnosticEventListener registration
120 
assertFrameType(@rameType int frameType)121     private void assertFrameType(@FrameType int frameType) {
122         switch(frameType) {
123             case FRAME_TYPE_FREEZE:
124             case FRAME_TYPE_LIVE:
125                 return;
126             default:
127                 throw new IllegalArgumentException(String.format(
128                             "%d is not a valid diagnostic frame type", frameType));
129         }
130     }
131 
132     /**
133      * Register a new listener for events of a given frame type and rate.
134      * @param listener
135      * @param frameType
136      * @param rate
137      * @return true if the registration was successful; false otherwise
138      * @throws IllegalArgumentException
139      */
140     @AddedInOrBefore(majorVersion = 33)
registerListener( OnDiagnosticEventListener listener, @FrameType int frameType, int rate)141     public boolean registerListener(
142             OnDiagnosticEventListener listener, @FrameType int frameType, int rate) {
143         assertFrameType(frameType);
144         synchronized (mActiveListeners) {
145             boolean needsServerUpdate = false;
146             CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
147             if (listeners == null) {
148                 listeners = new CarDiagnosticListeners(rate);
149                 mActiveListeners.put(frameType, listeners);
150                 needsServerUpdate = true;
151             }
152             if (listeners.addAndUpdateRate(listener, rate)) {
153                 needsServerUpdate = true;
154             }
155             if (needsServerUpdate) {
156                 if (!registerOrUpdateDiagnosticListener(frameType, rate)) {
157                     return false;
158                 }
159             }
160         }
161         return true;
162     }
163 
164     /**
165      * Unregister a listener, causing it to stop receiving all diagnostic events.
166      * @param listener
167      */
168     @AddedInOrBefore(majorVersion = 33)
unregisterListener(OnDiagnosticEventListener listener)169     public void unregisterListener(OnDiagnosticEventListener listener) {
170         synchronized (mActiveListeners) {
171             for (@FrameType int frameType : FRAME_TYPES) {
172                 doUnregisterListenerLocked(listener, frameType);
173             }
174         }
175     }
176 
doUnregisterListenerLocked(OnDiagnosticEventListener listener, @FrameType int frameType)177     private void doUnregisterListenerLocked(OnDiagnosticEventListener listener,
178             @FrameType int frameType) {
179         CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
180         if (listeners != null) {
181             boolean needsServerUpdate = false;
182             if (listeners.contains(listener)) {
183                 needsServerUpdate = listeners.remove(listener);
184             }
185             if (listeners.isEmpty()) {
186                 try {
187                     mService.unregisterDiagnosticListener(frameType,
188                             mListenerToService);
189                 } catch (RemoteException e) {
190                     handleRemoteExceptionFromCarService(e);
191                     // continue for local clean-up
192                 }
193                 mActiveListeners.remove(frameType);
194             } else if (needsServerUpdate) {
195                 registerOrUpdateDiagnosticListener(frameType, listeners.getRate());
196             }
197         }
198     }
199 
registerOrUpdateDiagnosticListener(@rameType int frameType, int rate)200     private boolean registerOrUpdateDiagnosticListener(@FrameType int frameType, int rate) {
201         try {
202             return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService);
203         } catch (RemoteException e) {
204             return handleRemoteExceptionFromCarService(e, false);
205         }
206     }
207 
208     // ICarDiagnostic forwards
209 
210     /**
211      * Retrieve the most-recently acquired live frame data from the car.
212      * @return A CarDiagnostic event for the most recently known live frame if one is present.
213      *         null if no live frame has been recorded by the vehicle.
214      */
215     @AddedInOrBefore(majorVersion = 33)
getLatestLiveFrame()216     public @Nullable CarDiagnosticEvent getLatestLiveFrame() {
217         try {
218             return mService.getLatestLiveFrame();
219         } catch (RemoteException e) {
220             return handleRemoteExceptionFromCarService(e, null);
221         }
222     }
223 
224     /**
225      * Return the list of the timestamps for which a freeze frame is currently stored.
226      * @return An array containing timestamps at which, at the current time, the vehicle has
227      *         a freeze frame stored. If no freeze frames are currently stored, an empty
228      *         array will be returned.
229      * Because vehicles might have a limited amount of storage for frames, clients cannot
230      * assume that a timestamp obtained via this call will be indefinitely valid for retrieval
231      * of the actual diagnostic data, and must be prepared to handle a missing frame.
232      */
233     @AddedInOrBefore(majorVersion = 33)
getFreezeFrameTimestamps()234     public long[] getFreezeFrameTimestamps() {
235         try {
236             return mService.getFreezeFrameTimestamps();
237         } catch (RemoteException e) {
238             return handleRemoteExceptionFromCarService(e, new long[0]);
239         }
240     }
241 
242     /**
243      * Retrieve the freeze frame event data for a given timestamp, if available.
244      * @param timestamp
245      * @return A CarDiagnostic event for the frame at the given timestamp, if one is
246      *         available. null is returned otherwise.
247      * Storage constraints might cause frames to be deleted from vehicle memory.
248      * For this reason it cannot be assumed that a timestamp will yield a valid frame,
249      * even if it was initially obtained via a call to getFreezeFrameTimestamps().
250      */
251     @AddedInOrBefore(majorVersion = 33)
getFreezeFrame(long timestamp)252     public @Nullable CarDiagnosticEvent getFreezeFrame(long timestamp) {
253         try {
254             return mService.getFreezeFrame(timestamp);
255         } catch (RemoteException e) {
256             return handleRemoteExceptionFromCarService(e, null);
257         }
258     }
259 
260     /**
261      * Clear the freeze frame information from vehicle memory at the given timestamps.
262      * @param timestamps A list of timestamps to delete freeze frames at, or an empty array
263      *                   to delete all freeze frames from vehicle memory.
264      * @return true if all the required frames were deleted (including if no timestamps are
265      *         provided and all frames were cleared); false otherwise.
266      * Due to storage constraints, timestamps cannot be assumed to be indefinitely valid, and
267      * a false return from this method should be used by the client as cause for invalidating
268      * its local knowledge of the vehicle diagnostic state.
269      */
270     @AddedInOrBefore(majorVersion = 33)
clearFreezeFrames(long... timestamps)271     public boolean clearFreezeFrames(long... timestamps) {
272         try {
273             return mService.clearFreezeFrames(timestamps);
274         } catch (RemoteException e) {
275             return handleRemoteExceptionFromCarService(e, false);
276         }
277     }
278 
279     /**
280      * Returns true if this vehicle supports sending live frame information.
281      * @return
282      */
283     @AddedInOrBefore(majorVersion = 33)
isLiveFrameSupported()284     public boolean isLiveFrameSupported() {
285         try {
286             return mService.isLiveFrameSupported();
287         } catch (RemoteException e) {
288             return handleRemoteExceptionFromCarService(e, false);
289         }
290     }
291 
292     /**
293      * Returns true if this vehicle supports supports sending notifications to
294      * registered listeners when new freeze frames happen.
295      */
296     @AddedInOrBefore(majorVersion = 33)
isFreezeFrameNotificationSupported()297     public boolean isFreezeFrameNotificationSupported() {
298         try {
299             return mService.isFreezeFrameNotificationSupported();
300         } catch (RemoteException e) {
301             return handleRemoteExceptionFromCarService(e, false);
302         }
303     }
304 
305     /**
306      * Returns whether the underlying HAL supports retrieving freeze frames
307      * stored in vehicle memory using timestamp.
308      */
309     @AddedInOrBefore(majorVersion = 33)
isGetFreezeFrameSupported()310     public boolean isGetFreezeFrameSupported() {
311         try {
312             return mService.isGetFreezeFrameSupported();
313         } catch (RemoteException e) {
314             return handleRemoteExceptionFromCarService(e, false);
315         }
316     }
317 
318     /**
319      * Returns true if this vehicle supports clearing all freeze frames.
320      * This is only meaningful if freeze frame data is also supported.
321      *
322      * A return value of true for this method indicates that it is supported to call
323      * carDiagnosticManager.clearFreezeFrames()
324      * to delete all freeze frames stored in vehicle memory.
325      *
326      * @return
327      */
328     @AddedInOrBefore(majorVersion = 33)
isClearFreezeFramesSupported()329     public boolean isClearFreezeFramesSupported() {
330         try {
331             return mService.isClearFreezeFramesSupported();
332         } catch (RemoteException e) {
333             return handleRemoteExceptionFromCarService(e, false);
334         }
335     }
336 
337     /**
338      * Returns true if this vehicle supports clearing specific freeze frames by timestamp.
339      * This is only meaningful if freeze frame data is also supported.
340      *
341      * A return value of true for this method indicates that it is supported to call
342      * carDiagnosticManager.clearFreezeFrames(timestamp1, timestamp2, ...)
343      * to delete the freeze frames stored for the provided input timestamps, provided any exist.
344      *
345      * @return
346      */
347     @AddedInOrBefore(majorVersion = 33)
isSelectiveClearFreezeFramesSupported()348     public boolean isSelectiveClearFreezeFramesSupported() {
349         try {
350             return mService.isSelectiveClearFreezeFramesSupported();
351         } catch (RemoteException e) {
352             return handleRemoteExceptionFromCarService(e, false);
353         }
354     }
355 
356     private static class CarDiagnosticEventListenerToService
357             extends Stub {
358         private final WeakReference<CarDiagnosticManager> mManager;
359 
CarDiagnosticEventListenerToService(CarDiagnosticManager manager)360         CarDiagnosticEventListenerToService(CarDiagnosticManager manager) {
361             mManager = new WeakReference<>(manager);
362         }
363 
handleOnDiagnosticEvents(CarDiagnosticManager manager, List<CarDiagnosticEvent> events)364         private void handleOnDiagnosticEvents(CarDiagnosticManager manager,
365                 List<CarDiagnosticEvent> events) {
366             manager.mHandlerCallback.sendEvents(events);
367         }
368 
369         @Override
onDiagnosticEvents(List<CarDiagnosticEvent> events)370         public void onDiagnosticEvents(List<CarDiagnosticEvent> events) {
371             CarDiagnosticManager manager = mManager.get();
372             if (manager != null) {
373                 handleOnDiagnosticEvents(manager, events);
374             }
375         }
376     }
377 
378     private class CarDiagnosticListeners extends CarRatedListeners<OnDiagnosticEventListener> {
CarDiagnosticListeners(int rate)379         CarDiagnosticListeners(int rate) {
380             super(rate);
381         }
382 
onDiagnosticEvent(final CarDiagnosticEvent event)383         void onDiagnosticEvent(final CarDiagnosticEvent event) {
384             // throw away old data as oneway binder call can change order.
385             long updateTime = event.timestamp;
386             if (updateTime < mLastUpdateTime) {
387                 Log.w(CarLibLog.TAG_DIAGNOSTIC, "dropping old data");
388                 return;
389             }
390             mLastUpdateTime = updateTime;
391             final boolean hasVendorExtensionPermission = mVendorExtensionPermission.checkGranted();
392             final CarDiagnosticEvent eventToDispatch = hasVendorExtensionPermission
393                     ? event :
394                     event.withVendorSensorsRemoved();
395             List<OnDiagnosticEventListener> listeners;
396             synchronized (mActiveListeners) {
397                 listeners = new ArrayList<>(getListeners());
398             }
399             listeners.forEach(new Consumer<OnDiagnosticEventListener>() {
400 
401                 @Override
402                 public void accept(OnDiagnosticEventListener listener) {
403                     listener.onDiagnosticEvent(eventToDispatch);
404                 }
405             });
406         }
407     }
408 }
409