Consume car driving state and UX restrictions

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:

  1. Import the car library packages:
    import android.car.Car;
    /* For CarUxRestrictions */
    import android.car.drivingstate.CarUxRestrictions;
    import android.car.drivingstate.CarUxRestrictionsManager;
  2. 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);
            }
        };
      
  3. 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
    }
  4. Call mCar.getCarManager() - mCarUxRestrictionsManager to get the CarUxRestrictionsManager:
    CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
    mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
  5. To register mUxRChangeListener implemented in Step 2 above with the CarUxRestrictionsManager call mCarUxRestrictionsManager.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:

  1. Is there a current requirement to be distraction optimized?
  2. 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:

  1. To set speed to 0:
    adb shell dumpsys activity service com.android.car inject-vhal-event 0x11600207 0
  2. To set gear to Parked (to simulate CarDrivingStateEvent pointing to PARKED):
    adb shell dumpsys activity service com.android.car inject-vhal-event 0x11400400 4
  3. 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
  4. 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