This article explains how applications can transition gracefully to Distraction Optimized (DO) user interfaces. It describes how to consume a car's driving state as well as the corresponding user experience restrictions. For more information about Car User Experience (UX) Restrictions rules, see Car User Experience Restrictions, which details the three driving states of Parked, Idling, and Moving.
Audience
This content is provided for those who want to design applications that adapt to changes in a car's driving state and the correspondingly imposed UX restrictions.
Technical Details
CarDrivingStateManager
A car's driving state (Parked, Idling, or Moving) is derived from the sensor values provided by the Vehicle Hardware Abstraction Layer (VHAL). Basic sensor information, such as vehicle speed and current gear selection, is used to derive the vehicle's current driving state.
CarDrivingStateEvent
.
which provides @SystemApis, meaning that only Platform Internals, Bundled APKs (such as SysUI or
Settings), and Privileged APKs (such as) GMSCore can access the APIs. The APIs are protected by
permissions specific to driving state android.car.permission.CAR_DRIVING_STATE
. Clients
that require access to driving state information must request this permission.
CarUxRestrictionsManager
Those applications that display a user interface dependent upon driving state must listen to
CarUxRestrictionsManager
, which abstracts the mapping from driving state to UX restrictions so that applications need not
adjust for different market safety requirements.
Note: These activities must be marked as DistractionOptimized, as described in Driver Distraction Guidelines. If the activities are not marked accordingly, they will be blocked.
Instead, applications monitor restrictions exposed by the CarUxRestrictionsManager and not an absolute driving state exposed by the CarDrivingStateManager for anything related to the user interface or the user experience.
Code Sample
The following sample code illustrates how an application monitors UX restrictions:
- Import the car library packages:
import android.car.Car; /* For CarUxRestrictions */ import android.car.drivingstate.CarUxRestrictions; import android.car.drivingstate.CarUxRestrictionsManager;
- Implement the
CarUxRestrictionManager.OnUxRestrictionsChangedListener
(mUxRChangeListener
). This listener, once registered with the CarUxRestrictionsManager, will be called when a change in the UX restrictions occurs. Handle the restriction changes to be Distraction Optimized, as needed:@Nullable private CarUxRestrictionsManager mCarUxRestrictionsManager; private CarUxRestrictions mCurrentUxRestrictions; /* Implement the onUxRestrictionsChangedListener interface */ private CarUxRestrictionsManager.OnUxRestrictionsChangedListener mUxrChangeListener = new CarUxRestrictionsManager.OnUxRestrictionsChangedListener() { @Override public void onUxRestrictionsChanged(CarUxRestrictions carUxRestrictions) { mCurrentUxRestrictions = carUxRestrictions; /* Handle the new restrictions */ handleUxRestrictionsChanged(carUxRestrictions); } };
- Call the car APIs to create a car instance named mCar and connect to the car service:
mCar = Car.createCar(context); if (mCar == null) { // handle car connection error }
- Call
mCar.getCarManager() - mCarUxRestrictionsManager
to get theCarUxRestrictionsManager
:CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
- To register
mUxRChangeListener
implemented in Step 2 above with theCarUxRestrictionsManager
callmCarUxRestrictionsManager.registerListener()
:mCarUxRestrictionsManager.registerListener(mUxrChangeListener); mUxrChangeListener.onUxRestrictionsChanged( mCarUxRestrictionsManager.getCurrentCarUxRestrictions());
The completed block of sample code (created in Step 3 through Step 5) results in the listener receiving restriction changes when the drive state changes:
mCar = Car.createCar(context); if (mCar == null) { // handle car connection error } CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); mCarUxRestrictionsManager.registerListener(mUxrChangeListener); mUxrChangeListener.onUxRestrictionsChanged( mCarUxRestrictionsManager.getCurrentCarUxRestrictions());
CarUxRestrictions
The CarUxRestrictions object provides two types of information:
- Is there a current requirement to be Distraction Optimized?
- If so, what restrictions are currently in place?
Once CarUxRestrictions is obtained from either getCurrentUxRestrictions()
or the listener callback, applications can now use
the isRequiresDistractionOptimization()
API to determine if Distraction Optimized
is required. If this returns false, there is no requirement to be Distraction Optimized and an
application can safely run any activity.
If optimization is required, then use the getActiveRestrictions() API to obtain the set of restrictions in place. This
API returns an int, which is a bit mask of all the restrictions currently in effect. The
set of restrictions currently notified is listed under CarUxRestrictions
.
Note: Minor changes to the set of restrictions are anticipated to occur in the near future.
For example, if an application wants to determine if a restriction to play video exists, upon getting the CarUxRestrictions object, the application must check for the restriction:
int activeUxR = mCurrentCarUxRestrictions.getActiveRestrictions(); if ((activeUxR & CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO) != 0) { handleStopPlayingVideo(); }
DrivingState
CarDrivingStateManager presents the actual driving state of the vehicle (Parked, Idling, or Moving). CarDrivingStateManager APIs can be called similar to the CarUxRestrictionsManager. Applications can register a listener and/or get current driving state. The driving state is returned as CarDrivingStateEvent.
CarDrivingStateEvent
.
changes, the onDrivingStateChanged() method will be called with the new
CarDrivingStateEvent
.
import android.car.Car; /* For CarDrivingState */ import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarDrivingStateManager; mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( Car.CAR_DRIVING_STATE_SERVICE); /* Register the listener (implemented below) */ mDrivingStateManager.registerListener(mDrivingStateEventListener); /* While we wait for a change to be notified, query the current state */ mDrivingStateEvent = mDrivingStateManager.getCurrentCarDrivingState(); private final CarDrivingStateManager.CarDrivingStateEventListener mDrivingStateEventListener = new CarDrivingStateManager.CarDrivingStateEventListener() { @Override public void onDrivingStateChanged(CarDrivingStateEvent event) { mDrivingStateEvent = event; /* handle the state change accordingly */ handleDrivingStateChange(); } };
Testing
You can mimic the changing of gears and speed to change the driving state. Use an ADB shell command to inject vehicle events. This can be useful development and testing.
To simulate driving events:
- To set speed to 0:
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11600207 0
- To set gear to Parked (to simulate CarDrivingStateEvent pointing to PARKED):
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11400400 4
- To set gear to Drive, with speed still at 0 (to simulate CarDrivingStateEvent pointing to IDLING):
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11400400 8
- To set speed to 30 meters per second (to simulate CarDrivingStateEvent pointing to MOVING):
adb shell dumpsys activity service com.android.car inject-vhal-event 0x11600207 30