Merge "Fixed null pointer exception on mSupportedAreas." into qt-dev
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
index 4ca54a7..5806aa4 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsConfiguration.java
@@ -826,6 +826,9 @@
 
             public SpeedRange(@FloatRange(from = 0.0) float minSpeed,
                     @FloatRange(from = 0.0) float maxSpeed) {
+                if (Float.compare(minSpeed, 0) < 0 || Float.compare(maxSpeed, 0) < 0) {
+                    throw new IllegalArgumentException("Speed cannot be negative.");
+                }
                 if (minSpeed == MAX_SPEED) {
                     throw new IllegalArgumentException("Min speed cannot be MAX_SPEED.");
                 }
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 4848e99..6ba3f97 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -30,6 +30,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -78,10 +80,10 @@
     private final Context mContext;
     private final ICarUxRestrictionsManager mUxRService;
     private final EventCallbackHandler mEventCallbackHandler;
+    @GuardedBy("this")
     private OnUxRestrictionsChangedListener mUxRListener;
     private CarUxRestrictionsChangeListenerToService mListenerToService;
 
-
     /** @hide */
     public CarUxRestrictionsManager(IBinder service, Context context, Handler handler) {
         mContext = context;
@@ -91,9 +93,11 @@
 
     /** @hide */
     @Override
-    public synchronized void onCarDisconnected() {
+    public void onCarDisconnected() {
         mListenerToService = null;
-        mUxRListener = null;
+        synchronized (this) {
+            mUxRListener = null;
+        }
     }
 
     /**
@@ -118,21 +122,18 @@
      *
      * @param listener {@link OnUxRestrictionsChangedListener}
      */
-    public synchronized void registerListener(@NonNull OnUxRestrictionsChangedListener listener) {
-        if (listener == null) {
-            if (VDBG) {
-                Log.v(TAG, "registerListener(): null listener");
+    public void registerListener(@NonNull OnUxRestrictionsChangedListener listener) {
+        synchronized (this) {
+            // Check if the listener has been already registered.
+            if (mUxRListener != null) {
+                if (DBG) {
+                    Log.d(TAG, "Listener already registered listener");
+                }
+                return;
             }
-            throw new IllegalArgumentException("Listener is null");
+            mUxRListener = listener;
         }
-        // Check if the listener has been already registered.
-        if (mUxRListener != null) {
-            if (DBG) {
-                Log.d(TAG, "Listener already registered listener");
-            }
-            return;
-        }
-        mUxRListener = listener;
+
         try {
             if (mListenerToService == null) {
                 mListenerToService = new CarUxRestrictionsChangeListenerToService(this);
@@ -147,16 +148,18 @@
     /**
      * Unregister the registered {@link OnUxRestrictionsChangedListener}
      */
-    public synchronized void unregisterListener() {
-        if (mUxRListener == null) {
-            if (DBG) {
-                Log.d(TAG, "Listener was not previously registered");
+    public void unregisterListener() {
+        synchronized (this) {
+            if (mUxRListener == null) {
+                if (DBG) {
+                    Log.d(TAG, "Listener was not previously registered");
+                }
+                return;
             }
-            return;
+            mUxRListener = null;
         }
         try {
             mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService);
-            mUxRListener = null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -220,7 +223,7 @@
      * @hide
      */
     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
-    public synchronized boolean saveUxRestrictionsConfigurationForNextBoot(
+    public boolean saveUxRestrictionsConfigurationForNextBoot(
             CarUxRestrictionsConfiguration config) {
         try {
             return mUxRService.saveUxRestrictionsConfigurationForNextBoot(config);
@@ -240,7 +243,7 @@
      */
     @Nullable
     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
-    public synchronized CarUxRestrictionsConfiguration getStagedConfig() {
+    public CarUxRestrictionsConfiguration getStagedConfig() {
         try {
             return mUxRService.getStagedConfig();
         } catch (RemoteException e) {
@@ -256,7 +259,7 @@
      * @hide
      */
     @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
-    public synchronized CarUxRestrictionsConfiguration getConfig() {
+    public CarUxRestrictionsConfiguration getConfig() {
         try {
             return mUxRService.getConfig();
         } catch (RemoteException e) {
@@ -331,7 +334,6 @@
                 mgr.dispatchUxRChangeToClient((CarUxRestrictions) msg.obj);
             }
         }
-
     }
 
     /**
@@ -344,12 +346,10 @@
         if (restrictionInfo == null) {
             return;
         }
-        OnUxRestrictionsChangedListener listener;
         synchronized (this) {
-            listener = mUxRListener;
-        }
-        if (listener != null) {
-            listener.onUxRestrictionsChanged(restrictionInfo);
+            if (mUxRListener != null) {
+                mUxRListener.onUxRestrictionsChanged(restrictionInfo);
+            }
         }
     }
 }
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index eb9bab3..3b40292 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -53,7 +53,7 @@
 PRODUCT_PROPERTY_OVERRIDES += \
     persist.bluetooth.enablenewavrcp=false \
     ro.carrier=unknown \
-    ro.fw.multiuser.headless_system_user=true
+    ro.fw.mu.headless_system_user=true
 
 # Overlay for Google network and fused location providers
 $(call inherit-product, device/sample/products/location_overlay.mk)
diff --git a/service/Android.mk b/service/Android.mk
index a9d0e0b..f16877e 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -53,6 +53,7 @@
     car-frameworks-service \
     car-systemtest \
     com.android.car.procfsinspector-client \
+    blestream-protos \
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     SettingsLib \
@@ -88,6 +89,7 @@
     vehicle-hal-support-lib \
     car-systemtest \
     com.android.car.procfsinspector-client \
+    blestream-protos \
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     SettingsLib \
diff --git a/service/proto/Android.bp b/service/proto/Android.bp
new file mode 100644
index 0000000..8d500ea
--- /dev/null
+++ b/service/proto/Android.bp
@@ -0,0 +1,11 @@
+java_library_static {
+    name: "blestream-protos",
+    host_supported: true,
+    proto: {
+        type: "lite",
+    },
+    srcs: ["*.proto"],
+    no_framework_libs: true,
+    jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "28",
+}
diff --git a/service/proto/BLEHandshake.proto b/service/proto/BLEHandshake.proto
new file mode 100644
index 0000000..26f5783
--- /dev/null
+++ b/service/proto/BLEHandshake.proto
@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+package com.android.car.trust.BLEHandshake;
+
+option java_package = "com.android.car.trust";
+option java_outer_classname = "BLEHandshake";
+
+message VersionExchange {
+  // Required.
+  int32 minSupportedVersion = 1;
+
+  // Required.
+  int32 maxSupportedVersion = 2;
+}
\ No newline at end of file
diff --git a/service/proto/BLEStream.proto b/service/proto/BLEStream.proto
new file mode 100644
index 0000000..c0a1817
--- /dev/null
+++ b/service/proto/BLEStream.proto
@@ -0,0 +1,23 @@
+syntax = "proto3";
+
+package com.android.car.trust.BLEStream;
+
+option java_package = "com.android.car.trust";
+option java_outer_classname = "BLEStream";
+
+message BLEMessage {
+  // Required.
+  int32 version = 1;
+
+  // Required.
+  int32 operation = 2;
+
+  // Required.
+  int32 packet_number = 3;
+
+  // Required.
+  int32 total_packets = 4;
+
+  // Required.
+  bytes payload = 5;
+}
diff --git a/service/proto/jarjar-rules.txt b/service/proto/jarjar-rules.txt
new file mode 100644
index 0000000..c06c995
--- /dev/null
+++ b/service/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.car.trust.protobuf.@1
\ No newline at end of file
diff --git a/service/res/values/attrs.xml b/service/res/values/attrs.xml
index be20c0f..483c7e1 100644
--- a/service/res/values/attrs.xml
+++ b/service/res/values/attrs.xml
@@ -92,6 +92,14 @@
             <flag name="no_voice_transcription" value="256"/>
             <flag name="fully_restricted" value="511"/>
         </attr>
+        <!-- UX restrictions service supports returning different sets of UX restrictions for
+        the same driving state, through configurations for each "mode". -->
+        <attr name="mode">
+            <!-- Default mode. -->
+            <flag name="baseline" value="0"/>
+            <!-- Mode for passenger to interact with system. -->
+            <flag name="passenger" value="1"/>
+        </attr>
     </declare-styleable>
 
     <!-- 2. Some of UX restrictions can be parametrized. -->
diff --git a/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java b/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
index 95bcb2c..eec55c5 100644
--- a/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
+++ b/service/src/com/android/car/CarUxRestrictionsConfigurationXmlParser.java
@@ -16,17 +16,20 @@
 
 package com.android.car;
 
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+
 import android.annotation.Nullable;
 import android.annotation.XmlRes;
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.CarUxRestrictionsConfiguration;
+import android.car.drivingstate.CarUxRestrictionsConfiguration.Builder;
+import android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -160,19 +163,12 @@
                 // 1. Get the driving state attributes: driving state and speed range
                 TypedArray a = mContext.getResources().obtainAttributes(attrs,
                         R.styleable.UxRestrictions_DrivingState);
-                int drivingState = a
-                        .getInt(R.styleable.UxRestrictions_DrivingState_state,
-                                CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
-                float minSpeed = a
-                        .getFloat(
-                                R.styleable
-                                        .UxRestrictions_DrivingState_minSpeed,
-                                INVALID_SPEED);
-                float maxSpeed = a
-                        .getFloat(
-                                R.styleable
-                                        .UxRestrictions_DrivingState_maxSpeed,
-                                INVALID_SPEED);
+                int drivingState = a.getInt(R.styleable.UxRestrictions_DrivingState_state,
+                        CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+                float minSpeed = a.getFloat(R.styleable.UxRestrictions_DrivingState_minSpeed,
+                        INVALID_SPEED);
+                float maxSpeed = a.getFloat(R.styleable.UxRestrictions_DrivingState_maxSpeed,
+                        Builder.SpeedRange.MAX_SPEED);
                 a.recycle();
 
                 // 2. Traverse to the <Restrictions> tag
@@ -182,7 +178,42 @@
                 }
 
                 // 3. Parse the restrictions for this driving state
-                Pair<Boolean, Integer> restrictions = parseRestrictions(parser, attrs);
+                Builder.SpeedRange speedRange = parseSpeedRange(minSpeed, maxSpeed);
+                if (!parseAllRestrictions(parser, attrs, drivingState, speedRange)) {
+                    Log.e(TAG, "Could not parse restrictions for driving state:" + drivingState);
+                    return false;
+                }
+            }
+            parser.next();
+        }
+        return true;
+    }
+
+    /**
+     * Parses all <restrictions> tags nested with <drivingState> tag.
+     */
+    private boolean parseAllRestrictions(XmlResourceParser parser, AttributeSet attrs,
+            int drivingState, Builder.SpeedRange speedRange)
+            throws IOException, XmlPullParserException {
+        if (parser == null || attrs == null) {
+            Log.e(TAG, "Invalid arguments");
+            return false;
+        }
+        // The parser should be at the <Restrictions> tag at this point.
+        if (!RESTRICTIONS.equals(parser.getName())) {
+            Log.e(TAG, "Parser not at Restrictions element: " + parser.getName());
+            return false;
+        }
+        while (RESTRICTIONS.equals(parser.getName())) {
+            if (parser.getEventType() == XmlResourceParser.START_TAG) {
+                // Parse one restrictions tag.
+                DrivingStateRestrictions restrictions = parseRestrictions(parser, attrs);
+                if (restrictions == null) {
+                    Log.e(TAG, "");
+                    return false;
+                }
+                restrictions.setSpeedRange(speedRange);
+
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
                     Log.d(TAG, "Map " + drivingState + " : " + restrictions);
                 }
@@ -190,8 +221,8 @@
                 // Update the builder if the driving state and restrictions info are valid.
                 if (drivingState != CarDrivingStateEvent.DRIVING_STATE_UNKNOWN
                         && restrictions != null) {
-                    addToRestrictions(drivingState, minSpeed, maxSpeed, restrictions.first,
-                            restrictions.second);
+
+                    mConfigBuilder.setUxRestrictions(drivingState, restrictions);
                 }
             }
             parser.next();
@@ -204,15 +235,16 @@
      * for the enclosing driving state.
      */
     @Nullable
-    private Pair<Boolean, Integer> parseRestrictions(XmlResourceParser parser, AttributeSet attrs)
+    private DrivingStateRestrictions parseRestrictions(XmlResourceParser parser, AttributeSet attrs)
             throws IOException, XmlPullParserException {
-        int restrictions = UX_RESTRICTIONS_UNKNOWN;
-        boolean requiresOpt = true;
         if (parser == null || attrs == null) {
             Log.e(TAG, "Invalid Arguments");
             return null;
         }
 
+        int restrictions = UX_RESTRICTIONS_UNKNOWN;
+        int restrictionMode = UX_RESTRICTION_MODE_BASELINE;
+        boolean requiresOpt = true;
         while (RESTRICTIONS.equals(parser.getName())
                 && parser.getEventType() == XmlResourceParser.START_TAG) {
             TypedArray a = mContext.getResources().obtainAttributes(attrs,
@@ -222,23 +254,24 @@
                     CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
             requiresOpt = a.getBoolean(
                     R.styleable.UxRestrictions_Restrictions_requiresDistractionOptimization, true);
+            restrictionMode = a.getInt(
+                    R.styleable.UxRestrictions_Restrictions_mode, UX_RESTRICTION_MODE_BASELINE);
+
             a.recycle();
             parser.next();
         }
-        return new Pair<>(requiresOpt, restrictions);
+        return new DrivingStateRestrictions()
+                .setDistractionOptimizationRequired(requiresOpt)
+                .setRestrictions(restrictions)
+                .setMode(restrictionMode);
     }
 
-    private void addToRestrictions(int drivingState, float minSpeed, float maxSpeed,
-            boolean requiresOpt, int restrictions) {
-        CarUxRestrictionsConfiguration.Builder.SpeedRange speedRange = null;
-        if (Float.compare(minSpeed, INVALID_SPEED) != 0) {
-            if (Float.compare(maxSpeed, INVALID_SPEED) == 0) {
-                // Setting min speed but not max implies MAX_SPEED.
-                maxSpeed = CarUxRestrictionsConfiguration.Builder.SpeedRange.MAX_SPEED;
-            }
-            speedRange = new CarUxRestrictionsConfiguration.Builder.SpeedRange(minSpeed, maxSpeed);
+    @Nullable
+    private Builder.SpeedRange parseSpeedRange(float minSpeed, float maxSpeed) {
+        if (Float.compare(minSpeed, 0) < 0 || Float.compare(maxSpeed, 0) < 0) {
+            return null;
         }
-        mConfigBuilder.setUxRestrictions(drivingState, speedRange, requiresOpt, restrictions);
+        return new CarUxRestrictionsConfiguration.Builder.SpeedRange(minSpeed, maxSpeed);
     }
 
     private boolean traverseToTag(XmlResourceParser parser, String tag)
diff --git a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
index c67f33c..7c298ad 100644
--- a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
@@ -80,8 +80,7 @@
     private BluetoothDevice mRemoteEnrollmentDevice;
     @GuardedBy("this")
     private boolean mEnrollmentHandshakeAccepted;
-
-    private final Map<Long, Boolean> mTokenActiveState = new HashMap<>();
+    private final Map<Long, Boolean> mTokenActiveStateMap = new HashMap<>();
     private String mDeviceName;
     private final Context mContext;
 
@@ -172,6 +171,18 @@
         setEnrollmentHandshakeAccepted(false);
         // Disconnect from BLE
         mCarTrustAgentBleManager.disconnectRemoteDevice(mRemoteEnrollmentDevice);
+        // Remove any handles that have not been activated yet.
+        Iterator<Map.Entry<Long, Boolean>> it = mTokenActiveStateMap.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<Long, Boolean> pair = it.next();
+            boolean isHandleActive = pair.getValue();
+            if (!isHandleActive) {
+                long handle = pair.getKey();
+                int uid = mTrustedDeviceService.getSharedPrefs().getInt(String.valueOf(handle), -1);
+                removeEscrowToken(handle, uid);
+                it.remove();
+            }
+        }
     }
 
     /**
@@ -183,8 +194,8 @@
      */
     @Override
     public boolean isEscrowTokenActive(long handle, int uid) {
-        if (mTokenActiveState.get(handle) != null) {
-            return mTokenActiveState.get(handle);
+        if (mTokenActiveStateMap.get(handle) != null) {
+            return mTokenActiveStateMap.get(handle);
         }
         return false;
     }
@@ -193,7 +204,7 @@
      * Remove the Token associated with the given handle for the given user.
      *
      * @param handle handle corresponding to the escrow token
-     * @param uid user id
+     * @param uid    user id
      */
     @Override
     public void removeEscrowToken(long handle, int uid) {
@@ -207,7 +218,7 @@
      */
     @Override
     public void removeAllTrustedDevices(int uid) {
-        for (TrustedDeviceInfo device: getEnrolledDeviceInfosForUser(uid)) {
+        for (TrustedDeviceInfo device : getEnrolledDeviceInfosForUser(uid)) {
             removeEscrowToken(device.getHandle(), uid);
         }
     }
@@ -230,12 +241,13 @@
      * Enable or disable authentication of the head unit with a trusted device.
      *
      * @param isEnabled when set to {@code false}, head unit will not be
-     * discoverable to unlock the user.  Setting it to {@code true} will enable it back.
+     *                  discoverable to unlock the user.  Setting it to {@code true} will enable it
+     *                  back.
      */
     @Override
     public void setTrustedDeviceUnlockEnabled(boolean isEnabled) {
         mTrustedDeviceService.getCarTrustAgentUnlockService()
-            .setTrustedDeviceUnlockEnabled(isEnabled);
+                .setTrustedDeviceUnlockEnabled(isEnabled);
     }
 
     /**
@@ -295,6 +307,9 @@
             removeEscrowToken(handle, uid);
             return;
         }
+        // To conveniently get the user id to unlock when handle is received.
+        mTrustedDeviceService.getSharedPrefs().edit().putInt(String.valueOf(handle), uid).apply();
+        mTokenActiveStateMap.put(handle, false);
         for (EnrollmentStateClient client : mEnrollmentStateClients) {
             try {
                 client.mListener.onEscrowTokenAdded(handle);
@@ -341,12 +356,9 @@
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "onEscrowTokenActiveStateChanged: " + Long.toHexString(handle));
         }
-        mTokenActiveState.put(handle, isTokenActive);
+        mTokenActiveStateMap.put(handle, isTokenActive);
         dispatchEscrowTokenActiveStateChanged(handle, isTokenActive);
         if (isTokenActive) {
-            SharedPreferences.Editor editor = mTrustedDeviceService.getSharedPrefs().edit();
-            // To conveniently get the user id to unlock when handle is received.
-            editor.putInt(String.valueOf(handle), uid);
             Set<String> deviceInfo = mTrustedDeviceService.getSharedPrefs().getStringSet(
                     String.valueOf(uid), new HashSet<>());
             String deviceName;
@@ -360,13 +372,11 @@
             deviceInfo.add(new TrustedDeviceInfo(handle, mRemoteEnrollmentDevice.getAddress(),
                     deviceName).serialize());
             // To conveniently get the devices info regarding certain user.
-            editor.putStringSet(String.valueOf(uid), deviceInfo);
-            editor.apply();
-
+            mTrustedDeviceService.getSharedPrefs().edit().putStringSet(String.valueOf(uid),
+                    deviceInfo).apply();
             if (Log.isLoggable(TAG, Log.DEBUG)) {
                 Log.d(TAG, "Sending handle: " + handle);
             }
-
             mCarTrustAgentBleManager.sendEnrollmentHandle(mRemoteEnrollmentDevice,
                     mEncryptionKey.encryptData(Utils.longToBytes(handle)));
         } else {
@@ -466,7 +476,7 @@
      * <p>This method should be called continually until {@link #mEnrollmentHandshakeAccepted} is
      * {@code true}, meaning an secure channel has been set up.
      *
-     * @param  message The message received from the connected device.
+     * @param message The message received from the connected device.
      * @throws HandshakeException If an error was encountered during the handshake flow.
      */
     private void processInitEncryptionMessage(byte[] message) throws HandshakeException {
diff --git a/tests/DirectRenderingClusterSample/Android.mk b/tests/DirectRenderingClusterSample/Android.mk
index 41d2a65..28a5a78 100644
--- a/tests/DirectRenderingClusterSample/Android.mk
+++ b/tests/DirectRenderingClusterSample/Android.mk
@@ -40,7 +40,8 @@
     androidx.car_car-cluster \
     car-arch-common \
     car-media-common \
-    car-telephony-common
+    car-telephony-common \
+    car-apps-common
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
index 3071c72..f9c72f7 100644
--- a/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
@@ -23,7 +23,7 @@
     app:cardElevation="0dp"
     app:cardCornerRadius="6dp">
 
-    <com.android.car.media.common.CrossfadeImageView
+    <com.android.car.apps.common.CrossfadeImageView
         android:id="@+id/album_background"
         android:foreground="?android:attr/selectableItemBackground"
         android:layout_width="match_parent"
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
index 5b991a9..155970d 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUxRestrictionsConfigurationTest.java
@@ -247,7 +247,15 @@
         new Builder.SpeedRange(0f, MAX_SPEED);
     }
 
-    public void testSpeedRange_NegativeMax() {
+    public void testSpeedRange_NoNegativeMin() {
+        try {
+            new Builder.SpeedRange(-2f, 1f);
+        } catch (Exception e) {
+            // Expected exception.
+        }
+    }
+
+    public void testSpeedRange_NoNegativeMax() {
         try {
             new Builder.SpeedRange(2f, -1f);
         } catch (Exception e) {
@@ -255,6 +263,14 @@
         }
     }
 
+    public void testSpeedRange_MinCannotBeMaxSpeed() {
+        try {
+            new Builder.SpeedRange(MAX_SPEED, 1f);
+        } catch (Exception e) {
+            // Expected exception.
+        }
+    }
+
     public void testSpeedRange_MinGreaterThanMax() {
         try {
             new Builder.SpeedRange(5f, 2f);
diff --git a/tests/carservice_test/res/xml/ux_restrictions_passenger_mode.xml b/tests/carservice_test/res/xml/ux_restrictions_passenger_mode.xml
new file mode 100644
index 0000000..6a5dd07
--- /dev/null
+++ b/tests/carservice_test/res/xml/ux_restrictions_passenger_mode.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+This xml contains UX restrictions configuration for testing.
+-->
+<UxRestrictions xmlns:car="http://schemas.android.com/apk/res-auto">
+    <RestrictionMapping>
+        <DrivingState car:state="parked">
+            <Restrictions car:mode="passenger"
+                car:requiresDistractionOptimization="false" car:uxr="baseline"/>
+            <Restrictions
+              car:requiresDistractionOptimization="true" car:uxr="no_video"/>
+        </DrivingState>
+        <DrivingState car:state="idling">
+            <Restrictions car:mode="passenger"
+                car:requiresDistractionOptimization="false" car:uxr="baseline"/>
+            <Restrictions
+                car:requiresDistractionOptimization="true" car:uxr="no_video"/>
+        </DrivingState>
+        <!-- Verify parsing multiple DrivingState tags also works.-->
+        <DrivingState car:state="moving">
+            <Restrictions car:mode="passenger"
+                car:requiresDistractionOptimization="false" car:uxr="baseline"/>
+        </DrivingState>
+        <DrivingState car:state="moving">
+            <Restrictions
+                car:requiresDistractionOptimization="true" car:uxr="no_video"/>
+        </DrivingState>
+    </RestrictionMapping>
+</UxRestrictions>
diff --git a/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java b/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
index cad210e..3a0dc4a 100644
--- a/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
+++ b/tests/carservice_test/src/com/android/car/CarUxRestrictionsConfigurationXmlParserTest.java
@@ -18,6 +18,8 @@
 import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING;
 import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
 import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_PASSENGER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -108,4 +110,44 @@
         assertTrue(fast.isRequiresDistractionOptimization());
         assertEquals(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO, fast.getActiveRestrictions());
     }
+
+    @Test
+    public void testParsingPassengerState() throws IOException, XmlPullParserException {
+        CarUxRestrictionsConfiguration config = CarUxRestrictionsConfigurationXmlParser.parse(
+                getContext(), R.xml.ux_restrictions_passenger_mode);
+
+        CarUxRestrictions moving = config.getUxRestrictions(
+                DRIVING_STATE_MOVING, 1f, UX_RESTRICTION_MODE_PASSENGER);
+        assertFalse(moving.isRequiresDistractionOptimization());
+
+        CarUxRestrictions idling = config.getUxRestrictions(
+                DRIVING_STATE_IDLING, 0f, UX_RESTRICTION_MODE_PASSENGER);
+        assertFalse(idling.isRequiresDistractionOptimization());
+
+        CarUxRestrictions parked = config.getUxRestrictions(
+                DRIVING_STATE_PARKED, 0f, UX_RESTRICTION_MODE_PASSENGER);
+        assertFalse(parked.isRequiresDistractionOptimization());
+    }
+
+    @Test
+    public void testParsingPassengerMode_ValuesInBaselineAreNotAffected()
+            throws IOException, XmlPullParserException {
+        CarUxRestrictionsConfiguration config = CarUxRestrictionsConfigurationXmlParser.parse(
+                getContext(), R.xml.ux_restrictions_passenger_mode);
+
+        CarUxRestrictions moving = config.getUxRestrictions(
+                DRIVING_STATE_MOVING, 1f, UX_RESTRICTION_MODE_BASELINE);
+        assertTrue(moving.isRequiresDistractionOptimization());
+        assertEquals(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO, moving.getActiveRestrictions());
+
+        CarUxRestrictions idling = config.getUxRestrictions(
+                DRIVING_STATE_IDLING, 0f, UX_RESTRICTION_MODE_BASELINE);
+        assertTrue(idling.isRequiresDistractionOptimization());
+        assertEquals(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO, idling.getActiveRestrictions());
+
+        CarUxRestrictions parked = config.getUxRestrictions(
+                DRIVING_STATE_PARKED, 0f, UX_RESTRICTION_MODE_BASELINE);
+        assertTrue(parked.isRequiresDistractionOptimization());
+        assertEquals(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO, parked.getActiveRestrictions());
+    }
 }