This page explains how apps 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, 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 apps 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 apps 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 apps don't need to
adjust for different market safety requirements.
Note: These activities must be marked as distraction optimized, as described in Driver distraction guidelines. If the activities aren't marked accordingly, they're blocked.
Instead, apps 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 app 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
CarUxRestrictionManager.OnUxRestrictionsChangedListener
(mUxRChangeListener
). This listener, when registered with the CarUxRestrictionsManager, is 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?
When CarUxRestrictions is obtained from either
getCurrentUxRestrictions()
or the listener callback, apps 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 app 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 app wants to determine if a restriction to play video exists, upon getting the CarUxRestrictions object, the app 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. Apps can register a listener or get current driving state. The driving state is returned as CarDrivingStateEvent.
CarDrivingStateEvent
.
changes, the onDrivingStateChanged()
method is 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