• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.car.Car;
24 import android.car.CarManagerBase;
25 import android.content.Context;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.util.Log;
32 import android.view.Display;
33 
34 import com.android.internal.annotations.GuardedBy;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.lang.ref.WeakReference;
39 import java.util.Arrays;
40 import java.util.List;
41 
42 /**
43  * API to register and get the User Experience restrictions imposed based on the car's driving
44  * state.
45  */
46 public final class CarUxRestrictionsManager implements CarManagerBase {
47     private static final String TAG = "CarUxRManager";
48     private static final boolean DBG = false;
49     private static final boolean VDBG = false;
50     private static final int MSG_HANDLE_UX_RESTRICTIONS_CHANGE = 0;
51 
52     /**
53      * Baseline restriction mode is the default UX restrictions used for driving state.
54      *
55      * @hide
56      */
57     public static final int UX_RESTRICTION_MODE_BASELINE = 0;
58     /**
59      * Passenger restriction mode uses UX restrictions for {@link #UX_RESTRICTION_MODE_PASSENGER},
60      * set through {@link CarUxRestrictionsConfiguration.Builder.UxRestrictions#setMode(int)}.
61      *
62      * <p>If a new {@link CarUxRestrictions} is available upon mode transition, it'll be immediately
63      * dispatched to listeners.
64      *
65      * <p>If passenger mode restrictions is not configured for current driving state, it will fall
66      * back to {@link #UX_RESTRICTION_MODE_BASELINE}.
67      *
68      * <p>Caller are responsible for determining and executing the criteria for entering and exiting
69      * this mode. Exiting by setting mode to {@link #UX_RESTRICTION_MODE_BASELINE}.
70      *
71      * @hide
72      */
73     public static final int UX_RESTRICTION_MODE_PASSENGER = 1;
74 
75     /** @hide */
76     @IntDef(prefix = { "UX_RESTRICTION_MODE_" }, value = {
77             UX_RESTRICTION_MODE_BASELINE,
78             UX_RESTRICTION_MODE_PASSENGER
79     })
80     @Retention(RetentionPolicy.SOURCE)
81     public @interface UxRestrictionMode {}
82 
83     private final Context mContext;
84     private int mDisplayId = Display.INVALID_DISPLAY;
85     private final ICarUxRestrictionsManager mUxRService;
86     private final EventCallbackHandler mEventCallbackHandler;
87     @GuardedBy("this")
88     private OnUxRestrictionsChangedListener mUxRListener;
89     private CarUxRestrictionsChangeListenerToService mListenerToService;
90 
91     /** @hide */
CarUxRestrictionsManager(IBinder service, Context context, Handler handler)92     public CarUxRestrictionsManager(IBinder service, Context context, Handler handler) {
93         mContext = context;
94         mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service);
95         mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
96     }
97 
98     /** @hide */
99     @Override
onCarDisconnected()100     public void onCarDisconnected() {
101         mListenerToService = null;
102         synchronized (this) {
103             mUxRListener = null;
104         }
105     }
106 
107     /**
108      * Listener Interface for clients to implement to get updated on driving state related
109      * changes.
110      */
111     public interface OnUxRestrictionsChangedListener {
112         /**
113          * Called when the UX restrictions due to a car's driving state changes.
114          *
115          * @param restrictionInfo The new UX restriction information
116          */
onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)117         void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo);
118     }
119 
120     /**
121      * Registers a {@link OnUxRestrictionsChangedListener} for listening to changes in the
122      * UX Restrictions to adhere to.
123      * <p>
124      * If a listener has already been registered, it has to be unregistered before registering
125      * the new one.
126      *
127      * @param listener {@link OnUxRestrictionsChangedListener}
128      */
registerListener(@onNull OnUxRestrictionsChangedListener listener)129     public void registerListener(@NonNull OnUxRestrictionsChangedListener listener) {
130         registerListener(listener, getDisplayId());
131     }
132 
133     /**
134      * @hide
135      */
registerListener(@onNull OnUxRestrictionsChangedListener listener, int displayId)136     public void registerListener(@NonNull OnUxRestrictionsChangedListener listener, int displayId) {
137         synchronized (this) {
138             // Check if the listener has been already registered.
139             if (mUxRListener != null) {
140                 if (DBG) {
141                     Log.d(TAG, "Listener already registered listener");
142                 }
143                 return;
144             }
145             mUxRListener = listener;
146         }
147 
148         try {
149             if (mListenerToService == null) {
150                 mListenerToService = new CarUxRestrictionsChangeListenerToService(this);
151             }
152             // register to the Service to listen for changes.
153             mUxRService.registerUxRestrictionsChangeListener(mListenerToService, displayId);
154         } catch (RemoteException e) {
155             throw e.rethrowFromSystemServer();
156         }
157     }
158 
159     /**
160      * Unregisters the registered {@link OnUxRestrictionsChangedListener}
161      */
unregisterListener()162     public void unregisterListener() {
163         synchronized (this) {
164             if (mUxRListener == null) {
165                 if (DBG) {
166                     Log.d(TAG, "Listener was not previously registered");
167                 }
168                 return;
169             }
170             mUxRListener = null;
171         }
172         try {
173             mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService);
174         } catch (RemoteException e) {
175             throw e.rethrowFromSystemServer();
176         }
177     }
178 
179     /**
180      * Sets new {@link CarUxRestrictionsConfiguration}s for next trip.
181      * <p>
182      * Saving new configurations does not affect current configuration. The new configuration will
183      * only be used after UX Restrictions service restarts when the vehicle is parked.
184      * <p>
185      * Input configurations must be one-to-one mapped to displays, namely each display must have
186      * exactly one configuration.
187      * See {@link CarUxRestrictionsConfiguration.Builder#setDisplayAddress(DisplayAddress)}.
188      *
189      * @param configs Map of display Id to UX restrictions configurations to be persisted.
190      * @return {@code true} if input config was successfully saved; {@code false} otherwise.
191      *
192      * @hide
193      */
194     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
saveUxRestrictionsConfigurationForNextBoot( List<CarUxRestrictionsConfiguration> configs)195     public boolean saveUxRestrictionsConfigurationForNextBoot(
196             List<CarUxRestrictionsConfiguration> configs) {
197         try {
198             return mUxRService.saveUxRestrictionsConfigurationForNextBoot(configs);
199         } catch (RemoteException e) {
200             throw e.rethrowFromSystemServer();
201         }
202     }
203 
204     /**
205      * Gets the current UX restrictions ({@link CarUxRestrictions}) in place.
206      *
207      * @return current UX restrictions that is in effect.
208      */
209     @Nullable
getCurrentCarUxRestrictions()210     public CarUxRestrictions getCurrentCarUxRestrictions() {
211         return getCurrentCarUxRestrictions(getDisplayId());
212     }
213 
214     /**
215      * @hide
216      */
217     @Nullable
getCurrentCarUxRestrictions(int displayId)218     public CarUxRestrictions getCurrentCarUxRestrictions(int displayId) {
219         try {
220             return mUxRService.getCurrentUxRestrictions(displayId);
221         } catch (RemoteException e) {
222             throw e.rethrowFromSystemServer();
223         }
224     }
225 
226     /**
227      * Sets restriction mode. Returns {@code true} if the operation succeeds.
228      *
229      * @hide
230      */
231     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
setRestrictionMode(@xRestrictionMode int mode)232     public boolean setRestrictionMode(@UxRestrictionMode int mode) {
233         try {
234             return mUxRService.setRestrictionMode(mode);
235         } catch (RemoteException e) {
236             throw e.rethrowFromSystemServer();
237         }
238     }
239 
240     /**
241      * Returns the current restriction mode.
242      *
243      * @hide
244      */
245     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
246     @UxRestrictionMode
getRestrictionMode()247     public int getRestrictionMode() {
248         try {
249             return mUxRService.getRestrictionMode();
250         } catch (RemoteException e) {
251             throw e.rethrowFromSystemServer();
252         }
253     }
254 
255     /**
256      * Sets a new {@link CarUxRestrictionsConfiguration} for next trip.
257      * <p>
258      * Saving a new configuration does not affect current configuration. The new configuration will
259      * only be used after UX Restrictions service restarts when the vehicle is parked.
260      *
261      * @param config UX restrictions configuration to be persisted.
262      * @return {@code true} if input config was successfully saved; {@code false} otherwise.
263      *
264      * @hide
265      */
266     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
saveUxRestrictionsConfigurationForNextBoot( CarUxRestrictionsConfiguration config)267     public boolean saveUxRestrictionsConfigurationForNextBoot(
268             CarUxRestrictionsConfiguration config) {
269         return saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(config));
270     }
271 
272     /**
273      * Gets the staged configurations.
274      * <p>
275      * Configurations set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)} do not
276      * immediately affect current drive. Instead, they are staged to take effect when car service
277      * boots up the next time.
278      * <p>
279      * This methods is only for test purpose, please do not use in production.
280      *
281      * @return current staged configuration, {@code null} if it's not available
282      *
283      * @hide
284      */
285     @Nullable
286     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
getStagedConfigs()287     public List<CarUxRestrictionsConfiguration> getStagedConfigs() {
288         try {
289             return mUxRService.getStagedConfigs();
290         } catch (RemoteException e) {
291             throw e.rethrowFromSystemServer();
292         }
293     }
294 
295     /**
296      * Gets the current configurations.
297      *
298      * @return current configurations that is in effect.
299      *
300      * @hide
301      */
302     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
getConfigs()303     public List<CarUxRestrictionsConfiguration> getConfigs() {
304         try {
305             return mUxRService.getConfigs();
306         } catch (RemoteException e) {
307             throw e.rethrowFromSystemServer();
308         }
309     }
310 
311     /**
312      * @hide
313      */
modeToString(@xRestrictionMode int mode)314     public static String modeToString(@UxRestrictionMode int mode) {
315         switch (mode) {
316             case UX_RESTRICTION_MODE_BASELINE:
317                 return "baseline";
318             case UX_RESTRICTION_MODE_PASSENGER:
319                 return "passenger";
320             default:
321                 throw new IllegalArgumentException("Unrecognized restriction mode " + mode);
322         }
323     }
324 
325     /**
326      * Class that implements the listener interface and gets called back from the
327      * {@link com.android.car.CarDrivingStateService} across the binder interface.
328      */
329     private static class CarUxRestrictionsChangeListenerToService extends
330             ICarUxRestrictionsChangeListener.Stub {
331         private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager;
332 
CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager)333         public CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager) {
334             mUxRestrictionsManager = new WeakReference<>(manager);
335         }
336 
337         @Override
onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)338         public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) {
339             CarUxRestrictionsManager manager = mUxRestrictionsManager.get();
340             if (manager != null) {
341                 manager.handleUxRestrictionsChanged(restrictionInfo);
342             }
343         }
344     }
345 
346     /**
347      * Gets the {@link CarUxRestrictions} from the service listener
348      * {@link CarUxRestrictionsChangeListenerToService} and dispatches it to a handler provided
349      * to the manager.
350      *
351      * @param restrictionInfo {@link CarUxRestrictions} that has been registered to listen on
352      */
handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo)353     private void handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo) {
354         // send a message to the handler
355         mEventCallbackHandler.sendMessage(mEventCallbackHandler.obtainMessage(
356                 MSG_HANDLE_UX_RESTRICTIONS_CHANGE, restrictionInfo));
357     }
358 
359     /**
360      * Callback Handler to handle dispatching the UX restriction changes to the corresponding
361      * listeners.
362      */
363     private static final class EventCallbackHandler extends Handler {
364         private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager;
365 
EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper)366         public EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper) {
367             super(looper);
368             mUxRestrictionsManager = new WeakReference<>(manager);
369         }
370 
371         @Override
handleMessage(Message msg)372         public void handleMessage(Message msg) {
373             CarUxRestrictionsManager mgr = mUxRestrictionsManager.get();
374             if (mgr != null) {
375                 mgr.dispatchUxRChangeToClient((CarUxRestrictions) msg.obj);
376             }
377         }
378     }
379 
380     /**
381      * Checks for the listeners to list of {@link CarUxRestrictions} and calls them back
382      * in the callback handler thread.
383      *
384      * @param restrictionInfo {@link CarUxRestrictions}
385      */
dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo)386     private void dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo) {
387         if (restrictionInfo == null) {
388             return;
389         }
390         synchronized (this) {
391             if (mUxRListener != null) {
392                 mUxRListener.onUxRestrictionsChanged(restrictionInfo);
393             }
394         }
395     }
396 
getDisplayId()397     private int getDisplayId() {
398         if (mDisplayId != Display.INVALID_DISPLAY) {
399             return mDisplayId;
400         }
401 
402         mDisplayId = mContext.getDisplayId();
403         Log.i(TAG, "Context returns display ID " + mDisplayId);
404 
405         if (mDisplayId == Display.INVALID_DISPLAY) {
406             mDisplayId = Display.DEFAULT_DISPLAY;
407             Log.e(TAG, "Could not retrieve display id. Using default: " + mDisplayId);
408         }
409 
410         return mDisplayId;
411     }
412 }
413