Merge "New Manager for Trusted device enrollment."
diff --git a/EncryptionRunner/Android.bp b/EncryptionRunner/Android.bp
new file mode 100644
index 0000000..b02e6de
--- /dev/null
+++ b/EncryptionRunner/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_library {
+    name: "EncryptionRunner",
+    min_sdk_version: "23",
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+android_test {
+    name: "EncryptionRunnerTest",
+    min_sdk_version: "23",
+    srcs: [
+        "test/**/*.java",
+    ],
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "EncryptionRunner",
+        "junit",
+        "truth-prebuilt",
+    ],
+}
diff --git a/EncryptionRunner/AndroidManifest.xml b/EncryptionRunner/AndroidManifest.xml
new file mode 100644
index 0000000..8d7643e
--- /dev/null
+++ b/EncryptionRunner/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="android.car.encryptionrunner" >
+    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.car.encryptionrunner"
+        android:label="Encryption Runner Tests" />
+</manifest>
diff --git a/EncryptionRunner/AndroidTest.xml b/EncryptionRunner/AndroidTest.xml
new file mode 100644
index 0000000..b6cbbfa
--- /dev/null
+++ b/EncryptionRunner/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Tests for EncryptionRunner.">
+    <option name="test-tag" value="EncryptionRunnerTest" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="EncryptionRunnerTest.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.car.encryptionrunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java b/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java
new file mode 100644
index 0000000..b08c985
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An ecnryption runnner that doesn't actually do encryption. Useful for debugging. Do not use in
+ * production environments.
+ */
+class DummyEncryptionRunner implements EncryptionRunner {
+
+    private static final String KEY = "key";
+    private static final String INIT = "init";
+    private static final String INIT_RESPONSE = "initResponse";
+    private static final String CLIENT_RESPONSE = "clientResponse";
+    public static final String PIN = "1234";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Mode.UNKNOWN, Mode.CLIENT, Mode.SERVER})
+    private @interface Mode {
+
+        int UNKNOWN = 0;
+        int CLIENT = 1;
+        int SERVER = 2;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({State.UNKNOWN, State.WAITING_FOR_RESPONSE, State.FINISHED})
+    private @interface State {
+
+        int UNKNOWN = 0;
+        int WAITING_FOR_RESPONSE = 1;
+        int FINISHED = 2;
+    }
+
+    @Mode
+    private int mMode;
+    @State
+    private int mState;
+
+    @Override
+    public HandshakeMessage initHandshake() {
+        mMode = Mode.CLIENT;
+        mState = State.WAITING_FOR_RESPONSE;
+        return HandshakeMessage.newBuilder()
+                .setNextMessage(INIT.getBytes())
+                .build();
+    }
+
+    @Override
+    public HandshakeMessage respondToInitRequest(byte[] initializationRequest)
+            throws HandshakeException {
+        mMode = Mode.SERVER;
+        if (!new String(initializationRequest).equals(INIT)) {
+            throw new HandshakeException("Unexpected initialization request");
+        }
+        mState = State.WAITING_FOR_RESPONSE;
+        return HandshakeMessage.newBuilder()
+                .setNextMessage(INIT_RESPONSE.getBytes())
+                .build();
+    }
+
+    @Override
+    public HandshakeMessage continueHandshake(byte[] response) throws HandshakeException {
+        if (mState != State.WAITING_FOR_RESPONSE) {
+            throw new HandshakeException("not waiting for response but got one");
+        }
+        switch(mMode) {
+            case Mode.SERVER:
+                if (!CLIENT_RESPONSE.equals(new String(response))) {
+                    throw new HandshakeException("unexpected response: " + new String(response));
+                }
+                mState = State.FINISHED;
+                return HandshakeMessage.newBuilder()
+                        .setHandshakeComplete(true)
+                        .setKey(new DummyKey())
+                        .build();
+            case Mode.CLIENT:
+                if (!INIT_RESPONSE.equals(new String(response))) {
+                    throw new HandshakeException("unexpected response: " + new String(response));
+                }
+                mState = State.FINISHED;
+                return HandshakeMessage.newBuilder()
+                        .setHandshakeComplete(true)
+                        .setKey(new DummyKey())
+                        .setNextMessage(CLIENT_RESPONSE.getBytes())
+                        .build();
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public Key keyOf(byte[] serialized) {
+        return new DummyKey();
+    }
+
+    @Override
+    public String getPin() {
+        return PIN;
+    }
+
+    @Override
+    public byte[] encryptData(Key key, byte[] data) {
+        return data;
+    }
+
+    @Override
+    public byte[] decryptData(Key key, byte[] encryptedData) {
+        return encryptedData;
+    }
+
+    private class DummyKey implements Key {
+
+        @Override
+        public byte[] asBytes() {
+            return KEY.getBytes();
+        }
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java
new file mode 100644
index 0000000..7e7bd3e
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.NonNull;
+
+/**
+ * A generalized interface that allows for generating shared secrets as well as encrypting
+ * messages.
+ */
+public interface EncryptionRunner {
+
+    /**
+     * Starts an encryption handshake.
+     *
+     * @return A handshake message with information about the handshake that is started.
+     */
+    HandshakeMessage initHandshake();
+
+    /**
+     * Starts an encryption handshake where the device that is being communicated with already
+     * initiated the request.
+     *
+     * @param initializationRequest the bytes that the other device sent over.
+     * @return a handshake message with information about the handshake.
+     * @throws HandshakeException if initialization request is invalid.
+     */
+    HandshakeMessage respondToInitRequest(@NonNull byte[] initializationRequest)
+            throws HandshakeException;
+
+    /**
+     * Continues a handshake after receiving another response from the connected device.
+     *
+     * @param response the response from the other device.
+     * @return a message that can be used to continue the handshake.
+     * @throws HandshakeException if unexpected bytes in response.
+     */
+    HandshakeMessage continueHandshake(@NonNull byte[] response) throws HandshakeException;
+
+    /**
+     * De seriliazes a previously serilized key generated by an instance of this encryption runner.
+     *
+     * @param serialized the serialized bytes of the key.
+     * @return the Key object used for encryption.
+     */
+    Key keyOf(@NonNull byte[] serialized);
+
+    /**
+     * A user visible shared pin. This pin can be used to verify that both devices that are
+     * communicating have agreed to the same key and will be shown to a user.
+     *
+     * @return the user visible pin.
+     */
+    String getPin();
+
+    /**
+     * Encrypts data using an encryption key.
+     *
+     * @param key  the key used to encrypt the data.
+     * @param data the data to be encrypted
+     * @return the encrypted data.
+     */
+    byte[] encryptData(@NonNull Key key, @NonNull byte[] data);
+
+    /**
+     * Decrypts data using a specified key.
+     *
+     * @param key           The key used to decrypt the data.
+     * @param encryptedData The encrypted data.
+     * @return decrypted data.
+     */
+    byte[] decryptData(@NonNull Key key, @NonNull byte[] encryptedData);
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java
new file mode 100644
index 0000000..d975835
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+/**
+ * Factory that creates encryption runner.
+ */
+public class EncryptionRunnerFactory {
+
+    /**
+     * Creates a new {@link EncryptionRunner} one that doesn't actually do encryption but is useful
+     * for testing.
+     */
+    static EncryptionRunner newDummyRunner() {
+        return new DummyEncryptionRunner();
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java
new file mode 100644
index 0000000..02c873c
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+/**
+ * Exception indicating an error during a Handshake of EncryptionRunner.
+ */
+public class HandshakeException extends Exception {
+
+    public HandshakeException(String message) {
+        super(message);
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java
new file mode 100644
index 0000000..5286770
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.Nullable;
+
+/**
+ * During an {@link EncryptionRunner} handshake process, these are the messages returned as part
+ * of each step.
+ */
+public class HandshakeMessage {
+
+    private final boolean mHandShakeComplete;
+    private final Key mKey;
+    private final byte[] mNextMessage;
+
+    /**
+     * @return Returns a builder for {@link HandshakeMessage}.
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Use the builder;
+     */
+    private HandshakeMessage(
+            boolean handShakeComplete,
+            @Nullable Key key,
+            @Nullable byte[] nextMessage) {
+        mHandShakeComplete = handShakeComplete;
+        mKey = key;
+        mNextMessage = nextMessage;
+    }
+
+    /**
+     * Returns the next message to send in a handshake.
+     */
+    @Nullable
+    public byte[] getNextMessage() {
+        return mNextMessage == null ? null : mNextMessage.clone();
+    }
+
+    /**
+     * Returns true if the handshake is complete.
+     */
+    public boolean isHandShakeComplete() {
+        return mHandShakeComplete;
+    }
+
+    /**
+     * Returns the encryption key that can be used to encrypt data.
+     */
+    @Nullable
+    public Key getKey() {
+        return mKey;
+    }
+
+    static class Builder {
+        boolean mHandshakeComplete;
+        Key mKey;
+        byte[] mNextMessage;
+
+        Builder setHandshakeComplete(boolean handshakeComplete) {
+            mHandshakeComplete = handshakeComplete;
+            return this;
+        }
+
+        Builder setKey(Key key) {
+            mKey = key;
+            return this;
+        }
+
+        Builder setNextMessage(byte[] nextMessage) {
+            mNextMessage = nextMessage == null ? null : nextMessage.clone();
+            return this;
+        }
+
+        HandshakeMessage build() {
+            return new HandshakeMessage(mHandshakeComplete, mKey, mNextMessage);
+        }
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/Key.java b/EncryptionRunner/src/android/car/encryptionrunner/Key.java
new file mode 100644
index 0000000..97dd362
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/Key.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.NonNull;
+
+/**
+ * Represents a serializable encryption key.
+ */
+public interface Key {
+    /**
+     * Returns a serialized encryption key.
+     */
+    @NonNull byte[] asBytes();
+}
diff --git a/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java b/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java
new file mode 100644
index 0000000..d08b37a
--- /dev/null
+++ b/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EncryptionRunnerTest {
+
+    private static final byte[] sTestData = "test data".getBytes();
+
+    @Test
+    public void happyFlow() throws Exception {
+        // This performs a handshake and then sends an "encrypted" message back and forth.
+        // Any encryption runner should be able to do this.
+        // Right now just using the dummy runner, when we have a real runner we can extract this
+        // method or just have the factory create a real runner.
+        EncryptionRunner clientRunner = EncryptionRunnerFactory.newDummyRunner();
+        EncryptionRunner serverRunner = EncryptionRunnerFactory.newDummyRunner();
+        HandshakeMessage initialClientMessage = clientRunner.initHandshake();
+
+        assertThat(initialClientMessage.isHandShakeComplete()).isFalse();
+        assertThat(initialClientMessage.getKey()).isNull();
+        assertThat(initialClientMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage initialServerMessage =
+                serverRunner.respondToInitRequest(initialClientMessage.getNextMessage());
+
+        assertThat(initialServerMessage.isHandShakeComplete()).isFalse();
+        assertThat(initialServerMessage.getKey()).isNull();
+        assertThat(initialServerMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage clientMessage =
+                clientRunner.continueHandshake(initialServerMessage.getNextMessage());
+
+        assertThat(clientMessage.isHandShakeComplete()).isTrue();
+        assertThat(clientMessage.getKey()).isNotNull();
+        assertThat(clientMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage serverMessage =
+                serverRunner.continueHandshake(clientMessage.getNextMessage());
+
+        assertThat(serverMessage.isHandShakeComplete()).isTrue();
+        assertThat(serverMessage.getKey()).isNotNull();
+        assertThat(serverMessage.getNextMessage()).isNull();
+
+        assertThat(serverRunner.decryptData(
+                serverMessage.getKey(),
+                clientRunner.encryptData(clientMessage.getKey(), sTestData))).isEqualTo(sTestData);
+        assertThat(clientRunner.decryptData(
+                clientMessage.getKey(),
+                serverRunner.encryptData(serverMessage.getKey(), sTestData))).isEqualTo(sTestData);
+    }
+
+}
diff --git a/car-lib/api/test-current.txt b/car-lib/api/test-current.txt
index ca87f9a..02cbd6c 100644
--- a/car-lib/api/test-current.txt
+++ b/car-lib/api/test-current.txt
@@ -6,6 +6,15 @@
 
 }
 
+package android.car.drivingstate {
+
+  public final class CarUxRestrictionsManager {
+    method public synchronized android.car.drivingstate.CarUxRestrictionsConfiguration getConfig() throws android.car.CarNotConnectedException;
+    method public synchronized android.car.drivingstate.CarUxRestrictionsConfiguration getStagedConfig() throws android.car.CarNotConnectedException;
+  }
+
+}
+
 package android.car.media {
 
   public final class CarAudioManager {
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index b93b9e3..7b60538 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
 import android.car.Car;
 import android.car.CarManagerBase;
 import android.car.CarNotConnectedException;
@@ -178,6 +179,49 @@
     }
 
     /**
+     * Get the current staged configuration, staged config file will only be accessible after
+     * the boot up completed or user has been switched.
+     * This methods is only for test purpose, please do not use in production.
+     *
+     * @return current staged configuration, {@code null} if it's not available
+     *
+     * @hide
+     *
+     */
+    @TestApi
+    @Nullable
+    @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
+    public synchronized CarUxRestrictionsConfiguration getStagedConfig()
+            throws CarNotConnectedException {
+        try {
+            return mUxRService.getStagedConfig();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not get staged UX restrictions staged configuration " + e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
+     * Get the current prod configuration
+     *
+     * @return current prod configuration that is in effect.
+     *
+     * @hide
+     *
+     */
+    @TestApi
+    @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
+    public synchronized CarUxRestrictionsConfiguration getConfig()
+            throws CarNotConnectedException {
+        try {
+            return mUxRService.getConfig();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not get production UX restrictions prod configuration" + e);
+            throw new CarNotConnectedException(e);
+        }
+    }
+
+    /**
      * Class that implements the listener interface and gets called back from the
      * {@link com.android.car.CarDrivingStateService} across the binder interface.
      */
diff --git a/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl b/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
index 270c74e..8f48c5e 100644
--- a/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
+++ b/car-lib/src/android/car/drivingstate/ICarUxRestrictionsManager.aidl
@@ -32,4 +32,6 @@
     void unregisterUxRestrictionsChangeListener(in ICarUxRestrictionsChangeListener listener) = 1;
     CarUxRestrictions getCurrentUxRestrictions() = 2;
     boolean saveUxRestrictionsConfigurationForNextBoot(in CarUxRestrictionsConfiguration config) = 3;
+    CarUxRestrictionsConfiguration getStagedConfig() = 4;
+    CarUxRestrictionsConfiguration getConfig() = 5;
 }
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index 868b825..d4d34be 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -194,9 +194,8 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
     }
 
-    @VisibleForTesting
-    @Nullable
-    /* package */ CarUxRestrictionsConfiguration getConfig() {
+    @Override
+    public CarUxRestrictionsConfiguration getConfig() {
         return mCarUxRestrictionsConfiguration;
     }
 
@@ -398,6 +397,18 @@
         return persistConfig(config, CONFIG_FILENAME_STAGED);
     }
 
+    @Override
+    @Nullable
+    public CarUxRestrictionsConfiguration getStagedConfig() {
+        File stagedConfig = mContext.getFileStreamPath(CONFIG_FILENAME_STAGED);
+        if (stagedConfig.exists()) {
+            logd("Attempting to read staged config");
+            return readPersistedConfig(stagedConfig);
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Writes configuration into the specified file.
      *
@@ -668,13 +679,13 @@
     CarUxRestrictionsConfiguration createDefaultConfig() {
         return new CarUxRestrictionsConfiguration.Builder()
                 .setUxRestrictions(CarDrivingStateEvent.DRIVING_STATE_PARKED,
-                      false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
+                        false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
                 .setUxRestrictions(CarDrivingStateEvent.DRIVING_STATE_IDLING,
-                      false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
+                        false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
                 .setUxRestrictions(CarDrivingStateEvent.DRIVING_STATE_MOVING,
-                      true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
+                        true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
                 .setUxRestrictions(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
-                      true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
+                        true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
                 .build();
     }
 
diff --git a/tests/UxRestrictionsSample/res/layout/main_activity.xml b/tests/UxRestrictionsSample/res/layout/main_activity.xml
index 851497a..0b097fb 100644
--- a/tests/UxRestrictionsSample/res/layout/main_activity.xml
+++ b/tests/UxRestrictionsSample/res/layout/main_activity.xml
@@ -26,41 +26,50 @@
       android:orientation="vertical">
 
     <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/status_header"
         android:layout_gravity="center"
         android:padding="@dimen/section_padding"
         android:textSize="@dimen/header_text_size"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <TextView
         android:id="@+id/driving_state"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/driving_state"
         android:textSize="@dimen/info_text_size"
         android:layout_gravity="center"
         android:padding="@dimen/section_padding"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <TextView
         android:id="@+id/do_status"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/is_do_reqd"
         android:textSize="@dimen/info_text_size"
         android:padding="@dimen/section_padding"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <TextView
         android:id="@+id/uxr_status"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/active_restrictions"
         android:padding="@dimen/section_padding"
         android:textSize="@dimen/info_text_size"
+        android:textAppearance="?android:textAppearanceLarge"/>
+
+    <TextView
+        android:id="@+id/show_uxr_config"
         android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:layout_height="wrap_content"
+        android:text="@string/uxr_config_header"
+        android:padding="@dimen/section_padding"
+        android:textSize="@dimen/info_text_size"
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <View
         android:layout_width="match_parent"
@@ -70,12 +79,12 @@
         android:background="@android:color/darker_gray"/>
 
     <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/action_header"
         android:padding="@dimen/section_padding"
         android:textSize="@dimen/header_text_size"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -84,10 +93,29 @@
           android:id="@+id/toggle_status"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
+          android:layout_marginLeft="@dimen/section_padding"
           android:padding="@dimen/section_padding"
           android:text="@string/disable_uxr"
           android:textAllCaps="false"
           android:textSize="@dimen/info_text_size"/>
+      <Button
+          android:id="@+id/show_staged_config"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginLeft="@dimen/section_padding"
+          android:padding="@dimen/section_padding"
+          android:text="@string/show_staged_config"
+          android:textAllCaps="false"
+          android:textSize="@dimen/info_text_size"/>
+      <Button
+          android:id="@+id/show_prod_config"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginLeft="@dimen/section_padding"
+          android:padding="@dimen/section_padding"
+          android:text="@string/show_prod_config"
+          android:textAllCaps="false"
+          android:textSize="@dimen/info_text_size"/>
     </LinearLayout>
 
     <View
@@ -98,12 +126,12 @@
         android:background="@android:color/darker_gray"/>
 
     <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/sample_header"
         android:padding="@dimen/section_padding"
         android:textSize="@dimen/header_text_size"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -112,6 +140,7 @@
           android:id="@+id/launch_message"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
+          android:padding="@dimen/section_padding"
           android:text="@string/sample_msg_activity"
           android:textAllCaps="false"
           android:textSize="@dimen/info_text_size"/>
@@ -125,12 +154,12 @@
         android:background="@android:color/darker_gray"/>
 
     <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/save_uxr_config_header"
         android:padding="@dimen/section_padding"
         android:textSize="@dimen/header_text_size"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:textAppearanceLarge"
-        android:layout_height="wrap_content"/>
+        android:textAppearance="?android:textAppearanceLarge"/>
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -139,6 +168,7 @@
           android:id="@+id/save_uxr_config"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
+          android:padding="@dimen/section_padding"
           android:text="@string/save_uxr_config"
           android:textSize="@dimen/info_text_size"/>
     </LinearLayout>
diff --git a/tests/UxRestrictionsSample/res/values/strings.xml b/tests/UxRestrictionsSample/res/values/strings.xml
index aa32ff4..b36d7f9 100644
--- a/tests/UxRestrictionsSample/res/values/strings.xml
+++ b/tests/UxRestrictionsSample/res/values/strings.xml
@@ -20,8 +20,11 @@
     <string name="driving_state" translatable="false">Driving State: </string>
     <string name="is_do_reqd" translatable="false">Distraction Optimization Required? </string>
     <string name="active_restrictions" translatable="false">Active UX Restrictions: </string>
+    <string name="uxr_config_header" translatable="false">UxR Configuration: </string>
     <string name="action_header" translatable="false"><u>Available Actions</u></string>
     <string name="disable_uxr" translatable="false">Disable Ux Restriction Engine</string>
+    <string name="show_staged_config" translatable="false">Show Staged Config</string>
+    <string name="show_prod_config" translatable="false">Show Production Config</string>
     <string name="sample_header" translatable="false"><u>Sample Activities</u></string>
     <string name="sample_msg_activity" translatable="false">Sample Message Activity</string>
     <string name="return_home" translatable="false"><u>Return Home</u></string>
@@ -30,4 +33,8 @@
     <string name="set_uxr_config_dialog_title" translatable="false">Select restrictions for IDLING/MOVING</string>
     <string name="set_uxr_config_dialog_negative_button" translatable="false">Cancel</string>
     <string name="set_uxr_config_dialog_positive_button" translatable="false">Save UXR Config</string>
+    <string name="no_staged_config" translatable="false">There is no staged configuration found</string>
+    <string name="no_prod_config" translatable="false">There is no production configuration found</string>
+    <string name="staged_config_title" translatable="false">Staged Config</string>
+    <string name="prod_config_title" translatable="false">Production Config</string>
 </resources>
diff --git a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
index cd9c015..27738bc 100644
--- a/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
+++ b/tests/UxRestrictionsSample/src/com/google/android/car/uxr/sample/MainActivity.java
@@ -34,11 +34,14 @@
 import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.JsonWriter;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
 
+import java.io.CharArrayWriter;
+
 /**
  * Sample app that uses components in car support library to demonstrate Car drivingstate UXR
  * status.
@@ -47,7 +50,7 @@
     public static final String TAG = "drivingstate";
 
     // Order of elements is based on number of bits shifted in value of the constants.
-    private static final CharSequence[] UX_RESTRICTION_NAMES = new CharSequence[] {
+    private static final CharSequence[] UX_RESTRICTION_NAMES = new CharSequence[]{
             "BASELINE",
             "NO_DIALPAD",
             "NO_FILTERING",
@@ -70,6 +73,8 @@
     private Button mToggleButton;
     private Button mSampleMsgButton;
     private Button mSaveUxrConfigButton;
+    private Button mShowStagedConfig;
+    private Button mShowProdConfig;
 
     private boolean mEnableUxR;
 
@@ -86,7 +91,6 @@
                                 Car.CAR_UX_RESTRICTION_SERVICE);
                         mCarPackageManager = (CarPackageManager) mCar.getCarManager(
                                 Car.PACKAGE_SERVICE);
-
                         if (mCarDrivingStateManager != null) {
                             mCarDrivingStateManager.registerListener(mDrvStateChangeListener);
                             updateDrivingStateText(
@@ -183,6 +187,10 @@
         mSaveUxrConfigButton = findViewById(R.id.save_uxr_config);
         mSaveUxrConfigButton.setOnClickListener(v -> saveUxrConfig());
 
+        mShowStagedConfig = findViewById(R.id.show_staged_config);
+        mShowStagedConfig.setOnClickListener(v -> showStagedUxRestrictionsConfig());
+        mShowProdConfig = findViewById(R.id.show_prod_config);
+        mShowProdConfig.setOnClickListener(v -> showProdUxRestrictionsConfig());
         mToggleButton.setOnClickListener(v -> updateToggleUxREnable());
 
         mSampleMsgButton = findViewById(R.id.launch_message);
@@ -228,6 +236,56 @@
         }
     }
 
+    private void showStagedUxRestrictionsConfig() {
+        try {
+            CarUxRestrictionsConfiguration stagedConfig =
+                    mCarUxRestrictionsManager.getStagedConfig();
+            if (stagedConfig == null) {
+                new AlertDialog.Builder(this)
+                        .setMessage(R.string.no_staged_config)
+                        .show();
+                return;
+            }
+            CharArrayWriter charWriter = new CharArrayWriter();
+            JsonWriter writer = new JsonWriter(charWriter);
+            writer.setIndent("\t");
+            stagedConfig.writeJson(writer);
+            new AlertDialog.Builder(this)
+                    .setTitle(R.string.staged_config_title)
+                    .setMessage(charWriter.toString())
+                    .show();
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car not connected", e);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void showProdUxRestrictionsConfig() {
+        try {
+            CarUxRestrictionsConfiguration prodConfig =
+                    mCarUxRestrictionsManager.getConfig();
+            if (prodConfig == null) {
+                new AlertDialog.Builder(this)
+                        .setMessage(R.string.no_prod_config)
+                        .show();
+                return;
+            }
+            CharArrayWriter charWriter = new CharArrayWriter();
+            JsonWriter writer = new JsonWriter(charWriter);
+            writer.setIndent("\t");
+            prodConfig.writeJson(writer);
+            new AlertDialog.Builder(this)
+                    .setTitle(R.string.prod_config_title)
+                    .setMessage(charWriter.toString())
+                    .show();
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car not connected", e);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
     private void launchSampleMsgActivity(View view) {
         Intent msgIntent = new Intent(this, SampleMessageActivity.class);
         startActivity(msgIntent);