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