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