• 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.hardware;
18 
19 import android.car.Car;
20 import android.car.CarApiUtil;
21 import android.car.CarLibLog;
22 import android.car.CarManagerBase;
23 import android.car.CarNotConnectedException;
24 import android.content.Context;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 import android.util.Log;
29 import android.util.SparseArray;
30 
31 import com.android.car.internal.CarPermission;
32 import com.android.car.internal.CarRatedListeners;
33 import com.android.car.internal.SingleMessageHandler;
34 
35 import java.lang.ref.WeakReference;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.function.Consumer;
39 
40 /** API for monitoring car diagnostic data. */
41 /** @hide */
42 public final class CarDiagnosticManager implements CarManagerBase {
43     public static final int FRAME_TYPE_FLAG_LIVE = 0;
44     public static final int FRAME_TYPE_FLAG_FREEZE = 1;
45 
46     private static final int MSG_DIAGNOSTIC_EVENTS = 0;
47 
48     private final ICarDiagnostic mService;
49     private final SparseArray<CarDiagnosticListeners> mActiveListeners = new SparseArray<>();
50 
51     /** Handles call back into clients. */
52     private final SingleMessageHandler<CarDiagnosticEvent> mHandlerCallback;
53 
54     private CarDiagnosticEventListenerToService mListenerToService;
55 
56     private final CarPermission mVendorExtensionPermission;
57 
CarDiagnosticManager(IBinder service, Context context, Handler handler)58     public CarDiagnosticManager(IBinder service, Context context, Handler handler) {
59         mService = ICarDiagnostic.Stub.asInterface(service);
60         mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(handler.getLooper(),
61             MSG_DIAGNOSTIC_EVENTS) {
62             @Override
63             protected void handleEvent(CarDiagnosticEvent event) {
64                 CarDiagnosticListeners listeners;
65                 synchronized (mActiveListeners) {
66                     listeners = mActiveListeners.get(event.frameType);
67                 }
68                 if (listeners != null) {
69                     listeners.onDiagnosticEvent(event);
70                 }
71             }
72         };
73         mVendorExtensionPermission = new CarPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
74     }
75 
76     @Override
onCarDisconnected()77     public void onCarDisconnected() {
78         synchronized(mActiveListeners) {
79             mActiveListeners.clear();
80             mListenerToService = null;
81         }
82     }
83 
84     /** Listener for diagnostic events. Callbacks are called in the Looper context. */
85     public interface OnDiagnosticEventListener {
86         /**
87          * Called when there is a diagnostic event from the car.
88          *
89          * @param carDiagnosticEvent
90          */
onDiagnosticEvent(final CarDiagnosticEvent carDiagnosticEvent)91         void onDiagnosticEvent(final CarDiagnosticEvent carDiagnosticEvent);
92     }
93 
94     // OnDiagnosticEventListener registration
95 
assertFrameType(int frameType)96     private void assertFrameType(int frameType) {
97         switch(frameType) {
98             case FRAME_TYPE_FLAG_FREEZE:
99             case FRAME_TYPE_FLAG_LIVE:
100                 return;
101             default:
102                 throw new IllegalArgumentException(String.format(
103                             "%d is not a valid diagnostic frame type", frameType));
104         }
105     }
106 
107     /**
108      * Register a new listener for events of a given frame type and rate.
109      * @param listener
110      * @param frameType
111      * @param rate
112      * @return true if the registration was successful; false otherwise
113      * @throws CarNotConnectedException
114      * @throws IllegalArgumentException
115      */
registerListener(OnDiagnosticEventListener listener, int frameType, int rate)116     public boolean registerListener(OnDiagnosticEventListener listener, int frameType, int rate)
117                 throws CarNotConnectedException, IllegalArgumentException {
118         assertFrameType(frameType);
119         synchronized(mActiveListeners) {
120             if (null == mListenerToService) {
121                 mListenerToService = new CarDiagnosticEventListenerToService(this);
122             }
123             boolean needsServerUpdate = false;
124             CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
125             if (listeners == null) {
126                 listeners = new CarDiagnosticListeners(rate);
127                 mActiveListeners.put(frameType, listeners);
128                 needsServerUpdate = true;
129             }
130             if (listeners.addAndUpdateRate(listener, rate)) {
131                 needsServerUpdate = true;
132             }
133             if (needsServerUpdate) {
134                 if (!registerOrUpdateDiagnosticListener(frameType, rate)) {
135                     return false;
136                 }
137             }
138         }
139         return true;
140     }
141 
142     /**
143      * Unregister a listener, causing it to stop receiving all diagnostic events.
144      * @param listener
145      */
unregisterListener(OnDiagnosticEventListener listener)146     public void unregisterListener(OnDiagnosticEventListener listener) {
147         synchronized(mActiveListeners) {
148             for(int i = 0; i < mActiveListeners.size(); i++) {
149                 doUnregisterListenerLocked(listener, mActiveListeners.keyAt(i));
150             }
151         }
152     }
153 
doUnregisterListenerLocked(OnDiagnosticEventListener listener, int sensor)154     private void doUnregisterListenerLocked(OnDiagnosticEventListener listener, int sensor) {
155         CarDiagnosticListeners listeners = mActiveListeners.get(sensor);
156         if (listeners != null) {
157             boolean needsServerUpdate = false;
158             if (listeners.contains(listener)) {
159                 needsServerUpdate = listeners.remove(listener);
160             }
161             if (listeners.isEmpty()) {
162                 try {
163                     mService.unregisterDiagnosticListener(sensor,
164                         mListenerToService);
165                 } catch (RemoteException e) {
166                     //ignore
167                 }
168                 mActiveListeners.remove(sensor);
169             } else if (needsServerUpdate) {
170                 try {
171                     registerOrUpdateDiagnosticListener(sensor, listeners.getRate());
172                 } catch (CarNotConnectedException e) {
173                     // ignore
174                 }
175             }
176         }
177     }
178 
registerOrUpdateDiagnosticListener(int frameType, int rate)179     private boolean registerOrUpdateDiagnosticListener(int frameType, int rate)
180         throws CarNotConnectedException {
181         try {
182             return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService);
183         } catch (IllegalStateException e) {
184             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
185         } catch (RemoteException e) {
186             throw new CarNotConnectedException();
187         }
188         return false;
189     }
190 
191     // ICarDiagnostic forwards
192 
193     /**
194      * Retrieve the most-recently acquired live frame data from the car.
195      * @return
196      * @throws CarNotConnectedException
197      */
getLatestLiveFrame()198     public CarDiagnosticEvent getLatestLiveFrame() throws CarNotConnectedException {
199         try {
200             return mService.getLatestLiveFrame();
201         } catch (IllegalStateException e) {
202             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
203         } catch (RemoteException e) {
204             throw new CarNotConnectedException();
205         }
206         return null;
207     }
208 
209     /**
210      * Return the list of the timestamps for which a freeze frame is currently stored.
211      * @return
212      * @throws CarNotConnectedException
213      */
getFreezeFrameTimestamps()214     public long[] getFreezeFrameTimestamps() throws CarNotConnectedException {
215         try {
216             return mService.getFreezeFrameTimestamps();
217         } catch (IllegalStateException e) {
218             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
219         } catch (RemoteException e) {
220             throw new CarNotConnectedException();
221         }
222         return new long[]{};
223     }
224 
225     /**
226      * Retrieve the freeze frame event data for a given timestamp, if available.
227      * @param timestamp
228      * @return
229      * @throws CarNotConnectedException
230      */
getFreezeFrame(long timestamp)231     public CarDiagnosticEvent getFreezeFrame(long timestamp) throws CarNotConnectedException {
232         try {
233             return mService.getFreezeFrame(timestamp);
234         } catch (IllegalStateException e) {
235             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
236         } catch (RemoteException e) {
237             throw new CarNotConnectedException();
238         }
239         return null;
240     }
241 
242     /**
243      * Clear the freeze frame information from vehicle memory at the given timestamps.
244      * @param timestamps
245      * @return
246      * @throws CarNotConnectedException
247      */
clearFreezeFrames(long... timestamps)248     public boolean clearFreezeFrames(long... timestamps) throws CarNotConnectedException {
249         try {
250             return mService.clearFreezeFrames(timestamps);
251         } catch (IllegalStateException e) {
252             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
253         } catch (RemoteException e) {
254             throw new CarNotConnectedException();
255         }
256         return false;
257     }
258 
259     private static class CarDiagnosticEventListenerToService
260             extends ICarDiagnosticEventListener.Stub {
261         private final WeakReference<CarDiagnosticManager> mManager;
262 
CarDiagnosticEventListenerToService(CarDiagnosticManager manager)263         public CarDiagnosticEventListenerToService(CarDiagnosticManager manager) {
264             mManager = new WeakReference<>(manager);
265         }
266 
handleOnDiagnosticEvents(CarDiagnosticManager manager, List<CarDiagnosticEvent> events)267         private void handleOnDiagnosticEvents(CarDiagnosticManager manager,
268             List<CarDiagnosticEvent> events) {
269             manager.mHandlerCallback.sendEvents(events);
270         }
271 
272         @Override
onDiagnosticEvents(List<CarDiagnosticEvent> events)273         public void onDiagnosticEvents(List<CarDiagnosticEvent> events) {
274             CarDiagnosticManager manager = mManager.get();
275             if (manager != null) {
276                 handleOnDiagnosticEvents(manager, events);
277             }
278         }
279     }
280 
281     private class CarDiagnosticListeners extends CarRatedListeners<OnDiagnosticEventListener> {
CarDiagnosticListeners(int rate)282         CarDiagnosticListeners(int rate) {
283             super(rate);
284         }
285 
onDiagnosticEvent(final CarDiagnosticEvent event)286         void onDiagnosticEvent(final CarDiagnosticEvent event) {
287             // throw away old sensor data as oneway binder call can change order.
288             long updateTime = event.timestamp;
289             if (updateTime < mLastUpdateTime) {
290                 Log.w(CarLibLog.TAG_DIAGNOSTIC, "dropping old sensor data");
291                 return;
292             }
293             mLastUpdateTime = updateTime;
294             final boolean hasVendorExtensionPermission = mVendorExtensionPermission.checkGranted();
295             final CarDiagnosticEvent eventToDispatch = hasVendorExtensionPermission ?
296                     event :
297                     event.withVendorSensorsRemoved();
298             List<OnDiagnosticEventListener> listeners;
299             synchronized (mActiveListeners) {
300                 listeners = new ArrayList<>(getListeners());
301             }
302             listeners.forEach(new Consumer<OnDiagnosticEventListener>() {
303 
304                 @Override
305                 public void accept(OnDiagnosticEventListener listener) {
306                     listener.onDiagnosticEvent(eventToDispatch);
307                 }
308             });
309         }
310     }
311 }
312