1 /*
2  * Copyright (C) 2018 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.drivingstate;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.car.Car;
24 import android.car.CarManagerBase;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.os.RemoteException;
30 import android.os.SystemClock;
31 import android.util.Log;
32 
33 import java.lang.ref.WeakReference;
34 
35 /**
36  * API to register and get driving state related information in a car.
37  *
38  * @hide
39  */
40 @SystemApi
41 @TestApi
42 public final class CarDrivingStateManager extends CarManagerBase {
43     private static final String TAG = "CarDrivingStateMgr";
44     private static final boolean DBG = false;
45     private static final boolean VDBG = false;
46     private static final int MSG_HANDLE_DRIVING_STATE_CHANGE = 0;
47 
48     private final ICarDrivingState mDrivingService;
49     private final EventCallbackHandler mEventCallbackHandler;
50     private CarDrivingStateEventListener mDrvStateEventListener;
51     private CarDrivingStateChangeListenerToService mListenerToService;
52 
53 
54     /** @hide */
CarDrivingStateManager(Car car, IBinder service)55     public CarDrivingStateManager(Car car, IBinder service) {
56         super(car);
57         mDrivingService = ICarDrivingState.Stub.asInterface(service);
58         mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
59     }
60 
61     /** @hide */
62     @Override
onCarDisconnected()63     public synchronized void onCarDisconnected() {
64         mListenerToService = null;
65         mDrvStateEventListener = null;
66     }
67 
68     /**
69      * Listener Interface for clients to implement to get updated on driving state changes.
70      *
71      * @hide
72      */
73     @SystemApi
74     public interface CarDrivingStateEventListener {
75         /**
76          * Called when the car's driving state changes.
77          * @param event Car's driving state.
78          */
onDrivingStateChanged(CarDrivingStateEvent event)79         void onDrivingStateChanged(CarDrivingStateEvent event);
80     }
81 
82     /**
83      * Register a {@link CarDrivingStateEventListener} to listen for driving state changes.
84      *
85      * @param listener  {@link CarDrivingStateEventListener}
86      *
87      * @hide
88      */
89     @SystemApi
registerListener(@onNull CarDrivingStateEventListener listener)90     public synchronized void registerListener(@NonNull CarDrivingStateEventListener listener) {
91         if (listener == null) {
92             if (VDBG) {
93                 Log.v(TAG, "registerCarDrivingStateEventListener(): null listener");
94             }
95             throw new IllegalArgumentException("Listener is null");
96         }
97         // Check if the listener has been already registered for this event type
98         if (mDrvStateEventListener != null) {
99             if (DBG) {
100                 Log.d(TAG, "Listener already registered");
101             }
102             return;
103         }
104         mDrvStateEventListener = listener;
105         try {
106             if (mListenerToService == null) {
107                 mListenerToService = new CarDrivingStateChangeListenerToService(this);
108             }
109             // register to the Service for getting notified
110             mDrivingService.registerDrivingStateChangeListener(mListenerToService);
111         } catch (RemoteException e) {
112             handleRemoteExceptionFromCarService(e);
113         }
114     }
115 
116     /**
117      * Unregister the registered {@link CarDrivingStateEventListener} for the given driving event
118      * type.
119      *
120      * @hide
121      */
122     @SystemApi
unregisterListener()123     public synchronized void unregisterListener() {
124         if (mDrvStateEventListener == null) {
125             if (DBG) {
126                 Log.d(TAG, "Listener was not previously registered");
127             }
128             return;
129         }
130         try {
131             mDrivingService.unregisterDrivingStateChangeListener(mListenerToService);
132             mDrvStateEventListener = null;
133             mListenerToService = null;
134         } catch (RemoteException e) {
135             handleRemoteExceptionFromCarService(e);
136         }
137     }
138 
139     /**
140      * Get the current value of the car's driving state.
141      *
142      * @return {@link CarDrivingStateEvent} corresponding to the given eventType
143      *
144      * @hide
145      */
146     @Nullable
147     @SystemApi
getCurrentCarDrivingState()148     public CarDrivingStateEvent getCurrentCarDrivingState() {
149         try {
150             return mDrivingService.getCurrentDrivingState();
151         } catch (RemoteException e) {
152             return handleRemoteExceptionFromCarService(e, null);
153         }
154     }
155 
156     /**
157      * Notify registered driving state change listener about injected event.
158      *
159      * @param drivingState Value in {@link CarDrivingStateEvent.CarDrivingState}.
160      *
161      * Requires Permission:
162      * {@link Car#PERMISSION_CONTROL_APP_BLOCKING}
163      *
164      * @hide
165      */
166     @TestApi
injectDrivingState(int drivingState)167     public void injectDrivingState(int drivingState) {
168         CarDrivingStateEvent event = new CarDrivingStateEvent(
169                 drivingState, SystemClock.elapsedRealtimeNanos());
170         try {
171             mDrivingService.injectDrivingState(event);
172         } catch (RemoteException e) {
173             handleRemoteExceptionFromCarService(e);
174         }
175     }
176 
177     /**
178      * Class that implements the listener interface and gets called back from the
179      * {@link com.android.car.CarDrivingStateService} across the binder interface.
180      */
181     private static class CarDrivingStateChangeListenerToService extends
182             ICarDrivingStateChangeListener.Stub {
183         private final WeakReference<CarDrivingStateManager> mDrvStateMgr;
184 
CarDrivingStateChangeListenerToService(CarDrivingStateManager manager)185         CarDrivingStateChangeListenerToService(CarDrivingStateManager manager) {
186             mDrvStateMgr = new WeakReference<>(manager);
187         }
188 
189         @Override
onDrivingStateChanged(CarDrivingStateEvent event)190         public void onDrivingStateChanged(CarDrivingStateEvent event) {
191             CarDrivingStateManager manager = mDrvStateMgr.get();
192             if (manager != null) {
193                 manager.handleDrivingStateChanged(event);
194             }
195         }
196     }
197 
198     /**
199      * Gets the {@link CarDrivingStateEvent} from the service listener
200      * {@link CarDrivingStateChangeListenerToService} and dispatches it to a handler provided
201      * to the manager
202      *
203      * @param event  {@link CarDrivingStateEvent} that has been registered to listen on
204      */
handleDrivingStateChanged(CarDrivingStateEvent event)205     private void handleDrivingStateChanged(CarDrivingStateEvent event) {
206         // send a message to the handler
207         mEventCallbackHandler.sendMessage(
208                 mEventCallbackHandler.obtainMessage(MSG_HANDLE_DRIVING_STATE_CHANGE, event));
209 
210     }
211 
212     /**
213      * Callback Handler to handle dispatching the driving state changes to the corresponding
214      * listeners
215      */
216     private static final class EventCallbackHandler extends Handler {
217         private final WeakReference<CarDrivingStateManager> mDrvStateMgr;
218 
EventCallbackHandler(CarDrivingStateManager manager, Looper looper)219         EventCallbackHandler(CarDrivingStateManager manager, Looper looper) {
220             super(looper);
221             mDrvStateMgr = new WeakReference<>(manager);
222         }
223 
224         @Override
handleMessage(Message msg)225         public void handleMessage(Message msg) {
226             CarDrivingStateManager mgr = mDrvStateMgr.get();
227             if (mgr != null) {
228                 mgr.dispatchDrivingStateChangeToClient((CarDrivingStateEvent) msg.obj);
229             }
230         }
231 
232     }
233 
234     /**
235      * Checks for the listener to {@link CarDrivingStateEvent} and calls it back
236      * in the callback handler thread
237      *
238      * @param event  {@link CarDrivingStateEvent}
239      */
dispatchDrivingStateChangeToClient(CarDrivingStateEvent event)240     private void dispatchDrivingStateChangeToClient(CarDrivingStateEvent event) {
241         if (event == null) {
242             return;
243         }
244         CarDrivingStateEventListener listener;
245         synchronized (this) {
246             listener = mDrvStateEventListener;
247         }
248         if (listener != null) {
249             listener.onDrivingStateChanged(event);
250         }
251     }
252 
253 }
254