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