DO NOT MERGE - Merge Android 10 into master

Bug: 139893257
Change-Id: I02b83849a0fdd86253bb210b8ffef9e2aa800367
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..422989e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,9 @@
+# People who can approve changes for submission.
+jinjian@google.com
+kevinjm@google.com
+
+# TLs
+rlagos@google.com
+
+# TLMs
+nicksauer@google.com
diff --git a/library/common.mk b/library/common.mk
deleted file mode 100644
index 14653cd..0000000
--- a/library/common.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-#
-# Include this make file to build your application against this module.
-#
-# Make sure to include it after you've set all your desired LOCAL variables.
-# Note that you must explicitly set your LOCAL_RESOURCE_DIR before including this file.
-#
-# For example:
-#
-#   LOCAL_RESOURCE_DIR := \
-#        $(LOCAL_PATH)/res
-#
-#   include frameworks/opt/car/setupwizard/library/common.mk
-#
-
-ifeq (,$(findstring car-setup-wizard-lib, $(LOCAL_STATIC_ANDROID_LIBRARIES)))
-    LOCAL_STATIC_ANDROID_LIBRARIES += car-setup-wizard-lib
-endif
\ No newline at end of file
diff --git a/library/Android.bp b/library/main/Android.bp
similarity index 96%
rename from library/Android.bp
rename to library/main/Android.bp
index aeaddf1..af4f995 100644
--- a/library/Android.bp
+++ b/library/main/Android.bp
@@ -1,4 +1,3 @@
-//
 // Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +17,7 @@
     name: "car-setup-wizard-lib",
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
+    libs: ["android.car"],
     static_libs: ["androidx.car_car"],
     optimize: {
         enabled: false,
diff --git a/library/AndroidManifest.xml b/library/main/AndroidManifest.xml
similarity index 76%
rename from library/AndroidManifest.xml
rename to library/main/AndroidManifest.xml
index ae6f822..9bd2e16 100644
--- a/library/AndroidManifest.xml
+++ b/library/main/AndroidManifest.xml
@@ -15,6 +15,9 @@
     limitations under the License.
 -->
 
-<manifest package="com.android.car.setupwizardlib">
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.car.setupwizardlib">
+    <uses-sdk
+        android:minSdkVersion="24"
+        android:targetSdkVersion="26" />
 </manifest>
diff --git a/library/res/values/colors.xml b/library/main/res/color/color_accent_suw.xml
similarity index 66%
copy from library/res/values/colors.xml
copy to library/main/res/color/color_accent_suw.xml
index b0aff07..c2aa65c 100644
--- a/library/res/values/colors.xml
+++ b/library/main/res/color/color_accent_suw.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    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.
@@ -14,7 +14,9 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<resources>
-    <!-- The color of the status bar. -->
-    <color name="colorPrimaryDark">@color/car_grey_300</color>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="@color/blue_400"
+          android:alpha="0.5"/>
+    <item android:color="@color/blue_400"/>
+</selector>
diff --git a/library/res/drawable/button_ripple_bg.xml b/library/main/res/drawable/button_ripple_bg.xml
similarity index 91%
rename from library/res/drawable/button_ripple_bg.xml
rename to library/main/res/drawable/button_ripple_bg.xml
index 1063a78..ace2d5c 100644
--- a/library/res/drawable/button_ripple_bg.xml
+++ b/library/main/res/drawable/button_ripple_bg.xml
@@ -15,5 +15,5 @@
     limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="@color/car_card_ripple_background">
+        android:color="@color/car_card_ripple_background_light">
 </ripple>
diff --git a/library/res/drawable/car_ic_arrow_back.xml b/library/main/res/drawable/car_ic_arrow_back.xml
similarity index 94%
rename from library/res/drawable/car_ic_arrow_back.xml
rename to library/main/res/drawable/car_ic_arrow_back.xml
index c9a0151..be45f39 100644
--- a/library/res/drawable/car_ic_arrow_back.xml
+++ b/library/main/res/drawable/car_ic_arrow_back.xml
@@ -21,5 +21,5 @@
         android:viewportHeight="48.0">
     <path
         android:pathData="M40,22H15.66l11.17,-11.17L24,8 8,24l16,16 2.83,-2.83L15.66,26H40v-4z"
-        android:fillColor="@color/car_tint"/>
+        android:fillColor="@color/car_tint_light"/>
 </vector>
diff --git a/library/main/res/font/sans_medium.xml b/library/main/res/font/sans_medium.xml
new file mode 100644
index 0000000..33e0168
--- /dev/null
+++ b/library/main/res/font/sans_medium.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <font
+        android:font="@font/sans_medium_regular"
+        android:fontStyle="normal"
+        android:fontWeight="500"/>
+    <font
+        android:font="@font/sans_medium_italic"
+        android:fontStyle="italic"
+        android:fontWeight="500"/>
+</font-family>
\ No newline at end of file
diff --git a/library/main/res/font/sans_medium_italic.ttf b/library/main/res/font/sans_medium_italic.ttf
new file mode 100644
index 0000000..7c1667b
--- /dev/null
+++ b/library/main/res/font/sans_medium_italic.ttf
Binary files differ
diff --git a/library/main/res/font/sans_medium_regular.ttf b/library/main/res/font/sans_medium_regular.ttf
new file mode 100644
index 0000000..1543660
--- /dev/null
+++ b/library/main/res/font/sans_medium_regular.ttf
Binary files differ
diff --git a/library/res/layout/base_activity.xml b/library/main/res/layout/base_activity.xml
similarity index 100%
rename from library/res/layout/base_activity.xml
rename to library/main/res/layout/base_activity.xml
diff --git a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml b/library/main/res/layout/base_compat_activity.xml
similarity index 69%
copy from library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
copy to library/main/res/layout/base_compat_activity.xml
index 9cda7cb..b30ed9c 100644
--- a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
+++ b/library/main/res/layout/base_compat_activity.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 Google Inc.
+    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.
@@ -14,10 +14,12 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.car.setupwizardlib.CarSetupWizardLayout
+<com.android.car.setupwizardlib.CarSetupWizardCompatLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:custom="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/car_setup_wizard_layout"
-    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
+    android:layout_height="match_parent"
+    app:showBackButton="true"
+    app:showPrimaryToolbarButton="true"
+    app:primaryToolbarButtonEnabled="true"/>
diff --git a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml b/library/main/res/layout/base_design_activity.xml
similarity index 69%
copy from library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
copy to library/main/res/layout/base_design_activity.xml
index 9cda7cb..9239d89 100644
--- a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
+++ b/library/main/res/layout/base_design_activity.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 Google Inc.
+    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.
@@ -14,10 +14,12 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.car.setupwizardlib.CarSetupWizardLayout
+<com.android.car.setupwizardlib.CarSetupWizardDesignLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:custom="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/car_setup_wizard_layout"
-    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
+    android:layout_height="match_parent"
+    app:showBackButton="true"
+    app:showPrimaryToolbarButton="true"
+    app:primaryToolbarButtonEnabled="true"/>
diff --git a/library/res/layout/car_setup_wizard_layout.xml b/library/main/res/layout/car_setup_wizard_layout.xml
similarity index 100%
rename from library/res/layout/car_setup_wizard_layout.xml
rename to library/main/res/layout/car_setup_wizard_layout.xml
diff --git a/library/res/layout/car_setup_wizard_toolbar.xml b/library/main/res/layout/car_setup_wizard_toolbar.xml
similarity index 91%
rename from library/res/layout/car_setup_wizard_toolbar.xml
rename to library/main/res/layout/car_setup_wizard_toolbar.xml
index 668e95d..d28a616 100644
--- a/library/res/layout/car_setup_wizard_toolbar.xml
+++ b/library/main/res/layout/car_setup_wizard_toolbar.xml
@@ -37,6 +37,7 @@
             android:layout_height="@dimen/car_primary_icon_size"
             android:layout_gravity="center"
             android:background="@drawable/button_ripple_bg"
+            android:contentDescription="@string/back_button_content_description"
             android:src="@drawable/car_ic_arrow_back"/>
     </FrameLayout>
 
@@ -47,7 +48,7 @@
         android:layout_marginEnd="@dimen/car_keyline_1"
         android:layout_toEndOf="@id/back_button_container"
         android:gravity="center_vertical"
-        android:textAppearance="@style/TextAppearance.Car.Title2"/>
+        android:textAppearance="@style/TextAppearance.Car.Body1.Medium"/>
 
     <LinearLayout
         android:id="@+id/button_container"
@@ -55,13 +56,13 @@
         android:layout_height="match_parent"
         android:layout_alignParentEnd="true"
         android:layout_marginEnd="@dimen/car_keyline_1"
+        android:gravity="center_vertical"
         android:orientation="horizontal">
 
         <ViewStub
             android:id="@+id/secondary_toolbar_button_stub"
             android:layout_width="wrap_content"
-            android:layout_height="@dimen/car_button_height"
-            android:layout_gravity="center_vertical"
+            android:layout_height="wrap_content"
             android:layout_marginEnd="@dimen/car_padding_4"
             android:inflatedId="@+id/secondary_toolbar_button"
             android:layout="@layout/flat_button"/>
@@ -69,8 +70,7 @@
         <ViewStub
             android:id="@+id/primary_toolbar_button_stub"
             android:layout_width="wrap_content"
-            android:layout_height="@dimen/car_button_height"
-            android:layout_gravity="center_vertical"
+            android:layout_height="wrap_content"
             android:inflatedId="@+id/primary_toolbar_button"
             android:layout="@layout/primary_button"/>
     </LinearLayout>
diff --git a/library/res/layout/flat_button.xml b/library/main/res/layout/flat_button.xml
similarity index 91%
rename from library/res/layout/flat_button.xml
rename to library/main/res/layout/flat_button.xml
index 11902fd..686912c 100644
--- a/library/res/layout/flat_button.xml
+++ b/library/main/res/layout/flat_button.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="?android:attr/borderlessButtonStyle"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/car_button_height"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/car_button_height"
     android:layout_gravity="center_vertical"
     android:layout_marginStart="@dimen/car_padding_4"
     android:ellipsize="end"
diff --git a/library/res/layout/primary_button.xml b/library/main/res/layout/primary_button.xml
similarity index 90%
rename from library/res/layout/primary_button.xml
rename to library/main/res/layout/primary_button.xml
index 33070b6..5acc697 100644
--- a/library/res/layout/primary_button.xml
+++ b/library/main/res/layout/primary_button.xml
@@ -17,7 +17,8 @@
 <Button
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/car_button_height"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/car_button_height"
     android:layout_marginStart="@dimen/car_padding_4"
     android:layout_gravity="center_vertical"
     android:ellipsize="end"
diff --git a/library/res/values/colors.xml b/library/main/res/values-w1280dp/config.xml
similarity index 75%
rename from library/res/values/colors.xml
rename to library/main/res/values-w1280dp/config.xml
index b0aff07..98343f3 100644
--- a/library/res/values/colors.xml
+++ b/library/main/res/values-w1280dp/config.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    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.
@@ -15,6 +15,6 @@
     limitations under the License.
 -->
 <resources>
-    <!-- The color of the status bar. -->
-    <color name="colorPrimaryDark">@color/car_grey_300</color>
-</resources>
+    <!-- Indicates that this device is not in narrow mode so Activities can adjust accordingly -->
+    <bool name="is_layout_narrow">false</bool>
+</resources>
\ No newline at end of file
diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml
new file mode 100644
index 0000000..957f3dd
--- /dev/null
+++ b/library/main/res/values/attrs.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2017 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.
+-->
+<resources>
+    <!-- Custom attribute definitions for the CarSetupWizardLayout -->
+    <declare-styleable name="CarSetupWizardLayout">
+        <!-- Attributes related to the visibility of the back button -->
+        <attr name="showBackButton" format="boolean"/>
+
+        <!-- Attributes related to the visibility and text of the toolbar title -->
+        <attr name="showToolbarTitle" format="boolean"/>
+        <attr name="toolbarTitleText" format="string"/>
+
+        <!--  Attributes related to the visibility and text of primary continue button -->
+        <attr name="showPrimaryToolbarButton" format="boolean"/>
+        <attr name="primaryToolbarButtonText" format="string"/>
+        <attr name="primaryToolbarButtonEnabled" format="boolean"/>
+        <attr name="primaryToolbarButtonFlat" format="boolean"/>
+
+        <!--  Attributes related to the visibility and text of secondary continue button -->
+        <attr name="showSecondaryToolbarButton" format="boolean"/>
+        <attr name="secondaryToolbarButtonText" format="string"/>
+        <attr name="secondaryToolbarButtonEnabled" format="boolean"/>
+
+        <!--  Attributes related to the visibility and indeterminate/determinate state
+              of the progress bar -->
+        <attr name="showProgressBar" format="boolean"/>
+        <attr name="indeterminateProgressBar" format="boolean"/>
+    </declare-styleable>
+
+    <!-- Custom attribute definitions for the CarSetupWizardBaseLayout -->
+    <declare-styleable name="CarSetupWizardBaseLayout">
+        <!-- Attributes related to the visibility of the back button -->
+        <attr name="showBackButton" format="boolean"/>
+
+        <!-- Attributes related to the visibility and text of the toolbar title -->
+        <attr name="showToolbarTitle" format="boolean"/>
+        <attr name="toolbarTitleText" format="string"/>
+
+        <!--  Attributes related to the visibility and text of primary continue button -->
+        <attr name="showPrimaryToolbarButton" format="boolean"/>
+        <attr name="primaryToolbarButtonText" format="string"/>
+        <attr name="primaryToolbarButtonEnabled" format="boolean"/>
+        <attr name="primaryToolbarButtonFlat" format="boolean"/>
+
+        <!--  Attributes related to the visibility and text of secondary continue button -->
+        <attr name="showSecondaryToolbarButton" format="boolean"/>
+        <attr name="secondaryToolbarButtonText" format="string"/>
+        <attr name="secondaryToolbarButtonEnabled" format="boolean"/>
+
+        <!--  Attributes related to the visibility and indeterminate/determinate state
+              of the progress bar -->
+        <attr name="showProgressBar" format="boolean"/>
+        <attr name="indeterminateProgressBar" format="boolean"/>
+    </declare-styleable>
+
+</resources>
\ No newline at end of file
diff --git a/library/res/values-night/colors.xml b/library/main/res/values/colors.xml
similarity index 75%
rename from library/res/values-night/colors.xml
rename to library/main/res/values/colors.xml
index 1376c6f..6767e03 100644
--- a/library/res/values-night/colors.xml
+++ b/library/main/res/values/colors.xml
@@ -16,11 +16,11 @@
 -->
 <resources>
     <!-- The default color for all activities in the SetupWizard. -->
-    <color name="windowBackground">@color/car_dark_blue_grey_700</color>
+    <color name="windowBackground">@android:color/black</color>
 
     <!-- The color of the status bar. -->
-    <color name="colorPrimaryDark">@color/car_dark_blue_grey_800</color>
+    <color name="colorPrimaryDark">@android:color/black</color>
 
-    <!-- The accent color for the SetupWizard. -->
-    <color name="accentColor">@color/car_yellow_800</color>
+    <color name="blue_400">#6BA5ED</color>
+    <color name="car_suw_tint">@color/blue_400</color>
 </resources>
diff --git a/library/res/values/colors.xml b/library/main/res/values/config.xml
similarity index 75%
copy from library/res/values/colors.xml
copy to library/main/res/values/config.xml
index b0aff07..0cb4687 100644
--- a/library/res/values/colors.xml
+++ b/library/main/res/values/config.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    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.
@@ -15,6 +15,6 @@
     limitations under the License.
 -->
 <resources>
-    <!-- The color of the status bar. -->
-    <color name="colorPrimaryDark">@color/car_grey_300</color>
-</resources>
+    <!-- Indicates that this device is in narrow mode so Activities can adjust accordingly -->
+    <bool name="is_layout_narrow">true</bool>
+</resources>
\ No newline at end of file
diff --git a/library/res/values/dimens.xml b/library/main/res/values/dimens.xml
similarity index 100%
rename from library/res/values/dimens.xml
rename to library/main/res/values/dimens.xml
diff --git a/library/res/values/colors.xml b/library/main/res/values/strings.xml
similarity index 72%
copy from library/res/values/colors.xml
copy to library/main/res/values/strings.xml
index b0aff07..beb37dc 100644
--- a/library/res/values/colors.xml
+++ b/library/main/res/values/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    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.
@@ -15,6 +15,6 @@
     limitations under the License.
 -->
 <resources>
-    <!-- The color of the status bar. -->
-    <color name="colorPrimaryDark">@color/car_grey_300</color>
-</resources>
+    <!-- Content description of the back button for accessibility services like Tallback [CHAR LIMIT=120] -->
+    <string name="back_button_content_description">Navigate back</string>
+</resources>
\ No newline at end of file
diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml
new file mode 100644
index 0000000..6ab739d
--- /dev/null
+++ b/library/main/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<resources>
+    <style name="Widget.Car.SetupWizard.Button" parent="Widget.Car.Button.DarkText">
+        <item name="android:fontFamily">@font/sans_medium</item>
+        <item name="android:textAllCaps">false</item>
+    </style>
+
+    <style name="Widget.Car.SetupWizard.Button.Borderless.Colored"
+           parent="Widget.Car.Button.Borderless.Colored">
+        <item name="android:fontFamily">@font/sans_medium</item>
+        <item name="android:textAllCaps">false</item>
+    </style>
+</resources>
diff --git a/library/res/values/themes.xml b/library/main/res/values/themes.xml
similarity index 61%
rename from library/res/values/themes.xml
rename to library/main/res/values/themes.xml
index 0c064da..b24738a 100644
--- a/library/res/values/themes.xml
+++ b/library/main/res/values/themes.xml
@@ -16,10 +16,17 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- The main theme for all activities within the Car Setup Wizard. -->
-    <style name="Theme.Car.SetupWizard.NoActionBar" parent="Theme.Car.Light.NoActionBar">
+    <style name="Theme.Car.SetupWizard.NoActionBar" parent="Theme.Car.Dark.NoActionBar">
         <item name="android:windowBackground">@color/windowBackground</item>
-        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
-        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:colorButtonNormal">?android:attr/colorAccent</item>
         <item name="android:colorControlHighlight">?android:attr/colorAccent</item>
+        <item name="android:colorControlNormal">?android:attr/colorAccent</item>
+        <item name="android:colorControlActivated">?android:attr/colorAccent</item>
+        <item name="android:buttonStyle">@style/Widget.Car.SetupWizard.Button</item>
+        <item name="android:borderlessButtonStyle">@style/Widget.Car.SetupWizard.Button.Borderless.Colored</item>
+    </style>
+
+    <style name="Theme.Car.SetupWizard.NoActionBar.Accent" parent="Theme.Car.SetupWizard.NoActionBar">
+        <item name="android:colorAccent">@color/color_accent_suw</item>
     </style>
 </resources>
diff --git a/library/src/com/android/car/setupwizardlib/BaseActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseActivity.java
similarity index 87%
rename from library/src/com/android/car/setupwizardlib/BaseActivity.java
rename to library/main/src/com/android/car/setupwizardlib/BaseActivity.java
index 5b9ac15..42137df 100644
--- a/library/src/com/android/car/setupwizardlib/BaseActivity.java
+++ b/library/main/src/com/android/car/setupwizardlib/BaseActivity.java
@@ -19,13 +19,16 @@
 import android.annotation.CallSuper;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.LayoutRes;
+import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.car.setupwizardlib.util.CarDrivingStateMonitor;
 import com.android.car.setupwizardlib.util.CarWizardManagerHelper;
 
 
@@ -41,7 +44,10 @@
  * moving to the next and previous screens in a Setup Wizard
  * <p>Provides setters {@link #setBackButtonVisible(boolean)} for setting CarSetupWizardLayout
  * component attributes
+ *
+ * @deprecated Use {@link BaseCompatActivity} or {@link BaseDesignActivity}.
  */
+@Deprecated
 public class BaseActivity extends FragmentActivity {
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     static final String CONTENT_FRAGMENT_TAG = "CONTENT_FRAGMENT_TAG";
@@ -50,7 +56,9 @@
      * Manager without requesting a result, the framework will choose not to issue a call to
      * onActivityResult with RESULT_CANCELED when navigating backward.
      */
-    private static final int REQUEST_CODE_NEXT = 10000;
+    protected static final int REQUEST_CODE_NEXT = 10000;
+
+    private boolean mNextActionAlreadyTriggered;
 
     /**
      * To implement a specific request code, see the following:
@@ -90,15 +98,15 @@
             }
         });
 
+        resetPrimaryToolbarButtonOnClickListener();
+        resetSecondaryToolbarButtonOnClickListener();
+
         /* If this activity has a saved instance and a content fragment, call onContentFragmentSet()
          * so the appropriate views/events are updated.
          */
         if (savedInstanceState != null && getContentFragment() != null) {
             onContentFragmentSet(getContentFragment());
         }
-
-        resetPrimaryToolbarButtonOnCLickListener();
-        resetSecondaryToolbarButtonOnCLickListener();
     }
 
     @Override
@@ -108,6 +116,32 @@
         // Fragment commits are not allowed once the Activity's state has been saved. Once
         // onStart() has been called, the FragmentManager should now allow commits.
         mAllowFragmentCommits = true;
+        // Need to check for UX restrictions to setup wizard running and exit if they are enabled.
+        CarDrivingStateMonitor.get(this).startMonitor();
+    }
+
+    @Override
+    @CallSuper
+    protected void onResume() {
+        super.onResume();
+        // Need to reset next buttons so that they can be pressed again.
+        mNextActionAlreadyTriggered = false;
+    }
+
+    @Override
+    @CallSuper
+    protected void onPause() {
+        super.onPause();
+        // Need this for visibility for tests.
+    }
+
+    @Override
+    @CallSuper
+    protected void onStop() {
+        super.onStop();
+        // Trigger a stop to the CarDrivingStateMonitor. If the monitor is restarted soon by a
+        // subsequent activity then this will do nothing so as not to thrash the monitor.
+        CarDrivingStateMonitor.get(this).stopMonitor();
     }
 
     @Override
@@ -228,11 +262,18 @@
         if (resultCode == RESULT_CANCELED) {
             throw new IllegalArgumentException("Cannot call nextAction with RESULT_CANCELED");
         }
-        onNextActionInvoked();
         setResultCode(resultCode, data);
+        if (mNextActionAlreadyTriggered) {
+            Log.v("CarSetupWizard",
+                    "BaseActivity: nextAction triggered multiple times without page refresh, "
+                            + "ignoring.");
+            return;
+        }
+        mNextActionAlreadyTriggered = true;
+        onNextActionInvoked();
         Intent nextIntent =
                 CarWizardManagerHelper.getNextIntent(getIntent(), mResultCode, mResultData);
-        startActivityForResult(nextIntent, REQUEST_CODE_NEXT);
+        startActivity(nextIntent);
     }
 
     /**
@@ -318,6 +359,13 @@
     }
 
     /**
+     * Sets the text appearance for the toolbar title.
+     */
+    protected void setToolbarTitleStyle(@StyleRes int style) {
+        mCarSetupWizardLayout.setToolbarTitleStyle(style);
+    }
+
+    /**
      * Sets whether the primary continue button is visible.
      */
     protected void setPrimaryToolbarButtonVisible(boolean visible) {
@@ -360,10 +408,12 @@
      * Reset's the primary toolbar button's on click listener to call {@link #nextAction} with
      * RESULT_OK
      */
-    protected void resetPrimaryToolbarButtonOnCLickListener() {
-        setPrimaryToolbarButtonOnClickListener(v -> nextAction(RESULT_OK));
-    }
+    protected void resetPrimaryToolbarButtonOnClickListener() {
+        setPrimaryToolbarButtonOnClickListener(v -> {
+            nextAction(RESULT_OK);
+        });
 
+    }
 
     /**
      * Sets whether the secondary continue button is visible.
@@ -401,8 +451,10 @@
      * Reset's the secondary toolbar button's on click listener to call {@link #nextAction} with
      * RESULT_OK
      */
-    protected void resetSecondaryToolbarButtonOnCLickListener() {
-        setSecondaryToolbarButtonOnClickListener(v -> nextAction(RESULT_OK));
+    protected void resetSecondaryToolbarButtonOnClickListener() {
+        setSecondaryToolbarButtonOnClickListener(v -> {
+            nextAction(RESULT_OK);
+        });
     }
 
     /**
diff --git a/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java
new file mode 100644
index 0000000..c8bcf4b
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import androidx.annotation.LayoutRes;
+
+/** The base setup wizard activity that applies a light theming to the partner overlay.
+ */
+public class BaseCompatActivity extends BaseSetupWizardActivity {
+    @Override
+    @LayoutRes
+    int getLayout() {
+        return R.layout.base_compat_activity;
+    }
+
+    @Override
+    protected CarSetupWizardCompatLayout getCarSetupWizardLayout() {
+        return (CarSetupWizardCompatLayout) super.getCarSetupWizardLayout();
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java
new file mode 100644
index 0000000..a7288b5
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import androidx.annotation.LayoutRes;
+
+/** The base setup wizard activity that applies heavy theming from the partner overlay.
+ */
+public class BaseDesignActivity extends BaseSetupWizardActivity {
+    @Override
+    @LayoutRes
+    int getLayout() {
+        return R.layout.base_design_activity;
+    }
+
+    @Override
+    protected CarSetupWizardDesignLayout getCarSetupWizardLayout() {
+        return (CarSetupWizardDesignLayout) super.getCarSetupWizardLayout();
+    }
+}
diff --git a/library/src/com/android/car/setupwizardlib/BaseActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
similarity index 83%
copy from library/src/com/android/car/setupwizardlib/BaseActivity.java
copy to library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
index 5b9ac15..cf9829b 100644
--- a/library/src/com/android/car/setupwizardlib/BaseActivity.java
+++ b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -19,21 +19,24 @@
 import android.annotation.CallSuper;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.LayoutRes;
+import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.car.setupwizardlib.util.CarDrivingStateMonitor;
+import com.android.car.setupwizardlib.util.CarSetupWizardUiUtils;
 import com.android.car.setupwizardlib.util.CarWizardManagerHelper;
 
-
 /**
  * Base Activity for CarSetupWizard screens that provides a variety of helper functions that make
  * it easier to work with the CarSetupWizardLayout and moving between Setup Wizard screens.
  *
- * <p>This activity sets an instance of {@link CarSetupWizardLayout} as the Content View.
+ * <p>This activity sets an instance of {@link CarSetupWizardCompatLayout} as the Content View.
  * <p>Provides helper methods like {@link #setContentFragment} and {@link #onContentFragmentSet} for
  * easy updating of the CarSetupWizard layout components based on the current Fragment being
  * displayed
@@ -42,15 +45,17 @@
  * <p>Provides setters {@link #setBackButtonVisible(boolean)} for setting CarSetupWizardLayout
  * component attributes
  */
-public class BaseActivity extends FragmentActivity {
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+abstract class BaseSetupWizardActivity extends FragmentActivity {
+    @VisibleForTesting
     static final String CONTENT_FRAGMENT_TAG = "CONTENT_FRAGMENT_TAG";
     /**
      * Wizard Manager does not actually return an activity result, but if we invoke Wizard
      * Manager without requesting a result, the framework will choose not to issue a call to
      * onActivityResult with RESULT_CANCELED when navigating backward.
      */
-    private static final int REQUEST_CODE_NEXT = 10000;
+    protected static final int REQUEST_CODE_NEXT = 10000;
+
+    private boolean mNextActionAlreadyTriggered;
 
     /**
      * To implement a specific request code, see the following:
@@ -73,14 +78,14 @@
      * {@link #onSaveInstanceState(Bundle)} that fragment commits are not allowed.
      */
     private boolean mAllowFragmentCommits = true;
-    private CarSetupWizardLayout mCarSetupWizardLayout;
+    private CarSetupWizardBaseLayout mCarSetupWizardLayout;
     private Intent mResultData;
 
     @Override
     @CallSuper
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.base_activity);
+        setContentView(getLayout());
 
         mCarSetupWizardLayout = findViewById(R.id.car_setup_wizard_layout);
 
@@ -90,24 +95,52 @@
             }
         });
 
+        resetPrimaryToolbarButtonOnClickListener();
+        resetSecondaryToolbarButtonOnClickListener();
+
         /* If this activity has a saved instance and a content fragment, call onContentFragmentSet()
          * so the appropriate views/events are updated.
          */
         if (savedInstanceState != null && getContentFragment() != null) {
             onContentFragmentSet(getContentFragment());
         }
-
-        resetPrimaryToolbarButtonOnCLickListener();
-        resetSecondaryToolbarButtonOnCLickListener();
     }
 
     @Override
     @CallSuper
     protected void onStart() {
         super.onStart();
+        // Must be done here so that the SystemUI is hidden when back button is clicked
+        CarSetupWizardUiUtils.maybeHideSystemUI(this);
         // Fragment commits are not allowed once the Activity's state has been saved. Once
         // onStart() has been called, the FragmentManager should now allow commits.
         mAllowFragmentCommits = true;
+        // Need to check for UX restrictions to setup wizard running and exit if they are enabled.
+        CarDrivingStateMonitor.get(this).startMonitor();
+    }
+
+    @Override
+    @CallSuper
+    protected void onResume() {
+        super.onResume();
+        // Need to reset next buttons so that they can be pressed again.
+        mNextActionAlreadyTriggered = false;
+    }
+
+    @Override
+    @CallSuper
+    protected void onPause() {
+        super.onPause();
+        // Need this for visibility for tests.
+    }
+
+    @Override
+    @CallSuper
+    protected void onStop() {
+        super.onStop();
+        // Trigger a stop to the CarDrivingStateMonitor. If the monitor is restarted soon by a
+        // subsequent activity then this will do nothing so as not to thrash the monitor.
+        CarDrivingStateMonitor.get(this).stopMonitor();
     }
 
     @Override
@@ -228,11 +261,18 @@
         if (resultCode == RESULT_CANCELED) {
             throw new IllegalArgumentException("Cannot call nextAction with RESULT_CANCELED");
         }
-        onNextActionInvoked();
         setResultCode(resultCode, data);
+        if (mNextActionAlreadyTriggered) {
+            Log.v("CarSetupWizard",
+                    "BaseSetupWizardActivity: nextAction triggered multiple times without"
+                            + "page refresh, ignoring.");
+            return;
+        }
+        mNextActionAlreadyTriggered = true;
+        onNextActionInvoked();
         Intent nextIntent =
                 CarWizardManagerHelper.getNextIntent(getIntent(), mResultCode, mResultData);
-        startActivityForResult(nextIntent, REQUEST_CODE_NEXT);
+        startActivity(nextIntent);
     }
 
     /**
@@ -292,7 +332,6 @@
         return mResultData;
     }
 
-
     // CarSetupWizardLayout Accessors
 
     /**
@@ -318,6 +357,13 @@
     }
 
     /**
+     * Sets the text appearance for the toolbar title.
+     */
+    protected void setToolbarTitleStyle(@StyleRes int style) {
+        mCarSetupWizardLayout.setToolbarTitleStyle(style);
+    }
+
+    /**
      * Sets whether the primary continue button is visible.
      */
     protected void setPrimaryToolbarButtonVisible(boolean visible) {
@@ -360,11 +406,12 @@
      * Reset's the primary toolbar button's on click listener to call {@link #nextAction} with
      * RESULT_OK
      */
-    protected void resetPrimaryToolbarButtonOnCLickListener() {
-        setPrimaryToolbarButtonOnClickListener(v -> nextAction(RESULT_OK));
+    protected void resetPrimaryToolbarButtonOnClickListener() {
+        setPrimaryToolbarButtonOnClickListener(v -> {
+            nextAction(RESULT_OK);
+        });
     }
 
-
     /**
      * Sets whether the secondary continue button is visible.
      */
@@ -401,8 +448,10 @@
      * Reset's the secondary toolbar button's on click listener to call {@link #nextAction} with
      * RESULT_OK
      */
-    protected void resetSecondaryToolbarButtonOnCLickListener() {
-        setSecondaryToolbarButtonOnClickListener(v -> nextAction(RESULT_OK));
+    protected void resetSecondaryToolbarButtonOnClickListener() {
+        setSecondaryToolbarButtonOnClickListener(v -> {
+            nextAction(RESULT_OK);
+        });
     }
 
     /**
@@ -426,12 +475,15 @@
         mCarSetupWizardLayout.setProgressBarVisible(visible);
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     boolean getAllowFragmentCommits() {
         return mAllowFragmentCommits;
     }
 
-    protected CarSetupWizardLayout getCarSetupWizardLayout() {
+    protected CarSetupWizardBaseLayout getCarSetupWizardLayout() {
         return mCarSetupWizardLayout;
     }
+
+    @LayoutRes
+    abstract int getLayout();
 }
diff --git a/library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
similarity index 65%
copy from library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
copy to library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
index c22a912..6a95a15 100644
--- a/library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
+++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,42 +13,59 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.car.setupwizardlib;
 
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
+
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * Custom layout for the Car Setup Wizard. Provides accessors for modifying elements such as buttons
- * and progress bars. Any modifications to elements built by
- * the CarSetupWizardLayout should be done through methods provided by this class unless that is
- * not possible so as to keep the state internally consistent.
+ * and progress bars. Any modifications to elements built by the CarSetupWizardBaseLayout should be
+ * done through methods provided by this class unless that is not possible so as to keep the state
+ * internally consistent.
  */
-public class CarSetupWizardLayout extends LinearLayout {
+class CarSetupWizardBaseLayout extends LinearLayout {
+    private static final String TAG = CarSetupWizardBaseLayout.class.getSimpleName();
     private static final int ANIMATION_DURATION_MS = 100;
 
     private View mBackButton;
     private View mTitleBar;
     private Float mTitleBarElevation;
     private TextView mToolbarTitle;
+    private PartnerConfigHelper mPartnerConfigHelper;
 
     /* <p>The Primary Toolbar Button should always be used when there is only a single action that
      * moves the wizard to the next screen (e.g. Only need a 'Skip' button).
@@ -67,15 +84,16 @@
     private Button mSecondaryToolbarButton;
     private ProgressBar mProgressBar;
 
-    public CarSetupWizardLayout(Context context) {
+    CarSetupWizardBaseLayout(Context context) {
         this(context, null);
     }
 
-    public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs) {
+    CarSetupWizardBaseLayout(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    CarSetupWizardBaseLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
@@ -83,13 +101,14 @@
      * On initialization, the layout gets all of the custom attributes and initializes
      * the custom views that can be set by the user (e.g. back button, continue button).
      */
-    public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
+    CarSetupWizardBaseLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        mPartnerConfigHelper = PartnerConfigHelper.get(context);
         TypedArray attrArray = context.getTheme().obtainStyledAttributes(
                 attrs,
-                R.styleable.CarSetupWizardLayout,
+                R.styleable.CarSetupWizardBaseLayout,
                 0, 0);
 
         init(attrArray);
@@ -117,29 +136,29 @@
 
         try {
             showBackButton = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_showBackButton, true);
+                    R.styleable.CarSetupWizardBaseLayout_showBackButton, true);
             showToolbarTitle = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_showToolbarTitle, false);
+                    R.styleable.CarSetupWizardBaseLayout_showToolbarTitle, false);
             toolbarTitleText = attrArray.getString(
-                    R.styleable.CarSetupWizardLayout_toolbarTitleText);
+                    R.styleable.CarSetupWizardBaseLayout_toolbarTitleText);
             showPrimaryToolbarButton = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_showPrimaryToolbarButton, true);
+                    R.styleable.CarSetupWizardBaseLayout_showPrimaryToolbarButton, true);
             primaryToolbarButtonText = attrArray.getString(
-                    R.styleable.CarSetupWizardLayout_primaryToolbarButtonText);
+                    R.styleable.CarSetupWizardBaseLayout_primaryToolbarButtonText);
             primaryToolbarButtonEnabled = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_primaryToolbarButtonEnabled, true);
+                    R.styleable.CarSetupWizardBaseLayout_primaryToolbarButtonEnabled, true);
             mPrimaryToolbarButtonFlat = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_primaryToolbarButtonFlat, false);
+                    R.styleable.CarSetupWizardBaseLayout_primaryToolbarButtonFlat, false);
             showSecondaryToolbarButton = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_showSecondaryToolbarButton, false);
+                    R.styleable.CarSetupWizardBaseLayout_showSecondaryToolbarButton, false);
             secondaryToolbarButtonText = attrArray.getString(
-                    R.styleable.CarSetupWizardLayout_secondaryToolbarButtonText);
+                    R.styleable.CarSetupWizardBaseLayout_secondaryToolbarButtonText);
             secondaryToolbarButtonEnabled = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_secondaryToolbarButtonEnabled, true);
+                    R.styleable.CarSetupWizardBaseLayout_secondaryToolbarButtonEnabled, true);
             showProgressBar = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_showProgressBar, false);
+                    R.styleable.CarSetupWizardBaseLayout_showProgressBar, false);
             indeterminateProgressBar = attrArray.getBoolean(
-                    R.styleable.CarSetupWizardLayout_indeterminateProgressBar, true);
+                    R.styleable.CarSetupWizardBaseLayout_indeterminateProgressBar, true);
         } finally {
             attrArray.recycle();
         }
@@ -149,12 +168,22 @@
 
         // Set the back button visibility based on the custom attribute.
         setBackButton(findViewById(R.id.back_button));
+        Drawable drawable = mPartnerConfigHelper.getDrawable(
+                getContext(), PartnerConfig.CONFIG_TOOLBAR_BUTTON_ICON_BACK);
+        if (drawable != null) {
+            ((ImageView) mBackButton).setImageDrawable(drawable);
+        }
         setBackButtonVisible(showBackButton);
 
         // Se the title bar.
         setTitleBar(findViewById(R.id.application_bar));
         mTitleBarElevation =
                 getContext().getResources().getDimension(R.dimen.title_bar_drop_shadow_elevation);
+        int toolbarBgColor =
+                mPartnerConfigHelper.getColor(getContext(), PartnerConfig.CONFIG_TOOLBAR_BG_COLOR);
+        if (toolbarBgColor != 0) {
+            mTitleBar.setBackgroundColor(toolbarBgColor);
+        }
 
         // Set the toolbar title visibility and text based on the custom attributes.
         setToolbarTitle(findViewById(R.id.toolbar_title));
@@ -176,6 +205,7 @@
         if (showPrimaryToolbarButton) {
             setPrimaryToolbarButtonText(primaryToolbarButtonText);
             setPrimaryToolbarButtonEnabled(primaryToolbarButtonEnabled);
+            stylePrimaryToolbarButton(mPrimaryToolbarButton);
         } else {
             setPrimaryToolbarButtonVisible(false);
         }
@@ -202,13 +232,13 @@
     /**
      * Set a given view's visibility.
      */
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     void setViewVisible(View view, boolean visible) {
         view.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     // Add or remove the back button touch delegate depending on whether it is visible.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     void updateBackButtonTouchDelegate(boolean visible) {
         if (visible) {
             // Post this action in the parent's message queue to make sure the parent
@@ -257,7 +287,7 @@
         return mBackButton;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setBackButton(View backButton) {
         mBackButton = backButton;
     }
@@ -279,20 +309,13 @@
     }
 
     /**
-     * Sets the title bar view.
-     */
-    private void setTitleBar(View titleBar) {
-        mTitleBar = titleBar;
-    }
-
-    /**
      * Gets the toolbar title.
      */
     public TextView getToolbarTitle() {
         return mToolbarTitle;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setToolbarTitle(TextView toolbarTitle) {
         mToolbarTitle = toolbarTitle;
     }
@@ -312,13 +335,20 @@
     }
 
     /**
+     * Sets the style for the toolbar title.
+     */
+    public void setToolbarTitleStyle(@StyleRes int style) {
+        mToolbarTitle.setTextAppearance(style);
+    }
+
+    /**
      * Gets the primary toolbar button.
      */
     public Button getPrimaryToolbarButton() {
         return mPrimaryToolbarButton;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setPrimaryToolbarButton(Button primaryToolbarButton) {
         mPrimaryToolbarButton = primaryToolbarButton;
     }
@@ -373,6 +403,7 @@
         if (isFlat == mPrimaryToolbarButtonFlat) {
             return;
         }
+        mPrimaryToolbarButtonFlat = isFlat;
         Button newPrimaryButton = createPrimaryToolbarButton(isFlat);
 
         ViewGroup parent = (ViewGroup) findViewById(R.id.button_container);
@@ -382,10 +413,9 @@
 
         // Update state of layout
         setPrimaryToolbarButton(newPrimaryButton);
-        mPrimaryToolbarButtonFlat = isFlat;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     Button createPrimaryToolbarButton(boolean isFlat) {
         int layoutId = isFlat ? R.layout.flat_button : R.layout.primary_button;
         Button newPrimaryButton = (Button) inflate(getContext(), layoutId, null);
@@ -395,6 +425,7 @@
         newPrimaryButton.setText(mPrimaryToolbarButton.getText());
         newPrimaryButton.setOnClickListener(mPrimaryToolbarButtonOnClick);
         newPrimaryButton.setLayoutParams(mPrimaryToolbarButton.getLayoutParams());
+        stylePrimaryToolbarButton(newPrimaryButton);
 
         return newPrimaryButton;
     }
@@ -444,22 +475,6 @@
     }
 
     /**
-     * A method that will inflate the SecondaryToolbarButton if it is has not already been
-     * inflated. If it has been inflated already this method will do nothing.
-     */
-    private void maybeInflateSecondaryToolbarButton() {
-        ViewStub secondaryToolbarButtonStub = findViewById(R.id.secondary_toolbar_button_stub);
-        // If the secondaryToolbarButtonStub is null then the stub has been inflated so there is
-        // nothing to do.
-        if (secondaryToolbarButtonStub != null) {
-            secondaryToolbarButtonStub.inflate();
-            mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
-            setSecondaryToolbarButtonVisible(false);
-        }
-
-    }
-
-    /**
      * Gets the progress bar.
      */
     public ProgressBar getProgressBar() {
@@ -549,4 +564,180 @@
             mTitleBar.setElevation(0f);
         }
     }
+
+    /**
+     * Sets the title bar view.
+     */
+    private void setTitleBar(View titleBar) {
+        mTitleBar = titleBar;
+    }
+
+    /**
+     * A method that inflates the SecondaryToolbarButton if it is has not already been
+     * inflated. If it has been inflated already this method will do nothing.
+     */
+    private void maybeInflateSecondaryToolbarButton() {
+        ViewStub secondaryToolbarButtonStub = findViewById(R.id.secondary_toolbar_button_stub);
+        // If the secondaryToolbarButtonStub is null then the stub has been inflated so there is
+        // nothing to do.
+        if (secondaryToolbarButtonStub != null) {
+            secondaryToolbarButtonStub.inflate();
+            mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
+            setSecondaryToolbarButtonVisible(false);
+
+            setBackground(
+                    mSecondaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR);
+
+            setButtonPadding(mSecondaryToolbarButton);
+            setButtonTypeFace(mSecondaryToolbarButton);
+            setButtonTextSize(mSecondaryToolbarButton);
+            setButtonTextColor(
+                    mSecondaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR);
+
+            // Set button spacing
+            float marginEnd = PartnerConfigHelper.get(getContext()).getDimension(
+                    getContext(),
+                    PartnerConfig.CONFIG_TOOLBAR_BUTTON_SPACING);
+
+            MarginLayoutParams layoutParams =
+                    (MarginLayoutParams) mSecondaryToolbarButton.getLayoutParams();
+            layoutParams.setMarginEnd(Math.round(marginEnd));
+        }
+    }
+
+    /** Sets button text color using partner overlay if exists */
+    @VisibleForTesting
+    void setButtonTextColor(TextView button, PartnerConfig config) {
+        int color = mPartnerConfigHelper.getColor(getContext(), config);
+        if (color != 0) {
+            button.setTextColor(color);
+        }
+    }
+
+    /**
+     * Sets background using partner overlay if exists. Background color and radius are only
+     * applied if background resource doesn't exist. Otherwise default background color and radius
+     * may override what's set in the background.
+     */
+    @VisibleForTesting
+    void setBackground(View view, PartnerConfig bgConfig, PartnerConfig bgColorConfig) {
+        Drawable background = mPartnerConfigHelper.getDrawable(getContext(), bgConfig);
+        if (background == null) {
+            if (view instanceof Button) {
+                setButtonRadius((Button) view);
+            }
+            setBackgroundColor(view, bgColorConfig);
+        } else {
+            view.setBackground(background);
+        }
+    }
+
+    /** Sets button background color using partner overlay if exists */
+    @VisibleForTesting
+    void setBackgroundColor(View button, PartnerConfig config) {
+        int color = mPartnerConfigHelper.getColor(getContext(), config);
+        if (color != 0) {
+            Drawable background = button.getBackground();
+            if (background != null) {
+                background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+            }
+        }
+    }
+
+    /** Sets button text size using partner overlay if exists */
+    @VisibleForTesting
+    void setButtonTextSize(TextView button) {
+        float dimension = mPartnerConfigHelper.getDimension(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE);
+        if (dimension != 0) {
+            button.setTextSize(TypedValue.COMPLEX_UNIT_PX, dimension);
+        }
+    }
+
+    /** Sets button type face with partner overlay if exists */
+    private void setButtonTypeFace(TextView button) {
+        String fontFamily = mPartnerConfigHelper.getString(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_FONT_FAMILY);
+        if (TextUtils.isEmpty(fontFamily)) {
+            return;
+        }
+
+        Typeface typeface = Typeface.create(fontFamily, Typeface.NORMAL);
+        if (Objects.equals(typeface, Typeface.DEFAULT)) {
+            Log.w(TAG, String.format(
+                    "Couldn't find font: %s. Setting default font.",
+                    fontFamily));
+        }
+        button.setTypeface(typeface);
+    }
+
+    /** Sets button radius using partner overlay if exists */
+    private void setButtonRadius(Button button) {
+        float radius = mPartnerConfigHelper.getDimension(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_RADIUS);
+
+        GradientDrawable gradientDrawable = getGradientDrawable(button);
+        if (gradientDrawable != null) {
+            gradientDrawable.setCornerRadius(radius);
+        }
+    }
+
+    private void setButtonPadding(Button button) {
+        int hPadding = Math.round(
+                PartnerConfigHelper.get(getContext()).getDimension(
+                        getContext(),
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL)
+        );
+        int vPadding = Math.round(
+                PartnerConfigHelper.get(getContext()).getDimension(
+                        getContext(),
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL)
+        );
+        button.setPadding(hPadding, vPadding, hPadding, vPadding);
+    }
+
+    private void stylePrimaryToolbarButton(Button primaryButton) {
+        if (!mPrimaryToolbarButtonFlat) {
+            setBackground(
+                    primaryButton,
+                    PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG,
+                    PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG_COLOR);
+        }
+
+        setButtonPadding(primaryButton);
+        setButtonTypeFace(primaryButton);
+        setButtonTextSize(primaryButton);
+
+        PartnerConfig textColorConfig = mPrimaryToolbarButtonFlat
+                ? PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR
+                : PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR;
+        setButtonTextColor(primaryButton, textColorConfig);
+    }
+
+    private GradientDrawable getGradientDrawable(Button button) {
+        Drawable drawable = button.getBackground();
+        if (drawable instanceof InsetDrawable) {
+            return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
+        }
+
+        if (drawable instanceof RippleDrawable) {
+            drawable = ((RippleDrawable) drawable).getDrawable(0);
+            if (drawable instanceof InsetDrawable) {
+                return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
+            }
+            return (GradientDrawable) drawable;
+        }
+
+        return null;
+    }
+
+    private GradientDrawable getGradientDrawableFromInsetDrawable(InsetDrawable insetDrawable) {
+        return (GradientDrawable) insetDrawable.getDrawable();
+    }
 }
diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java
new file mode 100644
index 0000000..2d74aa9
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * This layout applies light theming attributes from the partner overlay.  It's functionally
+ * equivalent to CarSetupWizardBaseLayout which is package-private.  But in the future, it could be
+ * different.
+ */
+public class CarSetupWizardCompatLayout extends CarSetupWizardBaseLayout {
+    public CarSetupWizardCompatLayout(Context context) {
+        this(context, null);
+    }
+
+    public CarSetupWizardCompatLayout(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CarSetupWizardCompatLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public CarSetupWizardCompatLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java
new file mode 100644
index 0000000..45cf62f
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.util.AttributeSet;
+
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
+
+/**
+ * This layout applies heavy theming attributes from the partner overlay.
+ */
+public class CarSetupWizardDesignLayout extends CarSetupWizardBaseLayout {
+    public CarSetupWizardDesignLayout(Context context) {
+        this(context, null);
+    }
+
+    public CarSetupWizardDesignLayout(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CarSetupWizardDesignLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    /**
+     * On initialization, the layout gets all of the custom attributes and initializes
+     * the custom views that can be set by the user (e.g. back button, continue button).
+     */
+    public CarSetupWizardDesignLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(context);
+        int bgColor = partnerConfigHelper.getColor(
+                context,
+                PartnerConfig.CONFIG_LAYOUT_BG_COLOR);
+        if (bgColor != 0) {
+            setBackgroundColor(bgColor);
+        }
+
+        int tintColor = partnerConfigHelper.getColor(
+                context,
+                PartnerConfig.CONFIG_LOADING_INDICATOR_COLOR);
+        if (tintColor != 0) {
+            getProgressBar().setIndeterminateTintList(ColorStateList.valueOf(tintColor));
+        }
+    }
+}
diff --git a/library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
similarity index 72%
rename from library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
rename to library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
index c22a912..554762f 100644
--- a/library/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
+++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java
@@ -19,36 +19,55 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
+
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * Custom layout for the Car Setup Wizard. Provides accessors for modifying elements such as buttons
  * and progress bars. Any modifications to elements built by
  * the CarSetupWizardLayout should be done through methods provided by this class unless that is
  * not possible so as to keep the state internally consistent.
+ *
+ * @deprecated Use {@link CarSetupWizardCompatLayout} or {@link CarSetupWizardDesignLayout}.
  */
+@Deprecated
 public class CarSetupWizardLayout extends LinearLayout {
+    private static final String TAG = CarSetupWizardLayout.class.getSimpleName();
     private static final int ANIMATION_DURATION_MS = 100;
 
     private View mBackButton;
     private View mTitleBar;
     private Float mTitleBarElevation;
     private TextView mToolbarTitle;
+    private PartnerConfigHelper mPartnerConfigHelper;
 
     /* <p>The Primary Toolbar Button should always be used when there is only a single action that
      * moves the wizard to the next screen (e.g. Only need a 'Skip' button).
@@ -87,6 +106,7 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        mPartnerConfigHelper = PartnerConfigHelper.get(context);
         TypedArray attrArray = context.getTheme().obtainStyledAttributes(
                 attrs,
                 R.styleable.CarSetupWizardLayout,
@@ -149,12 +169,22 @@
 
         // Set the back button visibility based on the custom attribute.
         setBackButton(findViewById(R.id.back_button));
+        Drawable drawable = mPartnerConfigHelper.getDrawable(
+                getContext(), PartnerConfig.CONFIG_TOOLBAR_BUTTON_ICON_BACK);
+        if (drawable != null) {
+            ((ImageView) mBackButton).setImageDrawable(drawable);
+        }
         setBackButtonVisible(showBackButton);
 
         // Se the title bar.
         setTitleBar(findViewById(R.id.application_bar));
         mTitleBarElevation =
                 getContext().getResources().getDimension(R.dimen.title_bar_drop_shadow_elevation);
+        int toolbarBgColor =
+                mPartnerConfigHelper.getColor(getContext(), PartnerConfig.CONFIG_TOOLBAR_BG_COLOR);
+        if (toolbarBgColor != 0) {
+            mTitleBar.setBackgroundColor(toolbarBgColor);
+        }
 
         // Set the toolbar title visibility and text based on the custom attributes.
         setToolbarTitle(findViewById(R.id.toolbar_title));
@@ -176,6 +206,18 @@
         if (showPrimaryToolbarButton) {
             setPrimaryToolbarButtonText(primaryToolbarButtonText);
             setPrimaryToolbarButtonEnabled(primaryToolbarButtonEnabled);
+
+            setBackground(
+                    mPrimaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG,
+                    PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG_COLOR);
+
+            setButtonPadding(mPrimaryToolbarButton);
+            setButtonTypeFace(mPrimaryToolbarButton);
+            setButtonTextSize(mPrimaryToolbarButton);
+            setButtonTextColor(
+                    mPrimaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR);
         } else {
             setPrimaryToolbarButtonVisible(false);
         }
@@ -202,13 +244,13 @@
     /**
      * Set a given view's visibility.
      */
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     void setViewVisible(View view, boolean visible) {
         view.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     // Add or remove the back button touch delegate depending on whether it is visible.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     void updateBackButtonTouchDelegate(boolean visible) {
         if (visible) {
             // Post this action in the parent's message queue to make sure the parent
@@ -257,7 +299,7 @@
         return mBackButton;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setBackButton(View backButton) {
         mBackButton = backButton;
     }
@@ -279,20 +321,13 @@
     }
 
     /**
-     * Sets the title bar view.
-     */
-    private void setTitleBar(View titleBar) {
-        mTitleBar = titleBar;
-    }
-
-    /**
      * Gets the toolbar title.
      */
     public TextView getToolbarTitle() {
         return mToolbarTitle;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setToolbarTitle(TextView toolbarTitle) {
         mToolbarTitle = toolbarTitle;
     }
@@ -312,13 +347,20 @@
     }
 
     /**
+     * Sets the style for the toolbar title.
+     */
+    public void setToolbarTitleStyle(@StyleRes int style) {
+        mToolbarTitle.setTextAppearance(style);
+    }
+
+    /**
      * Gets the primary toolbar button.
      */
     public Button getPrimaryToolbarButton() {
         return mPrimaryToolbarButton;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     final void setPrimaryToolbarButton(Button primaryToolbarButton) {
         mPrimaryToolbarButton = primaryToolbarButton;
     }
@@ -385,7 +427,7 @@
         mPrimaryToolbarButtonFlat = isFlat;
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     Button createPrimaryToolbarButton(boolean isFlat) {
         int layoutId = isFlat ? R.layout.flat_button : R.layout.primary_button;
         Button newPrimaryButton = (Button) inflate(getContext(), layoutId, null);
@@ -444,22 +486,6 @@
     }
 
     /**
-     * A method that will inflate the SecondaryToolbarButton if it is has not already been
-     * inflated. If it has been inflated already this method will do nothing.
-     */
-    private void maybeInflateSecondaryToolbarButton() {
-        ViewStub secondaryToolbarButtonStub = findViewById(R.id.secondary_toolbar_button_stub);
-        // If the secondaryToolbarButtonStub is null then the stub has been inflated so there is
-        // nothing to do.
-        if (secondaryToolbarButtonStub != null) {
-            secondaryToolbarButtonStub.inflate();
-            mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
-            setSecondaryToolbarButtonVisible(false);
-        }
-
-    }
-
-    /**
      * Gets the progress bar.
      */
     public ProgressBar getProgressBar() {
@@ -549,4 +575,162 @@
             mTitleBar.setElevation(0f);
         }
     }
+
+    /**
+     * Sets the title bar view.
+     */
+    private void setTitleBar(View titleBar) {
+        mTitleBar = titleBar;
+    }
+
+    /**
+     * A method that inflates the SecondaryToolbarButton if it is has not already been
+     * inflated. If it has been inflated already this method will do nothing.
+     */
+    private void maybeInflateSecondaryToolbarButton() {
+        ViewStub secondaryToolbarButtonStub = findViewById(R.id.secondary_toolbar_button_stub);
+        // If the secondaryToolbarButtonStub is null then the stub has been inflated so there is
+        // nothing to do.
+        if (secondaryToolbarButtonStub != null) {
+            secondaryToolbarButtonStub.inflate();
+            mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
+            setSecondaryToolbarButtonVisible(false);
+
+            setBackground(
+                    mSecondaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR);
+
+            setButtonPadding(mSecondaryToolbarButton);
+            setButtonTypeFace(mSecondaryToolbarButton);
+            setButtonTextSize(mSecondaryToolbarButton);
+            setButtonTextColor(
+                    mSecondaryToolbarButton,
+                    PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR);
+
+            // Set button spacing
+            float marginEnd = PartnerConfigHelper.get(getContext()).getDimension(
+                    getContext(),
+                    PartnerConfig.CONFIG_TOOLBAR_BUTTON_SPACING);
+
+            MarginLayoutParams layoutParams =
+                    (MarginLayoutParams) mSecondaryToolbarButton.getLayoutParams();
+            layoutParams.setMarginEnd(Math.round(marginEnd));
+        }
+    }
+
+    /** Sets button text color using partner overlay if exists */
+    @VisibleForTesting
+    void setButtonTextColor(TextView button, PartnerConfig config) {
+        int color = mPartnerConfigHelper.getColor(getContext(), config);
+        if (color != 0) {
+            button.setTextColor(color);
+        }
+    }
+
+    /**
+     * Sets background using partner overlay if exists. Background color and radius are only
+     * applied if background resource doesn't exist. Otherwise default background color and radius
+     * may override what's set in the background.
+     */
+    @VisibleForTesting
+    void setBackground(View view, PartnerConfig bgConfig, PartnerConfig bgColorConfig) {
+        Drawable background = mPartnerConfigHelper.getDrawable(getContext(), bgConfig);
+        if (background == null) {
+            if (view instanceof Button) {
+                setButtonRadius((Button) view);
+            }
+            setBackgroundColor(view, bgColorConfig);
+        } else {
+            view.setBackground(background);
+        }
+    }
+
+    /** Sets button background color using partner overlay if exists */
+    @VisibleForTesting
+    void setBackgroundColor(View button, PartnerConfig config) {
+        int color = mPartnerConfigHelper.getColor(getContext(), config);
+        if (color != 0) {
+            Drawable background = button.getBackground();
+            if (background != null) {
+                background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+            }
+        }
+    }
+
+    /** Sets button text size using partner overlay if exists */
+    @VisibleForTesting
+    void setButtonTextSize(TextView button) {
+        float dimension = mPartnerConfigHelper.getDimension(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE);
+        if (dimension != 0) {
+            button.setTextSize(TypedValue.COMPLEX_UNIT_PX, dimension);
+        }
+    }
+
+    /** Sets button type face with partner overlay if exists */
+    private void setButtonTypeFace(TextView button) {
+        String fontFamily = mPartnerConfigHelper.getString(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_FONT_FAMILY);
+        if (TextUtils.isEmpty(fontFamily)) {
+            return;
+        }
+
+        Typeface typeface = Typeface.create(fontFamily, Typeface.NORMAL);
+        if (Objects.equals(typeface, Typeface.DEFAULT)) {
+            Log.w(TAG, String.format(
+                    "Couldn't find font: %s. Setting default font.",
+                    fontFamily));
+        }
+        button.setTypeface(typeface);
+    }
+
+    /** Sets button radius using partner overlay if exists */
+    private void setButtonRadius(Button button) {
+        float radius = mPartnerConfigHelper.getDimension(
+                getContext(),
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_RADIUS);
+
+        GradientDrawable gradientDrawable = getGradientDrawable(button);
+        if (gradientDrawable != null) {
+            gradientDrawable.setCornerRadius(radius);
+        }
+    }
+
+    private void setButtonPadding(Button button) {
+        int hPadding = Math.round(
+                PartnerConfigHelper.get(getContext()).getDimension(
+                        getContext(),
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL)
+        );
+        int vPadding = Math.round(
+                PartnerConfigHelper.get(getContext()).getDimension(
+                        getContext(),
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL)
+        );
+        button.setPadding(hPadding, vPadding, hPadding, vPadding);
+    }
+
+    private GradientDrawable getGradientDrawable(Button button) {
+        Drawable drawable = button.getBackground();
+        if (drawable instanceof InsetDrawable) {
+            return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
+        }
+
+        if (drawable instanceof RippleDrawable) {
+            drawable = ((RippleDrawable) drawable).getDrawable(0);
+            if (drawable instanceof InsetDrawable) {
+                return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
+            }
+            return (GradientDrawable) drawable;
+        }
+
+        return null;
+    }
+
+    private GradientDrawable getGradientDrawableFromInsetDrawable(InsetDrawable insetDrawable) {
+        return (GradientDrawable) insetDrawable.getDrawable();
+    }
 }
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java
new file mode 100644
index 0000000..d3f6ea2
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+/** Resources that can be customized by partner overlay APK. */
+public enum PartnerConfig {
+
+    CONFIG_IS_IMMERSIVE(
+            PartnerConfigKey.KEY_IS_IMMERSIVE, ResourceType.BOOLEAN),
+
+    CONFIG_TOOLBAR_BG_COLOR(
+            PartnerConfigKey.KEY_TOOLBAR_BG_COLOR, ResourceType.COLOR),
+
+    CONFIG_TOOLBAR_BUTTON_ICON_BACK(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_ICON_BACK, ResourceType.DRAWABLE),
+
+    CONFIG_TOOLBAR_BUTTON_FONT_FAMILY(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_FONT_FAMILY, ResourceType.STRING),
+
+    CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_PADDING_HORIZONTAL, ResourceType.DIMENSION),
+
+    CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_PADDING_VERTICAL, ResourceType.DIMENSION),
+
+    CONFIG_TOOLBAR_BUTTON_RADIUS(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_RADIUS, ResourceType.DIMENSION),
+
+    CONFIG_TOOLBAR_BUTTON_SPACING(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_SPACING, ResourceType.DIMENSION),
+
+    CONFIG_TOOLBAR_BUTTON_TEXT_SIZE(
+            PartnerConfigKey.KEY_TOOLBAR_BUTTON_TEXT_SIZE, ResourceType.DIMENSION),
+
+    CONFIG_TOOLBAR_PRIMARY_BUTTON_BG(
+            PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_BG, ResourceType.DRAWABLE),
+
+    CONFIG_TOOLBAR_PRIMARY_BUTTON_BG_COLOR(
+            PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_BG_COLOR, ResourceType.COLOR),
+
+    CONFIG_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR(
+            PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
+
+    CONFIG_TOOLBAR_SECONDARY_BUTTON_BG(
+            PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_BG, ResourceType.DRAWABLE),
+
+    CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR(
+            PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_BG_COLOR, ResourceType.COLOR),
+
+    CONFIG_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR(
+            PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
+
+    CONFIG_LOADING_INDICATOR_COLOR(
+            PartnerConfigKey.KEY_LOADING_INDICATOR_COLOR, ResourceType.COLOR),
+
+    CONFIG_LAYOUT_BG_COLOR(
+            PartnerConfigKey.KEY_LAYOUT_BG_COLOR, ResourceType.COLOR);
+
+    public enum ResourceType {
+        COLOR,
+        DRAWABLE,
+        STRING,
+        DIMENSION,
+        BOOLEAN,
+    }
+
+    private final String mResourceName;
+    private final ResourceType mResourceType;
+
+    public ResourceType getResourceType() {
+        return mResourceType;
+    }
+
+    public String getResourceName() {
+        return mResourceName;
+    }
+
+    PartnerConfig(@PartnerConfigKey String resourceName, ResourceType type) {
+        this.mResourceName = resourceName;
+        this.mResourceType = type;
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java
new file mode 100644
index 0000000..2f367a8
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java
@@ -0,0 +1,305 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.EnumMap;
+
+/** The helper reads and caches the partner configurations from Car Setup Wizard. */
+public class PartnerConfigHelper {
+
+    private static final String TAG = PartnerConfigHelper.class.getSimpleName();
+
+    @VisibleForTesting
+    static final String SUW_AUTHORITY = "com.google.android.car.setupwizard.partner";
+
+    @VisibleForTesting
+    static final String SUW_GET_PARTNER_CONFIG_METHOD = "getOverlayConfig";
+    private static volatile PartnerConfigHelper sInstance = null;
+
+    @VisibleForTesting Bundle mResultBundle = null;
+
+    @VisibleForTesting
+    final EnumMap<PartnerConfig, Object> mPartnerResourceCache = new EnumMap<>(PartnerConfig.class);
+
+    /** Factory method to get an instance */
+    public static PartnerConfigHelper get(@NonNull Context context) {
+        if (sInstance == null) {
+            synchronized (PartnerConfigHelper.class) {
+                if (sInstance == null) {
+                    sInstance = new PartnerConfigHelper(context);
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Returns the color of given {@code partnerConfig}, or 0 if the given {@code partnerConfig}
+     * is not found. If the {@code ResourceType} of the given {@code partnerConfig} is not color,
+     * IllegalArgumentException will be thrown.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@code PartnerConfig} of target resource
+     */
+    @ColorInt
+    public int getColor(@NonNull Context context, PartnerConfig partnerConfig) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.COLOR) {
+            throw new IllegalArgumentException("Not a color resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            return (int) mPartnerResourceCache.get(partnerConfig);
+        }
+
+        int result = 0;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return 0;
+            }
+
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+            result = resource.getColor(resourceEntry.getResourceId(), null);
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * Returns the {@code Drawable} of given {@code partnerConfig}, or {@code null} if the given
+     * {@code partnerConfig} is not found. If the {@code ResourceType} of the given {@code
+     * resourceConfig} is not drawable, IllegalArgumentException will be thrown.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@code PartnerConfig} of target resource
+     */
+    @Nullable
+    public Drawable getDrawable(@NonNull Context context, PartnerConfig partnerConfig) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DRAWABLE) {
+            throw new IllegalArgumentException("Not a drawable resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            return (Drawable) mPartnerResourceCache.get(partnerConfig);
+        }
+
+        Drawable result = null;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return null;
+            }
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+
+            // for @null
+            TypedValue outValue = new TypedValue();
+            resource.getValue(resourceEntry.getResourceId(), outValue, true);
+            if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) {
+                return result;
+            }
+
+            result = resource.getDrawable(resourceEntry.getResourceId(), null);
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException | NotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * Returns the string of the given {@code partnerConfig}, or {@code null} if the given {@code
+     * resourceConfig} is not found. If the {@code ResourceType} of the given {@code partnerConfig}
+     * is not string, IllegalArgumentException will be thrown.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@code PartnerConfig} of target resource
+     */
+    @Nullable
+    public String getString(@NonNull Context context, PartnerConfig partnerConfig) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.STRING) {
+            throw new IllegalArgumentException("Not a string resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            return (String) mPartnerResourceCache.get(partnerConfig);
+        }
+
+        String result = null;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return null;
+            }
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+            result = resource.getString(resourceEntry.getResourceId());
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * Returns the dimension of given {@code partnerConfig}. The default return value is 0.
+     *
+     * @param context The context of client activity
+     * @param resourceConfig The {@code PartnerConfig} of target resource
+     */
+    public float getDimension(@NonNull Context context, PartnerConfig resourceConfig) {
+        return getDimension(context, resourceConfig, 0);
+    }
+
+    /**
+     * Returns the dimension of given {@code partnerConfig}. If the given {@code partnerConfig}
+     * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
+     * resourceConfig} is not dimension, will throw IllegalArgumentException.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@code PartnerConfig} of target resource
+     * @param defaultValue The default value
+     */
+    public float getDimension(
+            @NonNull Context context, PartnerConfig partnerConfig, float defaultValue) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DIMENSION) {
+            throw new IllegalArgumentException("Not a dimension resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            return (float) mPartnerResourceCache.get(partnerConfig);
+        }
+
+        float result = defaultValue;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return defaultValue;
+            }
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+            result = resource.getDimension(resourceEntry.getResourceId());
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * Returns the boolean value of given {@code partnerConfig}. If the given {@code partnerConfig}
+     * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
+     * resourceConfig} is not boolean, will throw IllegalArgumentException.
+     *
+     * @param context The context of client activity
+     * @param partnerConfig The {@code PartnerConfig} of target resource
+     * @param defaultValue The default value
+     */
+    public boolean getBoolean(
+            @NonNull Context context, PartnerConfig partnerConfig, boolean defaultValue) {
+        if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.BOOLEAN) {
+            throw new IllegalArgumentException("Not a boolean resource");
+        }
+
+        if (mPartnerResourceCache.containsKey(partnerConfig)) {
+            return (boolean) mPartnerResourceCache.get(partnerConfig);
+        }
+
+        boolean result = defaultValue;
+        try {
+            String resourceName = partnerConfig.getResourceName();
+            ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
+            if (resourceEntry == null) {
+                Log.w(TAG, "Resource not found: " + resourceName);
+                return defaultValue;
+            }
+            Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
+            result = resource.getBoolean(resourceEntry.getResourceId());
+            mPartnerResourceCache.put(partnerConfig, result);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, exception.getMessage());
+        }
+        return result;
+    }
+
+    private void getPartnerConfigBundle(Context context) {
+        if (mResultBundle == null) {
+            try {
+                Uri contentUri =
+                        new Uri.Builder()
+                                .scheme(ContentResolver.SCHEME_CONTENT)
+                                .authority(SUW_AUTHORITY)
+                                .appendPath(SUW_GET_PARTNER_CONFIG_METHOD)
+                                .build();
+                mResultBundle = context.getContentResolver().call(
+                        contentUri,
+                        SUW_GET_PARTNER_CONFIG_METHOD,
+                        /* arg= */ null,
+                        /* extras= */ null);
+                mPartnerResourceCache.clear();
+            } catch (IllegalArgumentException exception) {
+                Log.w(TAG, "Fail to get config from suw provider");
+            }
+        }
+    }
+
+    private Resources getResourcesByPackageName(Context context, String packageName)
+            throws PackageManager.NameNotFoundException {
+        PackageManager manager = context.getPackageManager();
+        return manager.getResourcesForApplication(packageName);
+    }
+
+    private ResourceEntry getResourceEntryFromKey(String resourceName) {
+        if (mResultBundle == null) {
+            return null;
+        }
+        return ResourceEntry.fromBundle(mResultBundle.getBundle(resourceName));
+    }
+
+    private PartnerConfigHelper(Context context) {
+        getPartnerConfigBundle(context);
+    }
+
+
+    @VisibleForTesting
+    static synchronized void resetForTesting() {
+        sInstance = null;
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java
new file mode 100644
index 0000000..e98a533
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import androidx.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+@StringDef({
+        PartnerConfigKey.KEY_IS_IMMERSIVE,
+        PartnerConfigKey.KEY_TOOLBAR_BG_COLOR,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_ICON_BACK,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_FONT_FAMILY,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_PADDING_HORIZONTAL,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_PADDING_VERTICAL,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_RADIUS,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_SPACING,
+        PartnerConfigKey.KEY_TOOLBAR_BUTTON_TEXT_SIZE,
+        PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_BG,
+        PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_BG_COLOR,
+        PartnerConfigKey.KEY_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR,
+        PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_BG,
+        PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_BG_COLOR,
+        PartnerConfigKey.KEY_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR,
+        PartnerConfigKey.KEY_LOADING_INDICATOR_COLOR,
+        PartnerConfigKey.KEY_LAYOUT_BG_COLOR
+})
+
+/** Resource names that can be customized by partner overlay APK. */
+public @interface PartnerConfigKey {
+
+    String KEY_IS_IMMERSIVE = "suw_compat_is_immersive";
+
+    String KEY_TOOLBAR_BG_COLOR = "suw_compat_toolbar_bg_color";
+
+    String KEY_TOOLBAR_BUTTON_ICON_BACK = "suw_compat_toolbar_button_icon_back";
+
+    String KEY_TOOLBAR_BUTTON_FONT_FAMILY = "suw_compat_toolbar_button_font_family";
+
+    String KEY_TOOLBAR_BUTTON_TEXT_SIZE = "suw_compat_toolbar_button_text_size";
+
+    String KEY_TOOLBAR_BUTTON_PADDING_HORIZONTAL = "suw_compat_toolbar_button_padding_horizontal";
+
+    String KEY_TOOLBAR_BUTTON_PADDING_VERTICAL = "suw_compat_toolbar_button_padding_vertical";
+
+    String KEY_TOOLBAR_BUTTON_RADIUS = "suw_compat_toolbar_button_radius";
+
+    String KEY_TOOLBAR_BUTTON_SPACING = "suw_compat_toolbar_button_spacing";
+
+    String KEY_TOOLBAR_PRIMARY_BUTTON_BG = "suw_compat_toolbar_primary_button_bg";
+
+    String KEY_TOOLBAR_PRIMARY_BUTTON_BG_COLOR =
+            "suw_compat_toolbar_primary_button_bg_color";
+
+    String KEY_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR = "suw_compat_toolbar_primary_button_text_color";
+
+    String KEY_TOOLBAR_SECONDARY_BUTTON_BG = "suw_compat_toolbar_secondary_button_bg";
+
+    String KEY_TOOLBAR_SECONDARY_BUTTON_BG_COLOR = "suw_compat_toolbar_secondary_button_bg_color";
+
+    String KEY_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR =
+            "suw_compat_toolbar_secondary_button_text_color";
+
+    String KEY_LOADING_INDICATOR_COLOR = "suw_design_loading_indicator_color";
+
+    String KEY_LAYOUT_BG_COLOR = "suw_design_layout_bg_color";
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java b/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java
new file mode 100644
index 0000000..6d5d39b
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * A potentially cross-package resource entry, which can then be retrieved using {@link
+ * PackageManager#getResourcesForApplication(String)}. This class can also be sent across to other
+ * packages on IPC via the Bundle representation.
+ */
+public final class ResourceEntry {
+    @VisibleForTesting static final String KEY_PACKAGE_NAME = "packageName";
+    @VisibleForTesting static final String KEY_RESOURCE_NAME = "resourceName";
+    @VisibleForTesting static final String KEY_RESOURCE_ID = "resourceId";
+
+    private final String mPackageName;
+    private final String mResourceName;
+    private final int mResourceId;
+
+    /**
+     * Creates a {@code ResourceEntry} object from a provided bundle.
+     *
+     * @param bundle the source bundle needs to have all the information for a {@code ResourceEntry}
+     */
+    public static ResourceEntry fromBundle(@Nullable Bundle bundle) {
+        if (bundle == null
+                || !bundle.containsKey(KEY_PACKAGE_NAME)
+                || !bundle.containsKey(KEY_RESOURCE_NAME)
+                || !bundle.containsKey(KEY_RESOURCE_ID)) {
+            return null;
+        }
+
+        String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        String resourceName = bundle.getString(KEY_RESOURCE_NAME);
+        int resourceId = bundle.getInt(KEY_RESOURCE_ID);
+        return new ResourceEntry(packageName, resourceName, resourceId);
+    }
+
+    public ResourceEntry(String packageName, String resourceName, int resourceId) {
+        mPackageName = packageName;
+        mResourceName = resourceName;
+        mResourceId = resourceId;
+    }
+
+    public String getPackageName() {
+        return this.mPackageName;
+    }
+
+    public String getResourceName() {
+        return this.mResourceName;
+    }
+
+    public int getResourceId() {
+        return this.mResourceId;
+    }
+
+    /**
+     * Returns a bundle representation of this resource entry, which can then be sent over IPC.
+     *
+     * @see #fromBundle(Bundle)
+     */
+    public Bundle toBundle() {
+        Bundle result = new Bundle();
+        result.putString(KEY_PACKAGE_NAME, mPackageName);
+        result.putString(KEY_RESOURCE_NAME, mResourceName);
+        result.putInt(KEY_RESOURCE_ID, mResourceId);
+        return result;
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
new file mode 100644
index 0000000..5408aa1
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java
@@ -0,0 +1,270 @@
+/*
+ * 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 com.android.car.setupwizardlib.util;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Monitor that listens for changes in the driving state so that it can trigger an exit of the
+ * setup wizard when {@link CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP}
+ * is active.
+ */
+public class CarDrivingStateMonitor implements
+        CarUxRestrictionsManager.OnUxRestrictionsChangedListener {
+
+    public static final String EXIT_BROADCAST_ACTION =
+            "com.android.car.setupwizardlib.driving_exit";
+
+    private static final String TAG = "CarDrivingStateMonitor";
+    private static final long DISCONNECT_DELAY_MS = 700;
+
+    private Car mCar;
+    private CarUxRestrictionsManager mRestrictionsManager;
+    // Need to track the number of times the monitor is started so a single stopMonitor call does
+    // not override them all.
+    private int mMonitorStartedCount;
+    // Flag that allows the monitor to be started for a ux restrictions check but not kept running.
+    // This is particularly useful when a DrivingExit is triggered by an app external to the base
+    // setup wizard package and we need to verify that it is a valid driving exit.
+    private boolean mStopMonitorAfterUxCheck;
+    private final Context mContext;
+    @VisibleForTesting
+    final Handler mHandler = new Handler(Looper.getMainLooper());
+    @VisibleForTesting
+    final Runnable mDisconnectRunnable = this::disconnectCarMonitor;
+
+    private CarDrivingStateMonitor(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Returns the singleton instance of CarDrivingStateMonitor.
+     */
+    public static CarDrivingStateMonitor get(Context context) {
+        return CarHelperRegistry.getOrCreateWithAppContext(
+                context.getApplicationContext(),
+                CarDrivingStateMonitor.class,
+                CarDrivingStateMonitor::new);
+    }
+
+    /**
+     * Starts the monitor listening to driving state changes.
+     */
+    public synchronized void startMonitor() {
+        if (isVerboseLoggable()) {
+            Log.v(TAG, "Starting monitor");
+        }
+        mMonitorStartedCount++;
+        if (mMonitorStartedCount == 0) {
+            return;
+        }
+        mHandler.removeCallbacks(mDisconnectRunnable);
+        if (mCar != null) {
+            if (mCar.isConnected()) {
+                try {
+                    onUxRestrictionsChanged(mRestrictionsManager.getCurrentCarUxRestrictions());
+                } catch (CarNotConnectedException e) {
+                    Log.e(TAG, "Car not connected", e);
+                }
+            } else {
+                try {
+                    mCar.connect();
+                } catch (IllegalStateException e) {
+                    // Connection failure - already connected or connecting.
+                    Log.e(TAG, "Failure connecting to Car object.", e);
+                }
+            }
+            return;
+        }
+        mCar = Car.createCar(mContext, new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                try {
+                    mRestrictionsManager = (CarUxRestrictionsManager)
+                            mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+                    if (mRestrictionsManager == null) {
+                        Log.e(TAG, "Unable to get CarUxRestrictionsManager");
+                        return;
+                    }
+                    onUxRestrictionsChanged(mRestrictionsManager.getCurrentCarUxRestrictions());
+                    mRestrictionsManager.registerListener(CarDrivingStateMonitor.this);
+                    if (mStopMonitorAfterUxCheck) {
+                        mStopMonitorAfterUxCheck = false;
+                        stopMonitor();
+                    }
+                } catch (CarNotConnectedException e) {
+                    Log.e(TAG, "Car not connected", e);
+                }
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                try {
+                    if (mRestrictionsManager != null) {
+                        mRestrictionsManager.unregisterListener();
+                        mRestrictionsManager = null;
+                    }
+                } catch (CarNotConnectedException e) {
+                    Log.e(TAG, "Car not connected", e);
+                }
+            }
+        });
+        try {
+            mCar.connect();
+        } catch (IllegalStateException e) {
+            // Connection failure - already connected or connecting.
+            Log.e(TAG, "Failure connecting to Car object.", e);
+        }
+    }
+
+    /**
+     * Stops the monitor from listening for driving state changes. This will only occur after a
+     * set delay so that calling stop/start in quick succession doesn't actually need to reconnect
+     * to the service repeatedly. This monitor also maintains parity between started and stopped so
+     * 2 started calls requires two stop calls to stop.
+     */
+    public synchronized void stopMonitor() {
+        if (isVerboseLoggable()) {
+            Log.v(TAG, "stopMonitor");
+        }
+        mHandler.removeCallbacks(mDisconnectRunnable);
+        mMonitorStartedCount--;
+        if (mMonitorStartedCount == 0) {
+            if (isVerboseLoggable()) {
+                Log.v(TAG, "Scheduling driving monitor timeout");
+            }
+            mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY_MS);
+        }
+        if (mMonitorStartedCount < 0) {
+            mMonitorStartedCount = 0;
+        }
+    }
+
+    private void disconnectCarMonitor() {
+        if (isVerboseLoggable()) {
+            Log.v(TAG, "Timeout finished, disconnecting Car Monitor");
+        }
+        if (mMonitorStartedCount > 0) {
+            return;
+        }
+        try {
+            if (mRestrictionsManager != null) {
+                mRestrictionsManager.unregisterListener();
+                mRestrictionsManager = null;
+            }
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car not connected for unregistering listener", e);
+        }
+
+        if (mCar == null || !mCar.isConnected()) {
+            return;
+        }
+
+        try {
+            mCar.disconnect();
+        } catch (IllegalStateException e) {
+            // Connection failure - already disconnected or disconnecting.
+            Log.e(TAG, "Failure disconnecting from Car object", e);
+        }
+    }
+
+    /**
+     * Returns {@code true} if the current driving state restricts setup from being completed.
+     */
+    public boolean checkIsSetupRestricted() {
+        if (mMonitorStartedCount <= 0 && (mCar == null || !mCar.isConnected())) {
+            if (isVerboseLoggable()) {
+                Log.v(TAG, "Starting monitor to perform restriction check, returning false for "
+                        + "restrictions in the meantime");
+            }
+            mStopMonitorAfterUxCheck = true;
+            startMonitor();
+            return false;
+        }
+        if (mRestrictionsManager == null) {
+            if (isVerboseLoggable()) {
+                Log.v(TAG, "Restrictions manager null in checkIsSetupRestricted, returning false");
+            }
+            return false;
+        }
+        try {
+            return checkIsSetupRestricted(mRestrictionsManager.getCurrentCarUxRestrictions());
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "CarNotConnected in checkIsSetupRestricted, returning false", e);
+        }
+        return false;
+    }
+
+    private boolean checkIsSetupRestricted(CarUxRestrictions restrictionInfo) {
+        return (restrictionInfo.getActiveRestrictions()
+                & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) != 0;
+    }
+
+    @Override
+    public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) {
+        // Check if setup restriction is active.
+        if (isVerboseLoggable()) {
+            Log.v(TAG, "onUxRestrictionsChanged");
+        }
+
+        // Get the current CarUxRestrictions rather than trusting the ones passed in.
+        // This prevents in part interference from other applications triggering a setup wizard
+        // exit unnecessarily, though the broadcast is also checked on the receiver side.
+        if (mRestrictionsManager != null) {
+            try {
+                restrictionInfo = mRestrictionsManager.getCurrentCarUxRestrictions();
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car not connected in onUxRestrictionsChanged, doing nothing.", e);
+            }
+        }
+
+        if (checkIsSetupRestricted(restrictionInfo)) {
+            if (isVerboseLoggable()) {
+                Log.v(TAG, "Triggering driving exit broadcast");
+            }
+            Intent broadcastIntent = new Intent();
+            broadcastIntent.setAction(EXIT_BROADCAST_ACTION);
+            mContext.sendBroadcast(broadcastIntent);
+        }
+    }
+
+    private boolean isVerboseLoggable() {
+        return Log.isLoggable(TAG, Log.VERBOSE);
+    }
+
+    /**
+     * Resets the car driving state monitor. This is only for use in testing.
+     */
+    @VisibleForTesting
+    public static void reset(Context context) {
+        CarHelperRegistry.getRegistry(context).putHelper(
+                CarDrivingStateMonitor.class, new CarDrivingStateMonitor(context));
+    }
+}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java b/library/main/src/com/android/car/setupwizardlib/util/CarHelperInjectionContext.java
similarity index 60%
copy from library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java
copy to library/main/src/com/android/car/setupwizardlib/util/CarHelperInjectionContext.java
index a0bc9ba..db15d1e 100644
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarHelperInjectionContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.car.setupwizardlib.robolectric;
+package com.android.car.setupwizardlib.util;
 
-public class TestConfig {
-    public static final int SDK_VERSION = 26;
-    public static final String MANIFEST_PATH =
-            "frameworks/opt/car/setupwizard/library/AndroidManifest.xml";
+import androidx.annotation.NonNull;
+
+/** An interface that returns an injected {@link CarHelperRegistry} */
+public interface CarHelperInjectionContext {
+
+    /** Returns the injected {@link CarHelperRegistry} */
+    @NonNull
+    CarHelperRegistry getCarHelperRegistry();
 }
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java b/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java
new file mode 100644
index 0000000..01447aa
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java
@@ -0,0 +1,137 @@
+/*
+ * 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 com.android.car.setupwizardlib.util;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/** A registry of singleton-like helpers, which can be injected by the application for testing. */
+public class CarHelperRegistry {
+
+    /**
+     * Interface that creates the helper.  Typically a constructor that takes a context.
+     *
+     * @param <H> The helper class.
+     */
+    public interface HelperCreator<H> {
+
+        /** Returns an instance of the helper class.  */
+        @NonNull
+        H createHelper(@NonNull Context appContext);
+    }
+
+    private static final CarHelperRegistry GLOBAL_REGISTRY = new CarHelperRegistry();
+
+    private final ConcurrentHashMap<Class<?>, Object> mMap = new ConcurrentHashMap<>();
+
+    /**
+     * Query the application context for an injected registry, or return the global registry if one
+     * doesn't exist. Normal runs would not have an injected registry and therefore use the global
+     * one, while test runs will typically inject a registry so it can inject individual helpers for
+     * testing.
+     *
+     * @param context The context to get the registry from.
+     * @return The registry injected by the application context if one exists, or the global
+     *     registry.
+     */
+    @NonNull
+    @VisibleForTesting
+    static synchronized CarHelperRegistry getRegistry(@NonNull Context context) {
+        final Context applicationContext = context.getApplicationContext();
+        if (applicationContext instanceof CarHelperInjectionContext) {
+            return ((CarHelperInjectionContext) applicationContext).getCarHelperRegistry();
+        }
+        return GLOBAL_REGISTRY;
+    }
+
+    /**
+     * Get the helper from the registry if it exists, or create and put one into the registry if it
+     * does not exist.
+     *
+     * <p>Since helpers are singleton-like, the context passed to the creator is always the
+     * application context so it doesn't leak local contexts pass their lifecycles.
+     *
+     * @param context The context to create the helper with. This can be any context as this method
+     *     will call {@link Context#getApplicationContext()} to ensure the application context is
+     *     used.
+     * @param cls The helper class.
+     * @param creator The method to create the helper. Typically if the helper has a constructor
+     *     that takes a context, this creator will be {@code MyHelper::new}.
+     * @param <H> The helper class.
+     * @return The helper in the registry, either existing or newly registered.
+     */
+    @NonNull
+    public static <H> H getOrCreateWithAppContext(
+            @NonNull Context context, @NonNull Class<H> cls, @NonNull HelperCreator<H> creator) {
+        return CarHelperRegistry.getRegistry(context)
+                .getOrCreateHelper(context.getApplicationContext(), cls, creator);
+    }
+
+    /**
+     * Retrieve a helper from the registry.
+     *
+     * @param cls The helper class.
+     * @param <H> The helper class.
+     * @return The helper of the given {@code cls}, or null if no helper of {@code cls} is
+     *     registered.
+     */
+    @Nullable
+    @SuppressWarnings("unchecked")
+    @VisibleForTesting
+    <H> H getHelper(@NonNull Class<H> cls) {
+        return (H) mMap.get(cls);
+    }
+
+    /** @see #getOrCreateWithAppContext(Context, Class, HelperCreator) */
+    @NonNull
+    @VisibleForTesting
+    <H> H getOrCreateHelper(
+            @NonNull Context appContext, @NonNull Class<H> cls, @NonNull HelperCreator<H> creator) {
+        // Synchronize on the class to ensure only creator is only called once (per classloader)
+        synchronized (cls) {
+            H helper = getHelper(cls);
+            if (helper == null) {
+                helper = creator.createHelper(appContext);
+                putHelper(cls, helper);
+            }
+            return helper;
+        }
+    }
+
+    @VisibleForTesting
+    int size() {
+        return mMap.size();
+    }
+
+    /**
+     * Put a helper for the given {@code cls} into the registry. Outside of this class, this should
+     * only be used to inject helper for testing purposes. Singletons should use {@link
+     * #getOrCreateWithAppContext(Context, Class, HelperCreator)} to create the instance which will
+     *
+     * @param cls The helper class.
+     * @param helper The helper instance to add to the registry.
+     * @param <H> The helper class.
+     */
+    public <H> void putHelper(@NonNull Class<H> cls, @NonNull H helper) {
+        mMap.put(cls, helper);
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarOrientationHelper.java b/library/main/src/com/android/car/setupwizardlib/util/CarOrientationHelper.java
new file mode 100644
index 0000000..efe0e90
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarOrientationHelper.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.car.setupwizardlib.util;
+
+import android.content.Context;
+
+import com.android.car.setupwizardlib.R;
+
+/**
+ * Helper class for determining which orientation mode the device is in (narrow or wide) so that
+ * layouts can be adjusted accordingly.
+ */
+public class CarOrientationHelper {
+
+    /**
+     * Will return {@code true} if the context is in narrow mode.
+     */
+    public static boolean isNarrowOrientation(Context context) {
+        return context.getResources().getBoolean(R.bool.is_layout_narrow);
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java
new file mode 100644
index 0000000..5a87323
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.android.car.setupwizardlib.util;
+
+import android.app.Activity;
+import android.util.Log;
+import android.view.View;
+
+import androidx.core.util.Preconditions;
+
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
+
+/** Utilities to aid in UI for car setup wizard flow. */
+public final class CarSetupWizardUiUtils {
+    private static final String TAG = CarSetupWizardUiUtils.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Hide system UI if configured as such by partner */
+    public static void maybeHideSystemUI(Activity activity) {
+        Preconditions.checkNotNull(activity);
+
+        if (!PartnerConfigHelper.get(activity)
+                .getBoolean(activity, PartnerConfig.CONFIG_IS_IMMERSIVE, true)) {
+            if (VERBOSE) {
+                Log.v(TAG, "Immersive mode disabled");
+            }
+            return;
+        }
+        if (VERBOSE) {
+            Log.v(TAG, "Setting immersive mode for SystemUi");
+        }
+        // See https://developer.android.com/training/system-ui/immersive#EnableFullscreen
+        // Enables regular immersive mode.
+        // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
+        // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        View decorView = activity.getWindow().getDecorView();
+        decorView.setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                        // Set the content to appear under the system bars so that the
+                        // content doesn't resize when the system bars hide and show.
+                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                        // Hide the nav bar and status bar
+                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
+    }
+
+    private CarSetupWizardUiUtils() {
+    }
+}
diff --git a/library/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java b/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java
similarity index 93%
rename from library/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java
rename to library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java
index 6e812c6..7f5cf7a 100644
--- a/library/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java
+++ b/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java
@@ -100,6 +100,17 @@
     }
 
     /**
+     * Checks whether an intent is running in the deferred setup wizard flow.
+     *
+     * @param intent The intent to be checked, usually from
+     *               {@link android.app.Activity#getIntent()}.
+     * @return true if the intent passed in was running in deferred setup wizard.
+     */
+    public static boolean isDeferredIntent(Intent intent) {
+        return intent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false);
+    }
+
+    /**
      * Check whether an intent is intended for the dealer.
      *
      * @param intent The intent to be checked, usually from
diff --git a/library/src/com/android/car/setupwizardlib/util/ResultCodes.java b/library/main/src/com/android/car/setupwizardlib/util/ResultCodes.java
similarity index 100%
rename from library/src/com/android/car/setupwizardlib/util/ResultCodes.java
rename to library/main/src/com/android/car/setupwizardlib/util/ResultCodes.java
diff --git a/library/main/tests/robotests/Android.bp b/library/main/tests/robotests/Android.bp
new file mode 100644
index 0000000..13335f3
--- /dev/null
+++ b/library/main/tests/robotests/Android.bp
@@ -0,0 +1,33 @@
+//##############################################################
+// CarSetupWizardLib app just for Robolectric test target.     #
+//##############################################################
+android_app {
+    name: "CarSetupWizardLib",
+
+    resource_dirs: ["res"],
+
+    platform_apis: true,
+
+    privileged: true,
+
+    libs: ["android.car"],
+
+    static_libs: ["car-setup-wizard-lib"],
+}
+
+//##############################################################
+// CarSetupWizardLib Robolectric test target.                  #
+//##############################################################
+android_robolectric_test {
+    name: "CarSetupWizardLibRoboTests",
+
+    srcs: ["src/**/*.java"],
+
+    java_resource_dirs: ["config"],
+
+    libs: [
+        "android.car",
+    ],
+
+    instrumentation_for: "CarSetupWizardLib",
+}
diff --git a/library/tests/robotests/AndroidManifest.xml b/library/main/tests/robotests/AndroidManifest.xml
similarity index 100%
rename from library/tests/robotests/AndroidManifest.xml
rename to library/main/tests/robotests/AndroidManifest.xml
diff --git a/library/tests/robotests/config/robolectric.properties b/library/main/tests/robotests/config/robolectric.properties
similarity index 87%
rename from library/tests/robotests/config/robolectric.properties
rename to library/main/tests/robotests/config/robolectric.properties
index 4cefc39..3626c87 100644
--- a/library/tests/robotests/config/robolectric.properties
+++ b/library/main/tests/robotests/config/robolectric.properties
@@ -13,5 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-manifest=frameworks/opt/car/setupwizard/library/tests/robotests/AndroidManifest.xml
 sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/library/tests/robotests/res/layout/base_activity_test_layout.xml b/library/main/tests/robotests/res/layout/base_activity_test_layout.xml
similarity index 100%
rename from library/tests/robotests/res/layout/base_activity_test_layout.xml
rename to library/main/tests/robotests/res/layout/base_activity_test_layout.xml
diff --git a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml b/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
similarity index 93%
rename from library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
rename to library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
index 9cda7cb..dd74b11 100644
--- a/library/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
+++ b/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml
@@ -14,7 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.car.setupwizardlib.CarSetupWizardLayout
+<com.android.car.setupwizardlib.CarSetupWizardCompatLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:custom="http://schemas.android.com/apk/res-auto"
     android:id="@+id/car_setup_wizard_layout"
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java
new file mode 100644
index 0000000..632ed6d
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java
@@ -0,0 +1,583 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import androidx.annotation.StyleRes;
+import androidx.fragment.app.Fragment;
+
+import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
+import com.android.car.setupwizardlib.robolectric.TestHelper;
+import com.android.car.setupwizardlib.shadows.ShadowCar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowTextView;
+
+/**
+ * Unit tests for the {@link BaseCompatActivity}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowCar.class)
+public class BaseCompatActivityTest extends BaseRobolectricTest {
+    private BaseCompatActivity mBaseCompatActivity;
+    private CarSetupWizardBaseLayout mCarSetupWizardLayout;
+    private ActivityController<BaseCompatActivity> mActivityController;
+    @Mock
+    private CarUxRestrictionsManager mMockRestrictionsManager;
+    @Mock
+    private CarUxRestrictions mMockRestrictions;
+
+    @Before
+    public void setupBaseCompatActivityAndLayout() throws CarNotConnectedException {
+        mActivityController = Robolectric.buildActivity(BaseCompatActivity.class).create();
+        mBaseCompatActivity = mActivityController.get();
+        mCarSetupWizardLayout = mBaseCompatActivity.getCarSetupWizardLayout();
+        ShadowCar.setCarManager(Car.CAR_UX_RESTRICTION_SERVICE, mMockRestrictionsManager);
+        doReturn(mMockRestrictions).when(mMockRestrictionsManager).getCurrentCarUxRestrictions();
+        doReturn(CarUxRestrictions.UX_RESTRICTIONS_BASELINE).when(mMockRestrictions)
+                .getActiveRestrictions();
+    }
+
+    @Test
+    public void testLayoutIsCompat() {
+        assertThat(mBaseCompatActivity.getLayout()).isEqualTo(R.layout.base_compat_activity);
+    }
+
+    @Test
+    public void testGetCarSetupWizardLayout() {
+        assertThat(mBaseCompatActivity.getCarSetupWizardLayout())
+                .isInstanceOf(CarSetupWizardCompatLayout.class);
+    }
+
+    /**
+     * Test that the BaseCompatActivity's content view is set to be a CarSetupWizardLayout
+     */
+    @Test
+    public void testContentViewIsCarSetupWizardLayout() {
+        View contentView = mBaseCompatActivity.findViewById(R.id.car_setup_wizard_layout);
+        assertThat(contentView).isNotNull();
+        assertThat(contentView instanceof CarSetupWizardCompatLayout).isTrue();
+    }
+
+    private BaseCompatActivity createSpyBaseCompatActivity() {
+        BaseCompatActivity spyBaseCompatActivity = Mockito.spy(
+                (BaseCompatActivity) Robolectric.buildActivity(BaseCompatActivity.class).get());
+        spyBaseCompatActivity.onCreate(null);
+
+        return spyBaseCompatActivity;
+    }
+
+    /**
+     * Test that the BaseCompatActivity sets the back button listener to call
+     * {@link BaseCompatActivity#handleBackButton()} when created.
+     */
+    @Test
+    public void testBackButtonListenerIsDefault() {
+        BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity();
+
+        ImageView backButton = (ImageView) spyBaseCompatActivity.findViewById(
+                R.id.back_button);
+        backButton.performClick();
+
+        verify(spyBaseCompatActivity).handleBackButton();
+    }
+
+    /**
+     * Test that the BaseCompatActivity sets the secondary toolbar button listener to the default
+     * when created.
+     */
+    @Test
+    public void testSecondaryToolbarButtonListenerIsDefault() {
+        BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity();
+
+        Button secondaryToolBarButton = (Button) spyBaseCompatActivity.findViewById(
+                R.id.secondary_toolbar_button);
+        secondaryToolBarButton.performClick();
+
+        verify(spyBaseCompatActivity).nextAction(Activity.RESULT_OK);
+    }
+
+    /**
+     * Test that the BaseCompatActivity sets the primary toolbar button listener to the default when
+     * created.
+     */
+    @Test
+    public void testPrimaryToolbarButtonListenerIsDefault() {
+        BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity();
+
+        Button primaryToolBarButton = (Button) spyBaseCompatActivity.findViewById(
+                R.id.primary_toolbar_button);
+        primaryToolBarButton.performClick();
+
+        verify(spyBaseCompatActivity).nextAction(Activity.RESULT_OK);
+    }
+
+    private BaseCompatActivity getStartedBaseCompatActivity() {
+        return (BaseCompatActivity)
+                Robolectric.buildActivity(BaseCompatActivity.class).create().start().get();
+    }
+
+    private BaseCompatActivity getSavedInstanceStateBaseCompatActivity() {
+        return (BaseCompatActivity) Robolectric.buildActivity(
+                BaseCompatActivity.class).create().saveInstanceState(new Bundle()).get();
+    }
+
+    /**
+     * Test that fragment commits are allowed after {@link BaseCompatActivity#onStart()} is called.
+     */
+    @Test
+    public void testFragmentCommitsAllowedAfterOnStart() {
+        assertThat(getStartedBaseCompatActivity().getAllowFragmentCommits()).isTrue();
+    }
+
+    /**
+     * Test that fragment commits are not allowed after {@link
+     * BaseCompatActivity#onSaveInstanceState} is called.
+     */
+    @Test
+    public void testFragmentCommitsNotAllowedAfterOnSavedInstanceState() {
+        assertThat(getSavedInstanceStateBaseCompatActivity().getAllowFragmentCommits()).isFalse();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setContentFragment} sets the content fragment and calls
+     * the expected methods when fragment commits are allowed.
+     */
+    @Test
+    public void testSetContentFragmentWhenFragmentCommitsAllowed() {
+        BaseCompatActivity spyBaseCompatActivity = Mockito.spy(getStartedBaseCompatActivity());
+
+        Fragment fragment = new Fragment();
+        spyBaseCompatActivity.setContentFragment(fragment);
+
+        assertThat(spyBaseCompatActivity.getContentFragment()).isEqualTo(fragment);
+        assertThat(spyBaseCompatActivity.getSupportFragmentManager().getBackStackEntryCount())
+                .isEqualTo(0);
+        // Verify that onContentFragmentSet is called with the test fragment
+        verify(spyBaseCompatActivity).onContentFragmentSet(fragment);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setContentFragment} does nothing when fragment commits
+     * are not allowed.
+     */
+    @Test
+    public void testSetContentFragmentWhenFragmentCommitsNotAllowed() {
+        BaseCompatActivity spyBaseCompatActivity =
+                Mockito.spy(getSavedInstanceStateBaseCompatActivity());
+
+        Fragment fragment = new Fragment();
+        spyBaseCompatActivity.setContentFragment(fragment);
+
+        assertThat(spyBaseCompatActivity.getContentFragment()).isEqualTo(null);
+        assertThat(spyBaseCompatActivity.getSupportFragmentManager().getBackStackEntryCount())
+                .isEqualTo(0);
+        // Verify that onContentFragmentSet is not called
+        verify(spyBaseCompatActivity, times(0)).onContentFragmentSet(fragment);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setContentFragmentWithBackstack)} sets the content
+     * fragment, adds it to the fragment backstack, and calls the expected methods when fragment
+     * commits are allowed.
+     */
+    @Test
+    public void testSetContentFragmentWithBackstackWhenFragmentCommitsAllowed() {
+        BaseCompatActivity spyBaseCompatActivity = Mockito.spy(getStartedBaseCompatActivity());
+
+        Fragment fragment = new Fragment();
+        spyBaseCompatActivity.setContentFragmentWithBackstack(fragment);
+
+        assertThat(spyBaseCompatActivity.getContentFragment()).isEqualTo(fragment);
+        assertThat(spyBaseCompatActivity.getSupportFragmentManager().getBackStackEntryCount())
+                .isEqualTo(1);
+        // Verify that onContentFragmentSet is called with the test fragment
+        verify(spyBaseCompatActivity).onContentFragmentSet(fragment);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setContentFragmentWithBackstack)} does nothing when
+     * fragment commits are not allowed.
+     */
+    @Test
+    public void testSetContentFragmentWithBackstackWhenFragmentCommitsNotAllowed() {
+        BaseCompatActivity spyBaseCompatActivity =
+                Mockito.spy(getSavedInstanceStateBaseCompatActivity());
+
+        Fragment fragment = new Fragment();
+        spyBaseCompatActivity.setContentFragment(fragment);
+
+        assertThat(spyBaseCompatActivity.getContentFragment()).isEqualTo(null);
+        assertThat(spyBaseCompatActivity.getSupportFragmentManager().getBackStackEntryCount())
+                .isEqualTo(0);
+        // Verify that onContentFragmentSet is not called
+        verify(spyBaseCompatActivity, times(0)).onContentFragmentSet(fragment);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#popBackStackImmediate()} returns false when no fragment
+     * is added to the backstack.
+     */
+    @Test
+    public void testPopBackStackImmediateWithEmptyStack() {
+        assertThat(mBaseCompatActivity.popBackStackImmediate()).isEqualTo(false);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#popBackStackImmediate()} returns true when a fragment is
+     * added to the backstack and that the fragment is popped off of the backstack.
+     */
+    @Test
+    public void testPopBackStackImmediateWithFragmentInStack() {
+        Fragment fragment = new Fragment();
+        mBaseCompatActivity.setContentFragmentWithBackstack(fragment);
+        assertThat(mBaseCompatActivity.popBackStackImmediate()).isEqualTo(true);
+
+        assertThat(mBaseCompatActivity.getContentFragment()).isNull();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#getContentFragment()} returns the content fragment.
+     */
+    @Test
+    public void testGetContentFragment() {
+        Fragment fragment = new Fragment();
+        mBaseCompatActivity.setContentFragment(fragment);
+
+        assertThat(mBaseCompatActivity.getContentFragment()).isEqualTo(
+                mBaseCompatActivity.getSupportFragmentManager().findFragmentByTag(
+                        mBaseCompatActivity.CONTENT_FRAGMENT_TAG));
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setContentLayout} adds the specified layout to the
+     * BaseCompatActivity.
+     */
+    @Test
+    public void testSetContentLayout() {
+        mBaseCompatActivity.setContentLayout(R.layout.base_activity_test_layout);
+        View contentLayout = mBaseCompatActivity.findViewById(R.id.content_layout);
+        assertThat(contentLayout).isNotNull();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#finishAction()} results in a call to
+     * {@link BaseCompatActivity#finish}.
+     */
+    @Test
+    public void testFinishAction() {
+        BaseCompatActivity spyBaseCompatActivity = Mockito.spy(mBaseCompatActivity);
+        spyBaseCompatActivity.finishAction();
+
+        verify(spyBaseCompatActivity).finish();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#finishAction(int)} )} results in a call to
+     * {@link BaseCompatActivity#nextAction} and {@link BaseCompatActivity#finish}.
+     */
+    @Test
+    public void testFinishActionWithResultCode() {
+        BaseCompatActivity spyBaseCompatActivity = Mockito.spy(mBaseCompatActivity);
+        spyBaseCompatActivity.finishAction(BaseCompatActivity.RESULT_OK);
+
+        verify(spyBaseCompatActivity).nextAction(BaseCompatActivity.RESULT_OK, null);
+        verify(spyBaseCompatActivity).finish();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setBackButtonVisible} sets the back button visible/not
+     * visible.
+     */
+    @Test
+    public void testSetBackButtonVisibleTrue() {
+        mBaseCompatActivity.setBackButtonVisible(true);
+        TestHelper.assertViewVisible(mCarSetupWizardLayout.getBackButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setBackButtonVisible} sets the back button visible/not
+     * visible.
+     */
+    @Test
+    public void testSetBackButtonVisibleFalse() {
+        mBaseCompatActivity.setBackButtonVisible(false);
+        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getBackButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setToolbarTitleVisible} sets the toolbar title
+     * visible/not visible.
+     */
+    @Test
+    public void testSetToolbarTitleVisibleTrue() {
+        mBaseCompatActivity.setToolbarTitleVisible(true);
+        TestHelper.assertViewVisible(mCarSetupWizardLayout.getToolbarTitle());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setToolbarTitleVisible} sets the toolbar button
+     * visible/not visible.
+     */
+    @Test
+    public void testSetToolbarTitleVisibleFalse() {
+        mBaseCompatActivity.setToolbarTitleVisible(false);
+        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getToolbarTitle());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setToolbarTitleText(String)} sets the toolbar title text.
+     */
+    @Test
+    public void testSetToolbarTitleText() {
+        mBaseCompatActivity.setToolbarTitleText("title text");
+        TestHelper.assertTextEqual(mCarSetupWizardLayout.getToolbarTitle(), "title text");
+    }
+
+    /**
+     * Test that a call to setToolbarTitleStyle sets the text appearance on the toolbar title.
+     */
+    @Test
+    public void testSetToolbarStyle() {
+        @StyleRes int newStyle = R.style.TextAppearance_Car_Body2;
+        mCarSetupWizardLayout.setToolbarTitleStyle(newStyle);
+        ShadowTextView shadowTextView = Shadows.shadowOf(mCarSetupWizardLayout.getToolbarTitle());
+        assertThat(shadowTextView.getTextAppearanceId()).isEqualTo(newStyle);
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonVisible} sets the primary toolbar
+     * button visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonVisibleTrue() {
+        mBaseCompatActivity.setPrimaryToolbarButtonVisible(true);
+        TestHelper.assertViewVisible(mCarSetupWizardLayout.getPrimaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonVisible} sets the primary toolbar
+     * button visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonVisibleFalse() {
+        mBaseCompatActivity.setPrimaryToolbarButtonVisible(false);
+        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getPrimaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonEnabled} sets the primary toolbar
+     * button visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonEnabledTrue() {
+        mBaseCompatActivity.setPrimaryToolbarButtonEnabled(true);
+        TestHelper.assertViewEnabled(mCarSetupWizardLayout.getPrimaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonEnabled} sets the primary toolbar
+     * button visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonEnabledFalse() {
+        mBaseCompatActivity.setPrimaryToolbarButtonEnabled(false);
+        TestHelper.assertViewNotEnabled(mCarSetupWizardLayout.getPrimaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonText(String)} sets the primary
+     * toolbar title text.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonText() {
+        mBaseCompatActivity.setPrimaryToolbarButtonText("button text");
+        TestHelper.assertTextEqual(mCarSetupWizardLayout.getPrimaryToolbarButton(), "button text");
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonFlat(boolean)} sets the primary
+     * toolbar button flat/not flat.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonFlatTrue() {
+        mBaseCompatActivity.setPrimaryToolbarButtonFlat(true);
+        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButtonFlat()).isTrue();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonFlat(boolean)} sets the primary
+     * toolbar button flat/not flat.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonFlatFalse() {
+        mBaseCompatActivity.setPrimaryToolbarButtonFlat(false);
+        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButtonFlat()).isFalse();
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setPrimaryToolbarButtonOnClickListener} sets the primary
+     * toolbar button's click listener.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonOnClickListener() {
+        View.OnClickListener spyListener = TestHelper.createSpyListener();
+
+        mBaseCompatActivity.setPrimaryToolbarButtonOnClickListener(spyListener);
+        mBaseCompatActivity.getCarSetupWizardLayout().getPrimaryToolbarButton().performClick();
+        verify(spyListener).onClick(Mockito.any());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonVisible} sets the secondary
+     * toolbar button visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonVisibleTrue() {
+        mBaseCompatActivity.setSecondaryToolbarButtonVisible(true);
+        TestHelper.assertViewVisible(mCarSetupWizardLayout.getSecondaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonVisible} sets the secondary
+     * toolbar button visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonVisibleFalse() {
+        mBaseCompatActivity.setSecondaryToolbarButtonVisible(false);
+        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getSecondaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonEnabled} sets the secondary
+     * toolbar button visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonEnabledTrue() {
+        mBaseCompatActivity.setSecondaryToolbarButtonEnabled(true);
+        TestHelper.assertViewEnabled(mCarSetupWizardLayout.getSecondaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonEnabled} sets the secondary
+     * toolbar button visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonEnabledFalse() {
+        mBaseCompatActivity.setSecondaryToolbarButtonEnabled(false);
+        TestHelper.assertViewNotEnabled(mCarSetupWizardLayout.getSecondaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonText(String)} sets the secondary
+     * toolbar title text.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonText() {
+        mBaseCompatActivity.setSecondaryToolbarButtonText("button text");
+        TestHelper.assertTextEqual(mCarSetupWizardLayout.getSecondaryToolbarButton(),
+                "button text");
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setSecondaryToolbarButtonOnClickListener} sets the
+     * secondary toolbar button's click listener.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonOnClickListener() {
+        View.OnClickListener spyListener = TestHelper.createSpyListener();
+
+        mBaseCompatActivity.setSecondaryToolbarButtonOnClickListener(spyListener);
+        mBaseCompatActivity.getCarSetupWizardLayout().getSecondaryToolbarButton().performClick();
+        verify(spyListener).onClick(Mockito.any());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setProgressBarVisible} sets the progressbar visible/not
+     * visible.
+     */
+    @Test
+    public void testSetProgressBarVisibleTrue() {
+        mBaseCompatActivity.setProgressBarVisible(true);
+        TestHelper.assertViewVisible(mCarSetupWizardLayout.getProgressBar());
+    }
+
+    /**
+     * Test that {@link BaseCompatActivity#setProgressBarVisible} sets the progressbar visible/not
+     * visible.
+     */
+    @Test
+    public void testSetProgressBarVisibleFalse() {
+        mBaseCompatActivity.setProgressBarVisible(false);
+        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getProgressBar());
+    }
+
+    @Test
+    public void testBaseCompatActivityOnStart_startsDrivingMonitor() {
+        mActivityController.start();
+        assertThat(ShadowCar.hasConnected()).isTrue();
+    }
+
+    @Test
+    public void testBaseCompatActivityOnStop_stopsDrivingMonitor() {
+        mActivityController.start().stop();
+        assertThat(ShadowCar.hasDisconnected()).isFalse();
+    }
+
+    @Test
+    public void testNextActionTwice_onlyTriggersOneStartActivity() {
+        BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity();
+        spyBaseCompatActivity.nextAction(Activity.RESULT_OK);
+        spyBaseCompatActivity.nextAction(Activity.RESULT_OK);
+        verify(spyBaseCompatActivity, times(1)).startActivity(Mockito.any());
+    }
+
+    @Test
+    public void testNextActionCanBeTriggeredAgain_onResume() {
+        BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity();
+        spyBaseCompatActivity.onResume();
+        spyBaseCompatActivity.nextAction(Activity.RESULT_OK);
+        spyBaseCompatActivity.onPause();
+        spyBaseCompatActivity.onResume();
+        spyBaseCompatActivity.nextAction(Activity.RESULT_OK);
+        verify(spyBaseCompatActivity, times(2)).startActivity(Mockito.any());
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseDesignActivityTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseDesignActivityTest.java
new file mode 100644
index 0000000..23066a8
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseDesignActivityTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
+import com.android.car.setupwizardlib.shadows.ShadowCar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/**
+ * Unit tests for the {@link BaseDesignActivity}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowCar.class)
+public class BaseDesignActivityTest extends BaseRobolectricTest {
+
+    private BaseDesignActivity mDesignActivity;
+
+    @Before
+    public void setupLayout() {
+        mDesignActivity = Robolectric
+                .buildActivity(BaseDesignActivity.class)
+                .create()
+                .get();
+    }
+
+    @Test
+    public void testLayoutIsDesign() {
+        assertThat(mDesignActivity.getLayout()).isEqualTo(R.layout.base_design_activity);
+    }
+
+    @Test
+    public void testGetCarSetupWizardLayout() {
+        assertThat(mDesignActivity.getCarSetupWizardLayout())
+                .isInstanceOf(CarSetupWizardDesignLayout.class);
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java
new file mode 100644
index 0000000..dddb660
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java
@@ -0,0 +1,522 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.app.Activity;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.StyleRes;
+
+import com.android.car.setupwizardlib.partner.FakeOverrideContentProvider;
+import com.android.car.setupwizardlib.partner.PartnerConfig;
+import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
+import com.android.car.setupwizardlib.robolectric.TestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowTextView;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Locale;
+
+/**
+ * Tests for the CarSetupWizardCompatLayout
+ */
+@RunWith(RobolectricTestRunner.class)
+public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest {
+    private static final Locale LOCALE_EN_US = new Locale("en", "US");
+    // Hebrew locale can be used to test RTL.
+    private static final Locale LOCALE_IW_IL = new Locale("iw", "IL");
+
+    private CarSetupWizardCompatLayout mCarSetupWizardCompatLayout;
+
+    @Before
+    public void setUp() {
+        mCarSetupWizardCompatLayout = createCarSetupWizardCompatLayout();
+
+        // Have to make this call first to ensure secondaryToolbar button is created from stub.
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(true);
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(false);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setBackButtonListener} does set the back button
+     * listener.
+     */
+    @Test
+    public void testSetBackButtonListener() {
+        View.OnClickListener spyListener = TestHelper.createSpyListener();
+
+        mCarSetupWizardCompatLayout.setBackButtonListener(spyListener);
+        mCarSetupWizardCompatLayout.getBackButton().performClick();
+        Mockito.verify(spyListener).onClick(mCarSetupWizardCompatLayout.getBackButton());
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setBackButtonVisible} does set the view
+     * visible/not visible and calls updateBackButtonTouchDelegate.
+     */
+    @Test
+    public void testSetBackButtonVisibleTrue() {
+        CarSetupWizardCompatLayout spyCarSetupWizardCompatLayout =
+                Mockito.spy(mCarSetupWizardCompatLayout);
+
+        spyCarSetupWizardCompatLayout.setBackButtonVisible(true);
+        View backButton = spyCarSetupWizardCompatLayout.getBackButton();
+        TestHelper.assertViewVisible(backButton);
+        Mockito.verify(spyCarSetupWizardCompatLayout).updateBackButtonTouchDelegate(true);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setBackButtonVisible} does set the view
+     * visible/not visible and calls updateBackButtonTouchDelegate.
+     */
+    @Test
+    public void testSetBackButtonVisibleFalse() {
+        CarSetupWizardCompatLayout spyCarSetupWizardCompatLayout =
+                Mockito.spy(mCarSetupWizardCompatLayout);
+
+        spyCarSetupWizardCompatLayout.setBackButtonVisible(false);
+        View backButton = spyCarSetupWizardCompatLayout.getBackButton();
+        TestHelper.assertViewNotVisible(backButton);
+        Mockito.verify(spyCarSetupWizardCompatLayout).updateBackButtonTouchDelegate(false);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setToolbarTitleVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetToolbarTitleVisibleTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getToolbarTitle();
+
+        mCarSetupWizardCompatLayout.setToolbarTitleVisible(true);
+        TestHelper.assertViewVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setToolbarTitleVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetToolbarTitleVisibleFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getToolbarTitle();
+
+        mCarSetupWizardCompatLayout.setToolbarTitleVisible(false);
+        TestHelper.assertViewNotVisible(toolbarTitle);
+    }
+
+    /**
+     * Tests that {@link CarSetupWizardCompatLayout#setToolbarTitleText(String)} does set the
+     * toolbar title text.
+     */
+    @Test
+    public void testSetToolbarTitleText() {
+        mCarSetupWizardCompatLayout.setToolbarTitleText("test title");
+        TestHelper.assertTextEqual(mCarSetupWizardCompatLayout.getToolbarTitle(), "test title");
+    }
+
+    /**
+     * Test that a call to setToolbarTitleStyle sets the text appearance on the toolbar title.
+     */
+    @Test
+    public void testSetToolbarStyle() {
+        @StyleRes int newStyle = R.style.TextAppearance_Car_Body2;
+        mCarSetupWizardCompatLayout.setToolbarTitleStyle(newStyle);
+        ShadowTextView shadowTextView =
+                Shadows.shadowOf(mCarSetupWizardCompatLayout.getToolbarTitle());
+        assertThat(shadowTextView.getTextAppearanceId()).isEqualTo(newStyle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonVisibleTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonVisible(true);
+        TestHelper.assertViewVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonVisibleFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonVisible(false);
+        TestHelper.assertViewNotVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonEnabled} does set the view
+     * enabled/not enabled.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonEnabledTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonEnabled(true);
+        TestHelper.assertViewEnabled(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonEnabled} does set the view
+     * enabled/not enabled.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonEnabledFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonEnabled(false);
+        TestHelper.assertViewNotEnabled(toolbarTitle);
+    }
+
+    /**
+     * Tests that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonText(String)} does set
+     * the primary toolbar button text.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonText() {
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonText("test title");
+        TestHelper.assertTextEqual(
+                mCarSetupWizardCompatLayout.getPrimaryToolbarButton(), "test title");
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonListener} does set the
+     * primary toolbar button listener.
+     */
+    @Test
+    public void testSetPrimaryToolbarButtonListener() {
+        View.OnClickListener spyListener = TestHelper.createSpyListener();
+
+        mCarSetupWizardCompatLayout.setPrimaryToolbarButtonListener(spyListener);
+        mCarSetupWizardCompatLayout.getPrimaryToolbarButton().performClick();
+        Mockito.verify(spyListener).onClick(mCarSetupWizardCompatLayout.getPrimaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#createPrimaryToolbarButton} creates a new button
+     * but holds over the correct attributes.
+     */
+    @Test
+    public void testCreatePrimaryButtonTrue() {
+        Button currPrimaryToolbarButton = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+        Button primaryToolbarButton = mCarSetupWizardCompatLayout.createPrimaryToolbarButton(true);
+
+        assertThat(primaryToolbarButton.getVisibility()).isEqualTo(
+                currPrimaryToolbarButton.getVisibility());
+        assertThat(primaryToolbarButton.isEnabled()).isEqualTo(
+                currPrimaryToolbarButton.isEnabled());
+        assertThat(primaryToolbarButton.getText()).isEqualTo(currPrimaryToolbarButton.getText());
+        assertThat(primaryToolbarButton.getLayoutParams()).isEqualTo(
+                currPrimaryToolbarButton.getLayoutParams());
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonVisible} does set the
+     * view visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonVisibleTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(true);
+        TestHelper.assertViewVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonVisible} does set the
+     * view visible/not visible.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonVisibleFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(false);
+        TestHelper.assertViewNotVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonEnabled} does set the
+     * view enabled/not enabled.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonEnabledTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonEnabled(true);
+        TestHelper.assertViewEnabled(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonEnabled} does set the
+     * view enabled/not enabled.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonEnabledFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton();
+
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonEnabled(false);
+        TestHelper.assertViewNotEnabled(toolbarTitle);
+    }
+
+    /**
+     * Tests that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonText(String)} does set
+     * the secondary toolbar button text.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonText() {
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonText("test title");
+        TestHelper.assertTextEqual(
+                mCarSetupWizardCompatLayout.getSecondaryToolbarButton(), "test title");
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonListener} does set the
+     * secondary toolbar button listener.
+     */
+    @Test
+    public void testSetSecondaryToolbarButtonListener() {
+        View.OnClickListener spyListener = TestHelper.createSpyListener();
+
+        mCarSetupWizardCompatLayout.setSecondaryToolbarButtonListener(spyListener);
+        mCarSetupWizardCompatLayout.getSecondaryToolbarButton().performClick();
+        Mockito.verify(spyListener)
+                .onClick(mCarSetupWizardCompatLayout.getSecondaryToolbarButton());
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setProgressBarVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetProgressBarVisibleTrue() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getProgressBar();
+
+        mCarSetupWizardCompatLayout.setProgressBarVisible(true);
+        TestHelper.assertViewVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setProgressBarVisible} does set the view
+     * visible/not visible.
+     */
+    @Test
+    public void testSetProgressBarVisibleFalse() {
+        View toolbarTitle = mCarSetupWizardCompatLayout.getProgressBar();
+
+        mCarSetupWizardCompatLayout.setProgressBarVisible(false);
+        TestHelper.assertViewNotVisible(toolbarTitle);
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setProgressBarIndeterminate(boolean)}
+     * does set the progress bar intermediate/not indeterminate.
+     */
+    @Test
+    public void testSetProgressBarIndeterminateTrue() {
+        mCarSetupWizardCompatLayout.setProgressBarIndeterminate(true);
+        assertThat(mCarSetupWizardCompatLayout.getProgressBar().isIndeterminate()).isTrue();
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setProgressBarIndeterminate(boolean)}
+     * does set the progress bar intermediate/not indeterminate.
+     */
+    @Test
+    public void testSetProgressBarIndeterminateFalse() {
+        mCarSetupWizardCompatLayout.setProgressBarIndeterminate(false);
+        assertThat(mCarSetupWizardCompatLayout.getProgressBar().isIndeterminate()).isFalse();
+    }
+
+    /**
+     * Test that {@link CarSetupWizardCompatLayout#setProgressBarProgress} does set the progress.
+     */
+    @Test
+    public void testSetProgressBarProgress() {
+        mCarSetupWizardCompatLayout.setProgressBarProgress(80);
+        assertThat(mCarSetupWizardCompatLayout.getProgressBar().getProgress()).isEqualTo(80);
+    }
+
+    @Test
+    public void testApplyUpdatedLocale() {
+        mCarSetupWizardCompatLayout.applyLocale(LOCALE_IW_IL);
+        TextView toolbarTitle = mCarSetupWizardCompatLayout.getToolbarTitle();
+        Button primaryToolbarButton = mCarSetupWizardCompatLayout.getPrimaryToolbarButton();
+        Button secondaryToolbarButton = mCarSetupWizardCompatLayout.getSecondaryToolbarButton();
+
+        assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_IW_IL);
+        assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL);
+        assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL);
+
+        mCarSetupWizardCompatLayout.applyLocale(LOCALE_EN_US);
+        assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_EN_US);
+        assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US);
+        assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US);
+    }
+
+    @Test
+    public void testGetBackButton() {
+        assertThat(mCarSetupWizardCompatLayout.getPrimaryToolbarButton()).isEqualTo(
+                mCarSetupWizardCompatLayout.findViewById(R.id.primary_toolbar_button));
+    }
+
+    @Test
+    public void testGetToolBarTitle() {
+        assertThat(mCarSetupWizardCompatLayout.getToolbarTitle()).isEqualTo(
+                mCarSetupWizardCompatLayout.findViewById(R.id.toolbar_title));
+    }
+
+    @Test
+    public void testGetPrimaryToolBarButton() {
+        assertThat(mCarSetupWizardCompatLayout.getPrimaryToolbarButton()).isEqualTo(
+                mCarSetupWizardCompatLayout.findViewById(R.id.primary_toolbar_button));
+    }
+
+    @Test
+    public void testGetSecondaryToolBarButton() {
+        assertThat(mCarSetupWizardCompatLayout.getSecondaryToolbarButton()).isEqualTo(
+                mCarSetupWizardCompatLayout.findViewById(R.id.secondary_toolbar_button));
+    }
+
+    @Test
+    public void testGetProgressBar() {
+        assertThat(mCarSetupWizardCompatLayout.getProgressBar()).isEqualTo(
+                mCarSetupWizardCompatLayout.findViewById(R.id.progress_bar));
+    }
+
+    @Test
+    public void testTitleBarElevationChange() {
+        mCarSetupWizardCompatLayout.addElevationToTitleBar(/*animate= */ false);
+        View titleBar = mCarSetupWizardCompatLayout.findViewById(R.id.application_bar);
+        assertThat(titleBar.getElevation()).isEqualTo(
+                application.getResources().getDimension(
+                        R.dimen.title_bar_drop_shadow_elevation));
+
+        mCarSetupWizardCompatLayout.removeElevationFromTitleBar(/*animate= */ false);
+        assertThat(titleBar.getElevation()).isEqualTo(0f);
+    }
+
+    @Test
+    public void testPartnerResourcesAreApplied() {
+        setupFakeContentProvider();
+
+        CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout();
+
+        // Verify primary button background
+        Button primary = layout.getPrimaryToolbarButton();
+        Drawable expected = application.getResources().getDrawable(R.drawable.button_ripple_bg);
+        assertThat(getDrawbleDefaultColor(primary.getBackground()))
+                .isEqualTo(getDrawbleDefaultColor(expected));
+
+        // Verify primary button text size
+        assertThat(primary.getTextSize())
+                .isEqualTo(FakeOverrideContentProvider.DEFAULT_DIMENSION);
+
+        // Verify paddings
+        assertThat(primary.getPaddingStart())
+                .isEqualTo(FakeOverrideContentProvider.DEFAULT_H_PADDING);
+
+        assertThat(primary.getPaddingEnd())
+                .isEqualTo(FakeOverrideContentProvider.DEFAULT_H_PADDING);
+
+        assertThat(primary.getPaddingTop())
+                .isEqualTo(FakeOverrideContentProvider.DEFAULT_V_PADDING);
+
+        assertThat(primary.getPaddingBottom())
+                .isEqualTo(FakeOverrideContentProvider.DEFAULT_V_PADDING);
+    }
+
+    @Test
+    public void testShouldNotApplyLayoutBackground() {
+        setupFakeContentProvider();
+        CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout();
+
+        ColorDrawable bg = (ColorDrawable) layout.getBackground();
+        assertThat(bg).isNull();
+    }
+
+    @Test
+    public void testSetButtonTextColor() {
+        setupFakeContentProvider();
+        CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout();
+        Button primary = layout.getPrimaryToolbarButton();
+
+        layout.setButtonTextColor(
+                primary, PartnerConfig.CONFIG_LAYOUT_BG_COLOR);
+
+        assertThat(primary.getCurrentTextColor())
+                .isEqualTo(FakeOverrideContentProvider.ANDROID_COLOR_DARK_GRAY);
+    }
+
+    @Test
+    public void testSetBackground() {
+        setupFakeContentProvider();
+        CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout();
+        layout.setSecondaryToolbarButtonVisible(true);
+        Button secondary = layout.getSecondaryToolbarButton();
+
+        layout.setBackground(
+                secondary,
+                PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG,
+                PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR);
+
+        Drawable expected = application.getResources().getDrawable(R.drawable.button_ripple_bg);
+        assertThat(getDrawbleDefaultColor(secondary.getBackground()))
+                .isEqualTo(getDrawbleDefaultColor(expected));
+    }
+
+    private void setupFakeContentProvider() {
+        FakeOverrideContentProvider.installDefaultProvider();
+    }
+
+    private CarSetupWizardCompatLayout createCarSetupWizardCompatLayout() {
+        Activity activity = Robolectric
+                .buildActivity(CarSetupWizardLayoutTestActivity.class)
+                .create()
+                .get();
+
+        return activity.findViewById(R.id.car_setup_wizard_layout);
+    }
+
+    private @ColorRes int getDrawbleDefaultColor(Drawable drawable) {
+        Drawable.ConstantState state = drawable.getConstantState();
+        ColorStateList colorStateList = ReflectionHelpers.getField(state, "mColor");
+        return colorStateList.getDefaultColor();
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayoutTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayoutTest.java
new file mode 100644
index 0000000..a57be9a
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayoutTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
+
+import com.android.car.setupwizardlib.partner.FakeOverrideContentProvider;
+import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Tests for the CarSetupWizardDesignLayout
+ */
+@RunWith(RobolectricTestRunner.class)
+public class CarSetupWizardDesignLayoutTest extends BaseRobolectricTest {
+
+    private CarSetupWizardDesignLayout mCarSetupWizardLayout;
+
+    @Before
+    public void setupLayout() {
+        FakeOverrideContentProvider.installDefaultProvider();
+        Activity activity = Robolectric
+                .buildActivity(BaseDesignActivity.class)
+                .create()
+                .get();
+        mCarSetupWizardLayout = activity.findViewById(R.id.car_setup_wizard_layout);
+    }
+
+    @Test
+    public void testShouldApplyLayoutBackground() {
+        ColorDrawable bg = (ColorDrawable) mCarSetupWizardLayout.getBackground();
+        assertThat(bg.getColor()).isEqualTo(FakeOverrideContentProvider.ANDROID_COLOR_DARK_GRAY);
+    }
+}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTestActivity.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTestActivity.java
similarity index 100%
rename from library/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTestActivity.java
rename to library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTestActivity.java
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java
new file mode 100644
index 0000000..16a6d2a
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ExternalResources.java
@@ -0,0 +1,246 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import androidx.annotation.AnyRes;
+import androidx.annotation.ArrayRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.robolectric.res.ResName;
+import org.robolectric.res.ResType;
+import org.robolectric.res.TypedResource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class to inject resources for an "external" application in Robolectric tests. This can be
+ * used with {@link org.robolectric.shadows.ShadowPackageManager#resources} to simulate loading
+ * resources from another package.
+ */
+class ExternalResources {
+
+    public static Resources injectExternalResources(String packageName) {
+        return injectExternalResources(createPackageInfo(packageName));
+    }
+
+    public static Resources injectExternalResources(PackageInfo packageInfo) {
+        try {
+            application.getPackageManager().getPackageInfo(packageInfo.packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Add the package if it does not exist
+            shadowOf(application.getPackageManager()).addPackage(packageInfo);
+        }
+        Resources resources = Resources.forPackageName(packageInfo.packageName);
+        shadowOf(application.getPackageManager()).resources.put(packageInfo.packageName, resources);
+        return resources;
+    }
+
+    /**
+     * Constructed resources for testing, representing resources external to the current package
+     * under test.
+     */
+    public static class Resources extends android.content.res.Resources {
+
+        private final String mPackageName;
+        private final Map<Integer, TypedResource<?>> mOverrideResources = new HashMap<>();
+        private final Map<ResName, Integer> mResourceIds = new HashMap<>();
+        private int mNextId = 1;
+
+        public static Resources forPackageName(String packageName) {
+            android.content.res.Resources res = application.getResources();
+            return new Resources(
+                    packageName, res.getAssets(), res.getDisplayMetrics(), res.getConfiguration());
+        }
+
+        @Override
+        public String getResourcePackageName(@AnyRes int resId) {
+            TypedResource<?> typedResource = mOverrideResources.get(resId);
+            if (typedResource != null) {
+                return mPackageName;
+            }
+            throw new NotFoundException();
+        }
+
+        @Override
+        public int getIdentifier(String name, String defType, String defPackage) {
+            Integer resourceId =
+                    mResourceIds.get(ResName.qualifyResName(name, defPackage, defType));
+            if (resourceId == null) {
+                return 0;
+            }
+            return resourceId;
+        }
+
+        @Override
+        public void getValue(int id, TypedValue outValue, boolean resolveRefs) {
+            TypedResource<?> typedResource = mOverrideResources.get(id);
+            if (typedResource != null) {
+                Object override = get(id, typedResource.getResType());
+                if (override instanceof TypedValue) {
+                    outValue.setTo((TypedValue) override);
+                } else if (override instanceof Integer) {
+                    outValue.data = (int) override;
+                }
+                return;
+            }
+            throw new NotFoundException();
+        }
+
+        public void putValue(String name, int value, String defaultType, ResType resType) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, defaultType),
+                    new TypedResource<>(value, resType, null));
+        }
+
+        public void putValue(String name, TypedValue value, String defaultType, ResType resType) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, defaultType),
+                    new TypedResource<>(value, resType, null));
+        }
+
+        @Override
+        public int getInteger(int id) {
+            return (int) get(id, ResType.INTEGER);
+        }
+
+        public void putInteger(String name, int value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "integer"),
+                    new TypedResource<>(value, ResType.INTEGER, null));
+        }
+
+        @Override
+        public int getColor(int id) {
+            return (int) get(id, ResType.COLOR);
+        }
+
+        @Override
+        public int getColor(int id, @Nullable Theme theme) {
+            return (int) get(id, ResType.COLOR);
+        }
+
+        public void putColor(String name, int value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "color"),
+                    new TypedResource<>(value, ResType.COLOR, null));
+        }
+
+        @Override
+        public Drawable getDrawable(int id) {
+            return (Drawable) get(id, ResType.DRAWABLE);
+        }
+
+        @Override
+        public Drawable getDrawable(int id, @Nullable Theme theme) {
+            return (Drawable) get(id, ResType.DRAWABLE);
+        }
+
+        public void putDrawable(String name, Drawable value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "drawable"),
+                    new TypedResource<>(value, ResType.DRAWABLE, null));
+        }
+
+        public void putBoolean(String name, boolean value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "bool"),
+                    new TypedResource<>(value, ResType.BOOLEAN, null));
+        }
+
+        @Override
+        public float getDimension(int id) {
+            return (float) get(id, ResType.DIMEN);
+        }
+
+        public void putDimension(String name, float value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "dimen"),
+                    new TypedResource<>(value, ResType.DIMEN, null));
+        }
+
+        @NonNull
+        @Override
+        public CharSequence getText(int id) {
+            return (CharSequence) get(id, ResType.CHAR_SEQUENCE);
+        }
+
+        @NonNull
+        @Override
+        public String getString(int id) {
+            return get(id, ResType.CHAR_SEQUENCE).toString();
+        }
+
+        public void putText(String name, CharSequence value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "string"),
+                    new TypedResource<>(value, ResType.CHAR_SEQUENCE, null));
+        }
+
+        @NonNull
+        @Override
+        public String[] getStringArray(@ArrayRes int id) {
+            return (String[]) get(id, ResType.CHAR_SEQUENCE_ARRAY);
+        }
+
+        public void putStringArray(String name, String[] value) {
+            put(
+                    ResName.qualifyResName(name, mPackageName, "string-array"),
+                    new TypedResource<>(value, ResType.CHAR_SEQUENCE_ARRAY, null));
+        }
+
+        private Resources(String packageName, AssetManager assets, DisplayMetrics metrics,
+                Configuration config) {
+            super(assets, metrics, config);
+            this.mPackageName = packageName;
+        }
+
+        private <T> void put(ResName resName, TypedResource<T> value) {
+            int id = mNextId++;
+            mOverrideResources.put(id, value);
+            mResourceIds.put(resName, id);
+        }
+
+        private Object get(@AnyRes int id, ResType type) {
+            TypedResource<?> override = mOverrideResources.get(id);
+            if (override != null && override.getResType() == type) {
+                return override.getData();
+            }
+            throw new NotFoundException();
+        }
+    }
+
+    private static PackageInfo createPackageInfo(String packageName) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
+        return packageInfo;
+    }
+
+    private ExternalResources() {}
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/FakeOverrideContentProvider.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/FakeOverrideContentProvider.java
new file mode 100644
index 0000000..b976ce5
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/FakeOverrideContentProvider.java
@@ -0,0 +1,175 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.annotation.ColorRes;
+
+import com.android.car.setupwizardlib.R;
+
+import org.robolectric.Robolectric;
+
+/**
+ * An implementation of
+ * {@link com.google.android.car.setupwizard.partner.PartnerCustomizationProvider} for
+ * Robolectric tests.
+ */
+public class FakeOverrideContentProvider extends ContentProvider {
+
+    public static final @ColorRes int ANDROID_COLOR_DARK_GRAY = android.R.color.darker_gray;
+    public static final float DEFAULT_DIMENSION = 28.0f;
+    public static final int DEFAULT_H_PADDING = 5;
+    public static final int DEFAULT_V_PADDING = 15;
+
+    private static final String TEST_PACKAGE_NAME = "test.packageName";
+
+    private final Bundle mFakeProviderResultBundle = new Bundle();
+
+    public FakeOverrideContentProvider injectResourceEntry(ResourceEntry resourceEntry) {
+        mFakeProviderResultBundle.putBundle(resourceEntry.getResourceName(),
+                resourceEntry.toBundle());
+        return this;
+    }
+
+    public static FakeOverrideContentProvider installEmptyProvider() {
+        PartnerConfigHelper.resetForTesting();
+        return Robolectric.setupContentProvider(
+                FakeOverrideContentProvider.class, PartnerConfigHelper.SUW_AUTHORITY);
+    }
+
+    public static FakeOverrideContentProvider installDefaultProvider() {
+        FakeOverrideContentProvider contentProvider = installEmptyProvider();
+
+        ExternalResources.Resources testResources =
+                ExternalResources.injectExternalResources(TEST_PACKAGE_NAME);
+
+        testResources.putColor(
+                PartnerConfig.CONFIG_LAYOUT_BG_COLOR.getResourceName(),
+                ANDROID_COLOR_DARK_GRAY);
+
+        contentProvider.injectResourceEntry(new ResourceEntry(
+                TEST_PACKAGE_NAME,
+                PartnerConfig.CONFIG_LAYOUT_BG_COLOR.getResourceName(),
+                testResources.getIdentifier(
+                        PartnerConfig.CONFIG_LAYOUT_BG_COLOR.getResourceName(),
+                        /* defType= */ "color",
+                        TEST_PACKAGE_NAME)
+        ));
+
+        testResources.putDrawable(
+                PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG.getResourceName(),
+                application.getResources().getDrawable(R.drawable.button_ripple_bg));
+
+        contentProvider.injectResourceEntry(new ResourceEntry(
+                TEST_PACKAGE_NAME,
+                PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG.getResourceName(),
+                testResources.getIdentifier(
+                        PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG.getResourceName(),
+                        /* defType= */ "drawable",
+                        TEST_PACKAGE_NAME)
+        ));
+
+        testResources.putDimension(
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE.getResourceName(),
+                DEFAULT_DIMENSION);
+
+        contentProvider.injectResourceEntry(new ResourceEntry(
+                TEST_PACKAGE_NAME,
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE.getResourceName(),
+                testResources.getIdentifier(
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE.getResourceName(),
+                        /* defType= */ "dimen",
+                        TEST_PACKAGE_NAME)
+        ));
+
+        testResources.putDimension(
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL.getResourceName(),
+                DEFAULT_H_PADDING);
+
+        contentProvider.injectResourceEntry(new ResourceEntry(
+                TEST_PACKAGE_NAME,
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL.getResourceName(),
+                testResources.getIdentifier(
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL.getResourceName(),
+                        /* defType= */ "dimen",
+                        TEST_PACKAGE_NAME)
+        ));
+
+        testResources.putDimension(
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL.getResourceName(),
+                DEFAULT_V_PADDING);
+
+        contentProvider.injectResourceEntry(new ResourceEntry(
+                TEST_PACKAGE_NAME,
+                PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL.getResourceName(),
+                testResources.getIdentifier(
+                        PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL.getResourceName(),
+                        /* defType= */ "dimen",
+                        TEST_PACKAGE_NAME)
+        ));
+
+        return contentProvider;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(
+            Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("query operation not supported currently.");
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException("getType operation not supported currently.");
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("insert operation not supported currently.");
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("delete operation not supported currently.");
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("update operation not supported currently.");
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (TextUtils.equals(method, PartnerConfigHelper.SUW_GET_PARTNER_CONFIG_METHOD)) {
+            return mFakeProviderResultBundle;
+        }
+        return null;
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java
new file mode 100644
index 0000000..081bb22
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/PartnerConfigHelperTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.graphics.drawable.Drawable;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Unit tests for {@link PartnerConfigHelper}. */
+@RunWith(RobolectricTestRunner.class)
+public class PartnerConfigHelperTest {
+
+    private static final String TEST_PACKAGE_NAME = "test.packageName";
+
+    private static final PartnerConfig TEST_COLOR_RESOURCE_NAME =
+            PartnerConfig.CONFIG_TOOLBAR_BG_COLOR;
+
+    private static final PartnerConfig TEST_DIMENSION_RESOURCE_NAME =
+            PartnerConfig.CONFIG_TOOLBAR_BUTTON_RADIUS;
+
+    private static final PartnerConfig TEST_STRING_RESOURCE_NAME =
+            PartnerConfig.CONFIG_TOOLBAR_BUTTON_FONT_FAMILY;
+
+    private static final PartnerConfig TEST_DRAWABLE_RESOURCE_NAME =
+            PartnerConfig.CONFIG_TOOLBAR_BUTTON_ICON_BACK;
+
+    private static final String EXCEPTED_STRING = "myFont";
+    private static final float EXCEPTED_DIMENSION = 10;
+
+    private Drawable mExpectedDrawable;
+    private List<ResourceEntry> mResourceEntries;
+
+    @Before
+    public void setUp() {
+        FakeOverrideContentProvider fakeOverrideDataProvider =
+                FakeOverrideContentProvider.installEmptyProvider();
+        mResourceEntries = prepareFakeData();
+        for (ResourceEntry entry : mResourceEntries) {
+            fakeOverrideDataProvider.injectResourceEntry(entry);
+        }
+
+        PartnerConfigHelper.resetForTesting();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void getDimension_withWrongConfigType_shouldThrowException() {
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
+        helper.getDimension(application, TEST_COLOR_RESOURCE_NAME);
+    }
+
+    @Test
+    public void getDimension_shouldReturnExpectedDimension() {
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_DIMENSION_RESOURCE_NAME);
+
+        float result = helper.getDimension(application, TEST_DIMENSION_RESOURCE_NAME);
+        assertThat(result).isEqualTo(EXCEPTED_DIMENSION);
+        assertThat(helper.mPartnerResourceCache).containsKey(TEST_DIMENSION_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_DIMENSION_RESOURCE_NAME))
+                .isEqualTo(EXCEPTED_DIMENSION);
+    }
+
+    @Test
+    public void getColor_shouldReturnExpectedColor() {
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_COLOR_RESOURCE_NAME);
+
+        int result = helper.getColor(application, TEST_COLOR_RESOURCE_NAME);
+        assertThat(result).isEqualTo(android.R.color.darker_gray);
+        assertThat(helper.mPartnerResourceCache).containsKey(TEST_COLOR_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_COLOR_RESOURCE_NAME))
+                .isEqualTo(android.R.color.darker_gray);
+    }
+
+    @Test
+    public void getString_shouldReturnExpectedString() {
+        PartnerConfigHelper helper = PartnerConfigHelper.get(application);
+        assertThat(helper.mPartnerResourceCache).doesNotContainKey(TEST_STRING_RESOURCE_NAME);
+
+        String result = helper.getString(application, TEST_STRING_RESOURCE_NAME);
+        assertThat(result).isEqualTo(EXCEPTED_STRING);
+        assertThat(helper.mPartnerResourceCache).containsKey(TEST_STRING_RESOURCE_NAME);
+        assertThat(helper.mPartnerResourceCache.get(TEST_STRING_RESOURCE_NAME))
+                .isEqualTo(EXCEPTED_STRING);
+    }
+
+    private List<ResourceEntry> prepareFakeData() {
+        ExternalResources.Resources testResources =
+                ExternalResources.injectExternalResources(TEST_PACKAGE_NAME);
+
+        testResources
+                .putColor(TEST_COLOR_RESOURCE_NAME.getResourceName(), android.R.color.darker_gray);
+        testResources
+                .putDimension(TEST_DIMENSION_RESOURCE_NAME.getResourceName(), EXCEPTED_DIMENSION);
+        testResources.putText(TEST_STRING_RESOURCE_NAME.getResourceName(), EXCEPTED_STRING);
+
+        mExpectedDrawable =
+                application
+                        .getResources()
+                        .getDrawable(android.R.drawable.arrow_up_float, null);
+        testResources.putDrawable(TEST_DRAWABLE_RESOURCE_NAME.getResourceName(), mExpectedDrawable);
+
+        return Arrays.asList(
+                new ResourceEntry(
+                        TEST_PACKAGE_NAME,
+                        TEST_COLOR_RESOURCE_NAME.getResourceName(),
+                        testResources.getIdentifier(
+                                TEST_COLOR_RESOURCE_NAME.getResourceName(),
+                                /* defType= */ "color",
+                                TEST_PACKAGE_NAME)),
+                new ResourceEntry(
+                        TEST_PACKAGE_NAME,
+                        TEST_DIMENSION_RESOURCE_NAME.getResourceName(),
+                        testResources.getIdentifier(
+                                TEST_DIMENSION_RESOURCE_NAME.getResourceName(),
+                                /* defType= */ "dimen",
+                                TEST_PACKAGE_NAME)),
+                new ResourceEntry(
+                        TEST_PACKAGE_NAME,
+                        TEST_STRING_RESOURCE_NAME.getResourceName(),
+                        testResources.getIdentifier(
+                                TEST_STRING_RESOURCE_NAME.getResourceName(),
+                                /* defType= */"string",
+                                TEST_PACKAGE_NAME)),
+                new ResourceEntry(
+                        TEST_PACKAGE_NAME,
+                        TEST_DRAWABLE_RESOURCE_NAME.getResourceName(),
+                        testResources.getIdentifier(
+                                TEST_DRAWABLE_RESOURCE_NAME.getResourceName(),
+                                /* defType= */ "drawable",
+                                TEST_PACKAGE_NAME))
+        );
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ResourceEntryTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ResourceEntryTest.java
new file mode 100644
index 0000000..651d7d2
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/partner/ResourceEntryTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 com.android.car.setupwizardlib.partner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Unit tests for {@link ResourceEntry}. */
+@RunWith(RobolectricTestRunner.class)
+public final class ResourceEntryTest {
+    private static final String TEST_PACKAGE_NAME = "test.packagename";
+    private static final int TEST_RESOURCE_ID = 1234;
+
+    @Test
+    public void toBundle_getBundleWithCorrectData() {
+        ResourceEntry resourceEntry =
+                new ResourceEntry(
+                        TEST_PACKAGE_NAME,
+                        PartnerConfigKey.KEY_TOOLBAR_BG_COLOR,
+                        TEST_RESOURCE_ID);
+        Bundle result = resourceEntry.toBundle();
+
+        assertThat(result.getString(ResourceEntry.KEY_PACKAGE_NAME)).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(result.getString(ResourceEntry.KEY_RESOURCE_NAME))
+                .isEqualTo(PartnerConfigKey.KEY_TOOLBAR_BG_COLOR);
+        assertThat(result.getInt(ResourceEntry.KEY_RESOURCE_ID)).isEqualTo(TEST_RESOURCE_ID);
+    }
+
+    @Test
+    public void fromBundle_bundleWithRequiredData_getResourceEntryWithCorrectData() {
+        Bundle source = new Bundle();
+        source.putString(ResourceEntry.KEY_PACKAGE_NAME, TEST_PACKAGE_NAME);
+        source.putString(
+                ResourceEntry.KEY_RESOURCE_NAME, PartnerConfigKey.KEY_TOOLBAR_BG_COLOR);
+        source.putInt(ResourceEntry.KEY_RESOURCE_ID, TEST_RESOURCE_ID);
+
+        ResourceEntry result = ResourceEntry.fromBundle(source);
+        assertThat(result.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(result.getResourceName())
+                .isEqualTo(PartnerConfigKey.KEY_TOOLBAR_BG_COLOR);
+        assertThat(result.getResourceId()).isEqualTo(TEST_RESOURCE_ID);
+    }
+
+    @Test
+    public void fromNullBundle_returnNull() {
+        assertThat(ResourceEntry.fromBundle(null)).isNull();
+    }
+
+    @Test
+    public void fromBundleWithoutRequiredData_returnNull() {
+        Bundle source = new Bundle();
+        source.putString("wrongKeyName", TEST_PACKAGE_NAME);
+
+        ResourceEntry result = ResourceEntry.fromBundle(source);
+        assertThat(result).isNull();
+    }
+}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java
similarity index 79%
rename from library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java
rename to library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java
index 569a569..14cb2da 100644
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/BaseRobolectricTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Google Inc.
+ * 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.
@@ -19,15 +19,13 @@
 import org.junit.Rule;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
-import org.robolectric.annotation.Config;
 
 /**
  * Base test for CarSetupWizardLib Robolectric tests that sets the manifest and sdk config
  * parameters
  */
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public abstract class BaseRobolectricTest {
-    //This rule automatically initializes any mocks created using the @Mock annotation
+    // This rule automatically initializes any mocks created using the @Mock annotation
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 }
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java
similarity index 97%
rename from library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java
rename to library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java
index f4caed3..1e16306 100644
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestHelper.java
@@ -26,6 +26,10 @@
  * Helper for commonly used assertions and mocks/spies.
  */
 public final class TestHelper {
+
+    private TestHelper() {
+    }
+
     // View testing helpers.
     public static void assertViewVisible(View view) {
         assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/shadows/ShadowCar.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/shadows/ShadowCar.java
new file mode 100644
index 0000000..f4e66b1
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/shadows/ShadowCar.java
@@ -0,0 +1,142 @@
+/*
+ * 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 com.android.car.setupwizardlib.shadows;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.content.Context;
+import android.content.ServiceConnection;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+/**
+ * Shadow class for {@link Car}. Allows tests to control the return values and behavior of the
+ * object.
+ */
+@Implements(Car.class)
+public class ShadowCar {
+
+    private static Car sMockCar = mock(Car.class);
+    private static boolean sIsConnected;
+    private static String sServiceName;
+    private static Object sCarManager;
+    private static boolean sHasConnected;
+    private static boolean sHasDisconnected;
+
+    /**
+     * Returns a mocked version of a {@link Car} object. Will reset
+     */
+    @Implementation
+    public static Car createCar(Context context, ServiceConnection serviceConnection) {
+        if (serviceConnection != null) {
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    serviceConnection.onServiceConnected(null, null);
+                    sHasConnected = true;
+                    return null;
+                }
+            }).when(sMockCar).connect();
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    sHasDisconnected = true;
+                    serviceConnection.onServiceDisconnected(null);
+                    return null;
+                }
+            }).when(sMockCar).disconnect();
+        }
+        doReturn(sIsConnected).when(sMockCar).isConnected();
+        if (sServiceName != null) {
+            try {
+                doReturn(sCarManager).when(sMockCar).getCarManager(sServiceName);
+            } catch (CarNotConnectedException e) {
+                // do nothing, have to do this because compiler doesn't understand mock can't throw
+                // exception.
+            }
+        }
+        return sMockCar;
+    }
+
+    /**
+     * Sets the isConnected state for the car returned by the {@link #createCar(Context,
+     * ServiceConnection)} method.
+     */
+    public static void setIsConnected(boolean connected) {
+        sIsConnected = connected;
+        doReturn(connected).when(sMockCar).isConnected();
+    }
+
+    /**
+     * Sets the manager returned by {@link Car#getCarManager(String)}.
+     *
+     * @param serviceName the name for the service request that should return this car manager.
+     * @param carManager  the object returned by a call with this service.
+     */
+    public static void setCarManager(String serviceName, Object carManager) {
+        sServiceName = serviceName;
+        sCarManager = carManager;
+        try {
+            doReturn(carManager).when(sMockCar).getCarManager(serviceName);
+        } catch (CarNotConnectedException e) {
+            // do nothing, have to do this because compiler doesn't understand mock can't throw e.
+        }
+    }
+
+    /**
+     * Returns whether the mock has received a call to connect.
+     */
+    public static boolean hasConnected() {
+        return sHasConnected;
+    }
+
+    /**
+     * Returns whether the mock has received a call to disconnect.
+     */
+    public static boolean hasDisconnected() {
+        return sHasDisconnected;
+    }
+
+    /**
+     * Triggers a disconnect on the mock object being held.
+     */
+    public static void triggerDisconnect() {
+        sMockCar.disconnect();
+    }
+
+    /**
+     * Resets the shadow state, note this will not remove stubbed behavior on references to older
+     * calls to {@link #createCar(Context, ServiceConnection)}.
+     */
+    @Resetter
+    public static void reset() {
+        sMockCar = mock(Car.class);
+        sServiceName = null;
+        sCarManager = null;
+        sIsConnected = false;
+        sHasConnected = false;
+        sHasDisconnected = false;
+    }
+}
diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java
new file mode 100644
index 0000000..0afc469
--- /dev/null
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitorTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 com.android.car.setupwizardlib.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.drivingstate.CarUxRestrictionsManager;
+
+import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
+import com.android.car.setupwizardlib.shadows.ShadowCar;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowLooper;
+
+/**
+ * Tests that the {@link CarDrivingStateMonitor} properly initiates the car restrictions monitoring
+ * and calls back to exit listeners on exit.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowCar.class)
+public class CarDrivingStateMonitorTest extends BaseRobolectricTest {
+    @Mock
+    private CarUxRestrictionsManager mMockRestrictionsManager;
+    @Mock
+    private CarUxRestrictions mMockRestrictions;
+
+    private CarDrivingStateMonitor mCarDrivingStateMonitor;
+    private ShadowApplication mShadowApplication;
+
+    @Before
+    public void setupCarState() throws CarNotConnectedException {
+        ShadowCar.setIsConnected(false);
+        ShadowCar.setCarManager(Car.CAR_UX_RESTRICTION_SERVICE, mMockRestrictionsManager);
+        doReturn(mMockRestrictions).when(mMockRestrictionsManager).getCurrentCarUxRestrictions();
+        mCarDrivingStateMonitor = CarDrivingStateMonitor.get(application);
+        mShadowApplication = Shadows.shadowOf(application);
+    }
+
+    @After
+    public void resetCarState() {
+        ShadowCar.reset();
+        CarDrivingStateMonitor.reset(application);
+    }
+
+    @Test
+    public void testStartMonitor_registersCarDrivingStateMonitorAsListener()
+            throws CarNotConnectedException {
+        mCarDrivingStateMonitor.startMonitor();
+        verify(mMockRestrictionsManager).registerListener(eq(mCarDrivingStateMonitor));
+        assertThat(ShadowCar.hasConnected()).isTrue();
+    }
+
+    @Test
+    public void testStartMonitor_withNoManagerFound_doesNothing()
+            throws CarNotConnectedException {
+        ShadowCar.setCarManager(Car.CAR_UX_RESTRICTION_SERVICE, null);
+        mCarDrivingStateMonitor.startMonitor();
+        verify(mMockRestrictionsManager, never()).registerListener(any());
+    }
+
+    @Test
+    public void testStopMonitor_triggersDisconnectCallback() {
+        mCarDrivingStateMonitor.startMonitor();
+        ShadowCar.setIsConnected(true);
+        mCarDrivingStateMonitor.stopMonitor();
+        assertThat(mCarDrivingStateMonitor.mHandler.hasCallbacks(
+                mCarDrivingStateMonitor.mDisconnectRunnable)).isTrue();
+    }
+
+    @Test
+    public void testDisconnect_unregistersCarDrivingStateMonitorAsListener()
+            throws CarNotConnectedException {
+        mCarDrivingStateMonitor.startMonitor();
+        ShadowCar.triggerDisconnect();
+        verify(mMockRestrictionsManager).unregisterListener();
+    }
+
+    @Test
+    public void testOnUxRestrictionsChanged_triggersExit() {
+        mCarDrivingStateMonitor.startMonitor();
+        doReturn(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP).when(mMockRestrictions)
+                .getActiveRestrictions();
+        mCarDrivingStateMonitor.onUxRestrictionsChanged(mMockRestrictions);
+        assertThat(mShadowApplication.getBroadcastIntents().get(0).getAction())
+                .isEqualTo(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION);
+    }
+
+    @Test
+    public void testStartMonitorWhileDriving_triggersExit() {
+        doReturn(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP).when(mMockRestrictions)
+                .getActiveRestrictions();
+        mCarDrivingStateMonitor.startMonitor();
+        assertThat(mShadowApplication.getBroadcastIntents().get(0).getAction())
+                .isEqualTo(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION);
+    }
+
+    @Test
+    public void testStartMonitor_clearsStopMonitorRunnable() {
+        mCarDrivingStateMonitor.startMonitor();
+        ShadowCar.setIsConnected(true);
+        mCarDrivingStateMonitor.stopMonitor();
+        mCarDrivingStateMonitor.startMonitor();
+        assertThat(mCarDrivingStateMonitor.mHandler.hasMessagesOrCallbacks()).isFalse();
+    }
+
+    @Test
+    public void testDrivingMonitorDisconnects_afterDisconnectTimeoutDelay()
+            throws CarNotConnectedException {
+        mCarDrivingStateMonitor.startMonitor();
+        mCarDrivingStateMonitor.stopMonitor();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        verify(mMockRestrictionsManager).unregisterListener();
+    }
+
+    @Test
+    public void testDrivingMonitorDoesNotDisconnect_unlessSameNumberOfStopCallsAsStart() {
+        mCarDrivingStateMonitor.startMonitor();
+        ShadowCar.setIsConnected(true);
+        mCarDrivingStateMonitor.startMonitor();
+        mCarDrivingStateMonitor.stopMonitor();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertThat(ShadowCar.hasDisconnected()).isFalse();
+        mCarDrivingStateMonitor.stopMonitor();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertThat(ShadowCar.hasDisconnected()).isTrue();
+    }
+}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java
similarity index 88%
rename from library/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java
rename to library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java
index a4583ca..4f85acf 100644
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java
+++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/CarWizardManagerHelperTest.java
@@ -23,16 +23,16 @@
 import android.provider.Settings;
 
 import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
-import com.android.car.setupwizardlib.robolectric.CarSetupWizardLibRobolectricTestRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for the {@link CarWizardManagerHelper}.
  */
-@RunWith(CarSetupWizardLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CarWizardManagerHelperTest extends BaseRobolectricTest {
     private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
 
@@ -114,6 +114,28 @@
     }
 
     /**
+     * Test that {@link CarWizardManagerHelper#isDeferredIntent} works.
+     */
+    @Test
+    public void testIsDeferredIntent_doesNotHaveExtra_shouldBeFalse() {
+        Intent intent = new Intent();
+        intent.putExtra(CarWizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false);
+
+        assertThat(CarWizardManagerHelper.isDeferredIntent(intent)).isFalse();
+    }
+
+    /**
+     * Test that {@link CarWizardManagerHelper#isDeferredIntent} works.
+     */
+    @Test
+    public void testIsDeferredIntent_doesHaveExtra_shouldBeTrue() {
+        Intent intent = new Intent();
+        intent.putExtra(CarWizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true);
+
+        assertThat(CarWizardManagerHelper.isDeferredIntent(intent)).isTrue();
+    }
+
+    /**
      * Test that {@link CarWizardManagerHelper#isDealerIntent} works.
      */
     @Test
diff --git a/library/res/values/attrs.xml b/library/res/values/attrs.xml
deleted file mode 100644
index d360db5..0000000
--- a/library/res/values/attrs.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 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.
--->
-<resources>
-    <!-- Custom attribute definitions for the CarSetupWizardLayout -->
-    <declare-styleable name="CarSetupWizardLayout">
-        <!-- Attributes related to the visibility of the back button -->
-        <attr name="showBackButton" format="boolean"/>
-
-        <!-- Attributes related to the visibility and text of the toolbar title -->
-        <attr name="showToolbarTitle" format="boolean"/>
-        <attr name="toolbarTitleText" format="string"/>
-
-        <!--  Attributes related to the visibility and text of primary continue button -->
-        <attr name="showPrimaryToolbarButton" format="boolean"/>
-        <attr name="primaryToolbarButtonText" format="string"/>
-        <attr name="primaryToolbarButtonEnabled" format="boolean"/>
-        <attr name="primaryToolbarButtonFlat" format="boolean"/>
-
-        <!--  Attributes related to the visibility and text of secondary continue button -->
-        <attr name="showSecondaryToolbarButton" format="boolean"/>
-        <attr name="secondaryToolbarButtonText" format="string"/>
-        <attr name="secondaryToolbarButtonEnabled" format="boolean"/>
-
-        <!--  Attributes related to the visibility and indeterminate/determinate state
-              of the progress bar -->
-        <attr name="showProgressBar" format="boolean"/>
-        <attr name="indeterminateProgressBar" format="boolean"/>
-    </declare-styleable>
-</resources>
\ No newline at end of file
diff --git a/library/tests/robotests/Android.mk b/library/tests/robotests/Android.mk
deleted file mode 100644
index c99965e..0000000
--- a/library/tests/robotests/Android.mk
+++ /dev/null
@@ -1,69 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-############################################################
-# CarSetupWizardLib app just for Robolectric test target.  #
-############################################################
-include $(CLEAR_VARS)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := CarSetupWizardLib
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_PRIVILEGED_MODULE := true
-
-include frameworks/opt/car/setupwizard/library/common.mk
-
-include $(BUILD_PACKAGE)
-
-#############################################
-# Car Setup Wizard Library Robolectric test target. #
-#############################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := CarSetupWizardLibRoboTests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/res
-
-LOCAL_JAVA_RESOURCE_DIRS := config
-
-# Include the testing libraries
-LOCAL_JAVA_LIBRARIES := \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt
-
-LOCAL_INSTRUMENTATION_FOR := CarSetupWizardLib
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#############################################################
-# Car Setup Wizard Library runner target to run the previous target. #
-#############################################################
-include $(CLEAR_VARS)
-LOCAL_MODULE := RunCarSetupWizardLibRoboTests
-
-LOCAL_JAVA_LIBRARIES := \
-    CarSetupWizardLibRoboTests \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt
-
-LOCAL_TEST_PACKAGE := CarSetupWizardLib
-
-LOCAL_ROBOTEST_FILES := $(filter-out %/BaseRobolectricTest.java,\
-    $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.))
-
-LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
-
-include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/BaseActivityTest.java b/library/tests/robotests/src/com/android/car/setupwizardlib/BaseActivityTest.java
deleted file mode 100644
index 44ec905..0000000
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/BaseActivityTest.java
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * 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 com.android.car.setupwizardlib;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
-import com.android.car.setupwizardlib.robolectric.CarSetupWizardLibRobolectricTestRunner;
-import com.android.car.setupwizardlib.robolectric.TestHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-
-/**
- * Unit tests for the {@link BaseActivity}.
- */
-@RunWith(CarSetupWizardLibRobolectricTestRunner.class)
-public class BaseActivityTest extends BaseRobolectricTest {
-    BaseActivity mBaseActivity;
-    CarSetupWizardLayout mCarSetupWizardLayout;
-
-    @Before
-    public void setupBaseActivityAndLayout() {
-        mBaseActivity = (BaseActivity) Robolectric.buildActivity(BaseActivity.class).create().get();
-        mCarSetupWizardLayout = mBaseActivity.getCarSetupWizardLayout();
-    }
-
-    /**
-     * Test that the BaseActivity's content view is set to be a CarSetupWizardLayout
-     */
-    @Test
-    public void testContentViewIsCarSetupWizardLayout() {
-        View contentView = mBaseActivity.findViewById(R.id.car_setup_wizard_layout);
-        assertThat(contentView).isNotNull();
-        assertThat(contentView instanceof CarSetupWizardLayout).isTrue();
-    }
-
-    private BaseActivity createSpyBaseActivity() {
-        BaseActivity spyBaseActivity = Mockito.spy(
-                (BaseActivity) Robolectric.buildActivity(BaseActivity.class).get());
-        spyBaseActivity.onCreate(null);
-
-        return spyBaseActivity;
-    }
-
-    /**
-     * Test that the BaseActivity sets the back button listener to call
-     * {@link BaseActivity#handleBackButton()} when created.
-     */
-    @Test
-    public void testBackButtonListenerIsDefault() {
-        BaseActivity spyBaseActivity = createSpyBaseActivity();
-
-        ImageView backButton = (ImageView) spyBaseActivity.findViewById(
-                R.id.back_button);
-        backButton.performClick();
-
-        Mockito.verify(spyBaseActivity).handleBackButton();
-    }
-
-    /**
-     * Test that the BaseActivity sets the secondary toolbar button listener to the default when
-     * created.
-     */
-    @Test
-    public void testSecondaryToolbarButtonListenerIsDefault() {
-        BaseActivity spyBaseActivity = createSpyBaseActivity();
-
-        Button secondaryToolBarButton = (Button) spyBaseActivity.findViewById(
-                R.id.secondary_toolbar_button);
-        secondaryToolBarButton.performClick();
-
-        Mockito.verify(spyBaseActivity).nextAction(Activity.RESULT_OK);
-    }
-
-
-    /**
-     * Test that the BaseActivity sets the primary toolbar button listener to the default when
-     * created.
-     */
-    @Test
-    public void testPrimaryToolbarButtonListenerIsDefault() {
-        BaseActivity spyBaseActivity = createSpyBaseActivity();
-
-        Button primaryToolBarButton = (Button) spyBaseActivity.findViewById(
-                R.id.primary_toolbar_button);
-        primaryToolBarButton.performClick();
-
-        Mockito.verify(spyBaseActivity).nextAction(Activity.RESULT_OK);
-    }
-
-    private BaseActivity getStartedBaseActivity() {
-        return (BaseActivity) Robolectric.buildActivity(BaseActivity.class).create().start().get();
-    }
-
-    private BaseActivity getSavedInstanceStateBaseActivity() {
-        return (BaseActivity) Robolectric.buildActivity(
-                BaseActivity.class).create().saveInstanceState(new Bundle()).get();
-    }
-
-    /**
-     * Test that fragment commits are allowed after {@link BaseActivity#onStart()} is called.
-     */
-    @Test
-    public void testFragmentCommitsAllowedAfterOnStart() {
-        assertThat(getStartedBaseActivity().getAllowFragmentCommits()).isTrue();
-    }
-
-    /**
-     * Test that fragment commits are not allowed after {@link BaseActivity#onSaveInstanceState }
-     * is called.
-     */
-    @Test
-    public void testFragmentCommitsNotAllowedAfterOnSavedInstanceState() {
-        assertThat(getSavedInstanceStateBaseActivity().getAllowFragmentCommits()).isFalse();
-    }
-
-
-    /**
-     * Test that {@link BaseActivity#setContentFragment} sets the content fragment and calls the
-     * expected methods when fragment commits are allowed.
-     */
-    @Test
-    public void testSetContentFragmentWhenFragmentCommitsAllowed() {
-        BaseActivity spyBaseActivity = Mockito.spy(getStartedBaseActivity());
-
-        Fragment fragment = new Fragment();
-        spyBaseActivity.setContentFragment(fragment);
-
-        assertThat(spyBaseActivity.getContentFragment()).isEqualTo(fragment);
-        assertThat(spyBaseActivity.getSupportFragmentManager().getBackStackEntryCount()).isEqualTo(
-                0);
-        // Verify that onContentFragmentSet is called with the test fragment
-        Mockito.verify(spyBaseActivity).onContentFragmentSet(fragment);
-    }
-
-    /**
-     * Test that {@link BaseActivity#setContentFragment} does nothing when fragment commits are not
-     * allowed.
-     */
-    @Test
-    public void testSetContentFragmentWhenFragmentCommitsNotAllowed() {
-        BaseActivity spyBaseActivity = Mockito.spy(getSavedInstanceStateBaseActivity());
-
-        Fragment fragment = new Fragment();
-        spyBaseActivity.setContentFragment(fragment);
-
-        assertThat(spyBaseActivity.getContentFragment()).isEqualTo(null);
-        assertThat(spyBaseActivity.getSupportFragmentManager().getBackStackEntryCount()).isEqualTo(
-                0);
-        // Verify that onContentFragmentSet is not called
-        Mockito.verify(spyBaseActivity, Mockito.times(0)).onContentFragmentSet(fragment);
-    }
-
-    /**
-     * Test that {@link BaseActivity#setContentFragmentWithBackstack)} sets the content fragment,
-     * adds it to the fragment backstack, and calls the expected methods when fragment commits
-     * are allowed.
-     */
-    @Test
-    public void testSetContentFragmentWithBackstackWhenFragmentCommitsAllowed() {
-        BaseActivity spyBaseActivity = Mockito.spy(getStartedBaseActivity());
-
-        Fragment fragment = new Fragment();
-        spyBaseActivity.setContentFragmentWithBackstack(fragment);
-
-        assertThat(spyBaseActivity.getContentFragment()).isEqualTo(fragment);
-        assertThat(spyBaseActivity.getSupportFragmentManager().getBackStackEntryCount()).isEqualTo(
-                1);
-        // Verify that onContentFragmentSet is called with the test fragment
-        Mockito.verify(spyBaseActivity).onContentFragmentSet(fragment);
-    }
-
-    /**
-     * Test that {@link BaseActivity#setContentFragmentWithBackstack)} does nothing when fragment
-     * commits are not allowed.
-     */
-    @Test
-    public void testSetContentFragmentWithBackstackWhenFragmentCommitsNotAllowed() {
-        BaseActivity spyBaseActivity = Mockito.spy(getSavedInstanceStateBaseActivity());
-
-        Fragment fragment = new Fragment();
-        spyBaseActivity.setContentFragment(fragment);
-
-        assertThat(spyBaseActivity.getContentFragment()).isEqualTo(null);
-        assertThat(spyBaseActivity.getSupportFragmentManager().getBackStackEntryCount()).isEqualTo(
-                0);
-        // Verify that onContentFragmentSet is not called
-        Mockito.verify(spyBaseActivity, Mockito.times(0)).onContentFragmentSet(fragment);
-    }
-
-    /**
-     * Test that {@link BaseActivity#popBackStackImmediate()} returns false when no fragment is
-     * added to the backstack.
-     */
-    @Test
-    public void testPopBackStackImmediateWithEmptyStack() {
-        assertThat(mBaseActivity.popBackStackImmediate()).isEqualTo(false);
-    }
-
-    /**
-     * Test that {@link BaseActivity#popBackStackImmediate()} returns true when a fragment is
-     * added to the backstack and that the fragment is popped off of the backstack.
-     */
-    @Test
-    public void testPopBackStackImmediateWithFragmentInStack() {
-        Fragment fragment = new Fragment();
-        mBaseActivity.setContentFragmentWithBackstack(fragment);
-        assertThat(mBaseActivity.popBackStackImmediate()).isEqualTo(true);
-
-        assertThat(mBaseActivity.getContentFragment()).isNull();
-    }
-
-    /**
-     * Test that {@link BaseActivity#getContentFragment()} returns the content fragment.
-     */
-    @Test
-    public void testGetContentFragment() {
-        Fragment fragment = new Fragment();
-        mBaseActivity.setContentFragment(fragment);
-
-        assertThat(mBaseActivity.getContentFragment()).isEqualTo(
-                mBaseActivity.getSupportFragmentManager().findFragmentByTag(
-                        mBaseActivity.CONTENT_FRAGMENT_TAG));
-    }
-
-    /**
-     * Test that {@link BaseActivity#setContentLayout} adds the specified layout to the
-     * BaseActivity.
-     */
-    @Test
-    public void testSetContentLayout() {
-        mBaseActivity.setContentLayout(R.layout.base_activity_test_layout);
-        View contentLayout = mBaseActivity.findViewById(R.id.content_layout);
-        assertThat(contentLayout).isNotNull();
-    }
-
-    /**
-     * Test that {@link BaseActivity#finishAction()} results in a call to
-     * {@link BaseActivity#finish}.
-     */
-    @Test
-    public void testFinishAction() {
-        BaseActivity spyBaseActivity = Mockito.spy(mBaseActivity);
-        spyBaseActivity.finishAction();
-
-        Mockito.verify(spyBaseActivity).finish();
-    }
-
-    /**
-     * Test that {@link BaseActivity#finishAction(int)} )} results in a call to
-     * {@link BaseActivity#nextAction} and {@link BaseActivity#finish}.
-     */
-    @Test
-    public void testFinishActionWithResultCode() {
-        BaseActivity spyBaseActivity = Mockito.spy(mBaseActivity);
-        spyBaseActivity.finishAction(BaseActivity.RESULT_OK);
-
-        Mockito.verify(spyBaseActivity).nextAction(BaseActivity.RESULT_OK, null);
-        Mockito.verify(spyBaseActivity).finish();
-    }
-
-    /**
-     * Test that {@link BaseActivity#setBackButtonVisible} sets the back button visible/not visible.
-     */
-    @Test
-    public void testSetBackButtonVisibleTrue() {
-        mBaseActivity.setBackButtonVisible(true);
-        TestHelper.assertViewVisible(mCarSetupWizardLayout.getBackButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setBackButtonVisible} sets the back button visible/not visible.
-     */
-    @Test
-    public void testSetBackButtonVisibleFalse() {
-        mBaseActivity.setBackButtonVisible(false);
-        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getBackButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setToolbarTitleVisible} sets the toolbar title visible/not
-     * visible.
-     */
-    @Test
-    public void testSetToolbarTitleVisibleTrue() {
-        mBaseActivity.setToolbarTitleVisible(true);
-        TestHelper.assertViewVisible(mCarSetupWizardLayout.getToolbarTitle());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setToolbarTitleVisible} sets the toolbar button visible/not
-     * visible.
-     */
-    @Test
-    public void testSetToolbarTitleVisibleFalse() {
-        mBaseActivity.setToolbarTitleVisible(false);
-        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getToolbarTitle());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setToolbarTitleText(String)} sets the toolbar title text.
-     */
-    @Test
-    public void testSetToolbarTitleText() {
-        mBaseActivity.setToolbarTitleText("title text");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getToolbarTitle(), "title text");
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonVisible} sets the primary toolbar button
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonVisibleTrue() {
-        mBaseActivity.setPrimaryToolbarButtonVisible(true);
-        TestHelper.assertViewVisible(mCarSetupWizardLayout.getPrimaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonVisible} sets the primary toolbar button
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonVisibleFalse() {
-        mBaseActivity.setPrimaryToolbarButtonVisible(false);
-        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getPrimaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonEnabled} sets the primary toolbar button
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonEnabledTrue() {
-        mBaseActivity.setPrimaryToolbarButtonEnabled(true);
-        TestHelper.assertViewEnabled(mCarSetupWizardLayout.getPrimaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonEnabled} sets the primary toolbar button
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonEnabledFalse() {
-        mBaseActivity.setPrimaryToolbarButtonEnabled(false);
-        TestHelper.assertViewNotEnabled(mCarSetupWizardLayout.getPrimaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonText(String)} sets the primary
-     * toolbar title text.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonText() {
-        mBaseActivity.setPrimaryToolbarButtonText("button text");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getPrimaryToolbarButton(), "button text");
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonFlat(boolean)} sets the primary toolbar
-     * button flat/not flat.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonFlatTrue() {
-        mBaseActivity.setPrimaryToolbarButtonFlat(true);
-        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButtonFlat()).isTrue();
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonFlat(boolean)} sets the primary toolbar
-     * button flat/not flat.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonFlatFalse() {
-        mBaseActivity.setPrimaryToolbarButtonFlat(false);
-        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButtonFlat()).isFalse();
-    }
-
-    /**
-     * Test that {@link BaseActivity#setPrimaryToolbarButtonOnClickListener} sets the primary
-     * toolbar button's click listener.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonOnClickListener() {
-        View.OnClickListener spyListener = TestHelper.createSpyListener();
-
-        mBaseActivity.setPrimaryToolbarButtonOnClickListener(spyListener);
-        mBaseActivity.getCarSetupWizardLayout().getPrimaryToolbarButton().performClick();
-        Mockito.verify(spyListener).onClick(Mockito.any());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonVisible} sets the secondary toolbar
-     * button visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonVisibleTrue() {
-        mBaseActivity.setSecondaryToolbarButtonVisible(true);
-        TestHelper.assertViewVisible(mCarSetupWizardLayout.getSecondaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonVisible} sets the secondary toolbar
-     * button visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonVisibleFalse() {
-        mBaseActivity.setSecondaryToolbarButtonVisible(false);
-        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getSecondaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonEnabled} sets the secondary toolbar
-     * button visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonEnabledTrue() {
-        mBaseActivity.setSecondaryToolbarButtonEnabled(true);
-        TestHelper.assertViewEnabled(mCarSetupWizardLayout.getSecondaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonEnabled} sets the secondary toolbar
-     * button visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonEnabledFalse() {
-        mBaseActivity.setSecondaryToolbarButtonEnabled(false);
-        TestHelper.assertViewNotEnabled(mCarSetupWizardLayout.getSecondaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonText(String)} sets the secondary
-     * toolbar title text.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonText() {
-        mBaseActivity.setSecondaryToolbarButtonText("button text");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getSecondaryToolbarButton(),
-                "button text");
-    }
-
-    /**
-     * Test that {@link BaseActivity#setSecondaryToolbarButtonOnClickListener} sets the secondary
-     * toolbar button's click listener.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonOnClickListener() {
-        View.OnClickListener spyListener = TestHelper.createSpyListener();
-
-        mBaseActivity.setSecondaryToolbarButtonOnClickListener(spyListener);
-        mBaseActivity.getCarSetupWizardLayout().getSecondaryToolbarButton().performClick();
-        Mockito.verify(spyListener).onClick(Mockito.any());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setProgressBarVisible} sets the progressbar visible/not
-     * visible.
-     */
-    @Test
-    public void testSetProgressBarVisibleTrue() {
-        mBaseActivity.setProgressBarVisible(true);
-        TestHelper.assertViewVisible(mCarSetupWizardLayout.getProgressBar());
-    }
-
-    /**
-     * Test that {@link BaseActivity#setProgressBarVisible} sets the progressbar visible/not
-     * visible.
-     */
-    @Test
-    public void testSetProgressBarVisibleFalse() {
-        mBaseActivity.setProgressBarVisible(false);
-        TestHelper.assertViewNotVisible(mCarSetupWizardLayout.getProgressBar());
-    }
-}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTest.java b/library/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTest.java
deleted file mode 100644
index 6cf83b1..0000000
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardLayoutTest.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.car.setupwizardlib;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest;
-import com.android.car.setupwizardlib.robolectric.CarSetupWizardLibRobolectricTestRunner;
-import com.android.car.setupwizardlib.robolectric.TestHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.Locale;
-
-/**
- * Tests for the CarSetupWizardLayout
- */
-@RunWith(CarSetupWizardLibRobolectricTestRunner.class)
-public class CarSetupWizardLayoutTest extends BaseRobolectricTest {
-    private static final float COMPARISON_TOLERANCE = .01f;
-    private static final Locale LOCALE_EN_US = new Locale("en", "US");
-    // Hebrew locale can be used to test RTL.
-    private static final Locale LOCALE_IW_IL = new Locale("iw", "IL");
-
-    private CarSetupWizardLayout mCarSetupWizardLayout;
-    private CarSetupWizardLayoutTestActivity mCarSetupWizardLayoutTestActivity;
-
-    @Before
-    public void setUp() {
-        mCarSetupWizardLayoutTestActivity = Robolectric
-                .buildActivity(CarSetupWizardLayoutTestActivity.class)
-                .create()
-                .get();
-
-        mCarSetupWizardLayout = mCarSetupWizardLayoutTestActivity.
-                findViewById(R.id.car_setup_wizard_layout);
-
-        // Have to make this call first to ensure secondaryToolbar button is created from stub.
-        mCarSetupWizardLayout.setSecondaryToolbarButtonVisible(true);
-        mCarSetupWizardLayout.setSecondaryToolbarButtonVisible(false);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setBackButtonListener} does set the back button
-     * listener.
-     */
-    @Test
-    public void testSetBackButtonListener() {
-        View.OnClickListener spyListener = TestHelper.createSpyListener();
-
-        mCarSetupWizardLayout.setBackButtonListener(spyListener);
-        mCarSetupWizardLayout.getBackButton().performClick();
-        Mockito.verify(spyListener).onClick(mCarSetupWizardLayout.getBackButton());
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setBackButtonVisible} does set the view visible/not
-     * visible and calls updateBackButtonTouchDelegate.
-     */
-    @Test
-    public void testSetBackButtonVisibleTrue() {
-        CarSetupWizardLayout spyCarSetupWizardLayout = Mockito.spy(mCarSetupWizardLayout);
-
-        spyCarSetupWizardLayout.setBackButtonVisible(true);
-        View backButton = spyCarSetupWizardLayout.getBackButton();
-        TestHelper.assertViewVisible(backButton);
-        Mockito.verify(spyCarSetupWizardLayout).updateBackButtonTouchDelegate(true);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setBackButtonVisible} does set the view visible/not
-     * visible and calls updateBackButtonTouchDelegate.
-     */
-    @Test
-    public void testSetBackButtonVisibleFalse() {
-        CarSetupWizardLayout spyCarSetupWizardLayout = Mockito.spy(mCarSetupWizardLayout);
-
-        spyCarSetupWizardLayout.setBackButtonVisible(false);
-        View backButton = spyCarSetupWizardLayout.getBackButton();
-        TestHelper.assertViewNotVisible(backButton);
-        Mockito.verify(spyCarSetupWizardLayout).updateBackButtonTouchDelegate(false);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setToolbarTitleVisible} does set the view visible/not
-     * visible.
-     */
-    @Test
-    public void testSetToolbarTitleVisibleTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getToolbarTitle();
-
-        mCarSetupWizardLayout.setToolbarTitleVisible(true);
-        TestHelper.assertViewVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setToolbarTitleVisible} does set the view visible/not
-     * visible.
-     */
-    @Test
-    public void testSetToolbarTitleVisibleFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getToolbarTitle();
-
-        mCarSetupWizardLayout.setToolbarTitleVisible(false);
-        TestHelper.assertViewNotVisible(toolbarTitle);
-    }
-
-    /**
-     * Tests that {@link CarSetupWizardLayout#setToolbarTitleText(String)} does set the toolbar
-     * title text.
-     */
-    @Test
-    public void testSetToolbarTitleText() {
-        mCarSetupWizardLayout.setToolbarTitleText("test title");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getToolbarTitle(), "test title");
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setPrimaryToolbarButtonVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonVisibleTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getPrimaryToolbarButton();
-
-        mCarSetupWizardLayout.setPrimaryToolbarButtonVisible(true);
-        TestHelper.assertViewVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setPrimaryToolbarButtonVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonVisibleFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getPrimaryToolbarButton();
-
-        mCarSetupWizardLayout.setPrimaryToolbarButtonVisible(false);
-        TestHelper.assertViewNotVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setPrimaryToolbarButtonEnabled} does set the view
-     * enabled/not enabled.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonEnabledTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getPrimaryToolbarButton();
-
-        mCarSetupWizardLayout.setPrimaryToolbarButtonEnabled(true);
-        TestHelper.assertViewEnabled(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setPrimaryToolbarButtonEnabled} does set the view
-     * enabled/not enabled.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonEnabledFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getPrimaryToolbarButton();
-
-        mCarSetupWizardLayout.setPrimaryToolbarButtonEnabled(false);
-        TestHelper.assertViewNotEnabled(toolbarTitle);
-    }
-
-    /**
-     * Tests that {@link CarSetupWizardLayout#setPrimaryToolbarButtonText(String)} does set the
-     * primary toolbar button text.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonText() {
-        mCarSetupWizardLayout.setPrimaryToolbarButtonText("test title");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getPrimaryToolbarButton(), "test title");
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setPrimaryToolbarButtonListener} does set the primary
-     * toolbar button listener.
-     */
-    @Test
-    public void testSetPrimaryToolbarButtonListener() {
-        View.OnClickListener spyListener = TestHelper.createSpyListener();
-
-        mCarSetupWizardLayout.setPrimaryToolbarButtonListener(spyListener);
-        mCarSetupWizardLayout.getPrimaryToolbarButton().performClick();
-        Mockito.verify(spyListener).onClick(mCarSetupWizardLayout.getPrimaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#createPrimaryToolbarButton} creates a new button but
-     * holds over the correct attributes.
-     */
-    @Test
-    public void testCreatePrimaryButtonTrue() {
-        Button currPrimaryToolbarButton = mCarSetupWizardLayout.getPrimaryToolbarButton();
-        Button primaryToolbarButton = mCarSetupWizardLayout.createPrimaryToolbarButton(true);
-
-        assertThat(primaryToolbarButton.getVisibility()).isEqualTo(
-                currPrimaryToolbarButton.getVisibility());
-        assertThat(primaryToolbarButton.isEnabled()).isEqualTo(
-                currPrimaryToolbarButton.isEnabled());
-        assertThat(primaryToolbarButton.getText()).isEqualTo(currPrimaryToolbarButton.getText());
-        assertThat(primaryToolbarButton.getLayoutParams()).isEqualTo(
-                currPrimaryToolbarButton.getLayoutParams());
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setSecondaryToolbarButtonVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonVisibleTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getSecondaryToolbarButton();
-
-        mCarSetupWizardLayout.setSecondaryToolbarButtonVisible(true);
-        TestHelper.assertViewVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setSecondaryToolbarButtonVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonVisibleFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getSecondaryToolbarButton();
-
-        mCarSetupWizardLayout.setSecondaryToolbarButtonVisible(false);
-        TestHelper.assertViewNotVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setSecondaryToolbarButtonEnabled} does set the view
-     * enabled/not enabled.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonEnabledTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getSecondaryToolbarButton();
-
-        mCarSetupWizardLayout.setSecondaryToolbarButtonEnabled(true);
-        TestHelper.assertViewEnabled(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setSecondaryToolbarButtonEnabled} does set the view
-     * enabled/not enabled.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonEnabledFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getSecondaryToolbarButton();
-
-        mCarSetupWizardLayout.setSecondaryToolbarButtonEnabled(false);
-        TestHelper.assertViewNotEnabled(toolbarTitle);
-    }
-
-    /**
-     * Tests that {@link CarSetupWizardLayout#setSecondaryToolbarButtonText(String)} does set the
-     * secondary toolbar button text.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonText() {
-        mCarSetupWizardLayout.setSecondaryToolbarButtonText("test title");
-        TestHelper.assertTextEqual(mCarSetupWizardLayout.getSecondaryToolbarButton(), "test title");
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setSecondaryToolbarButtonListener} does set the
-     * secondary toolbar button listener.
-     */
-    @Test
-    public void testSetSecondaryToolbarButtonListener() {
-        View.OnClickListener spyListener = TestHelper.createSpyListener();
-
-        mCarSetupWizardLayout.setSecondaryToolbarButtonListener(spyListener);
-        mCarSetupWizardLayout.getSecondaryToolbarButton().performClick();
-        Mockito.verify(spyListener).onClick(mCarSetupWizardLayout.getSecondaryToolbarButton());
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setProgressBarVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetProgressBarVisibleTrue() {
-        View toolbarTitle = mCarSetupWizardLayout.getProgressBar();
-
-        mCarSetupWizardLayout.setProgressBarVisible(true);
-        TestHelper.assertViewVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setProgressBarVisible} does set the view
-     * visible/not visible.
-     */
-    @Test
-    public void testSetProgressBarVisibleFalse() {
-        View toolbarTitle = mCarSetupWizardLayout.getProgressBar();
-
-        mCarSetupWizardLayout.setProgressBarVisible(false);
-        TestHelper.assertViewNotVisible(toolbarTitle);
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setProgressBarIndeterminate(boolean)}
-     * does set the progress bar intermediate/not indeterminate.
-     */
-    @Test
-    public void testSetProgressBarIndeterminateTrue() {
-        mCarSetupWizardLayout.setProgressBarIndeterminate(true);
-        assertThat(mCarSetupWizardLayout.getProgressBar().isIndeterminate()).isTrue();
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setProgressBarIndeterminate(boolean)}
-     * does set the progress bar intermediate/not indeterminate.
-     */
-    @Test
-    public void testSetProgressBarIndeterminateFalse() {
-        mCarSetupWizardLayout.setProgressBarIndeterminate(false);
-        assertThat(mCarSetupWizardLayout.getProgressBar().isIndeterminate()).isFalse();
-    }
-
-    /**
-     * Test that {@link CarSetupWizardLayout#setProgressBarProgress} does set the progress.
-     */
-    @Test
-    public void testSetProgressBarProgress() {
-        mCarSetupWizardLayout.setProgressBarProgress(80);
-        assertThat(mCarSetupWizardLayout.getProgressBar().getProgress()).isEqualTo(80);
-    }
-
-    @Test
-    public void testApplyUpdatedLocale() {
-        mCarSetupWizardLayout.applyLocale(LOCALE_IW_IL);
-        TextView toolbarTitle = mCarSetupWizardLayout.getToolbarTitle();
-        Button primaryToolbarButton = mCarSetupWizardLayout.getPrimaryToolbarButton();
-        Button secondaryToolbarButton = mCarSetupWizardLayout.getSecondaryToolbarButton();
-
-        assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_IW_IL);
-        assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL);
-        assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL);
-
-        mCarSetupWizardLayout.applyLocale(LOCALE_EN_US);
-        assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_EN_US);
-        assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US);
-        assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US);
-    }
-
-    @Test
-    public void testGetBackButton() {
-        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButton()).isEqualTo(
-                mCarSetupWizardLayout.findViewById(R.id.primary_toolbar_button));
-    }
-
-    @Test
-    public void testGetToolBarTitle() {
-        assertThat(mCarSetupWizardLayout.getToolbarTitle()).isEqualTo(
-                mCarSetupWizardLayout.findViewById(R.id.toolbar_title));
-    }
-
-    @Test
-    public void testGetPrimaryToolBarButton() {
-        assertThat(mCarSetupWizardLayout.getPrimaryToolbarButton()).isEqualTo(
-                mCarSetupWizardLayout.findViewById(R.id.primary_toolbar_button));
-    }
-
-    @Test
-    public void testGetSecondaryToolBarButton() {
-        assertThat(mCarSetupWizardLayout.getSecondaryToolbarButton()).isEqualTo(
-                mCarSetupWizardLayout.findViewById(R.id.secondary_toolbar_button));
-    }
-
-    @Test
-    public void testGetProgressBar() {
-        assertThat(mCarSetupWizardLayout.getProgressBar()).isEqualTo(
-                mCarSetupWizardLayout.findViewById(R.id.progress_bar));
-    }
-
-    @Test
-    public void testTitleBarElevationChange() {
-        mCarSetupWizardLayout.addElevationToTitleBar(/*animate= */ false);
-        View titleBar = mCarSetupWizardLayout.findViewById(R.id.application_bar);
-        assertThat(titleBar.getElevation()).isWithin(COMPARISON_TOLERANCE).of(
-                RuntimeEnvironment.application.getResources().getDimension(
-                        R.dimen.title_bar_drop_shadow_elevation));
-
-        mCarSetupWizardLayout.removeElevationFromTitleBar(/*animate= */ false);
-        assertThat(titleBar.getElevation()).isWithin(COMPARISON_TOLERANCE).of(0f);
-    }
-}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/CarSetupWizardLibRobolectricTestRunner.java b/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/CarSetupWizardLibRobolectricTestRunner.java
deleted file mode 100644
index 86e6598..0000000
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/CarSetupWizardLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.car.setupwizardlib.robolectric;
-
-import androidx.annotation.NonNull;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.manifest.AndroidManifest;
-import org.robolectric.res.Fs;
-import org.robolectric.res.ResourcePath;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Custom test runner for the testing of CarSetupWizardLibrary. This is needed because the
- * default behavior for robolectric is just to grab the resource directory in the target package.
- * We want to override this to add several spanning different projects.
- */
-public class CarSetupWizardLibRobolectricTestRunner extends RobolectricTestRunner {
-    private static final Map<String, String> AAR_VERSIONS;
-    private static final String SUPPORT_RESOURCE_PATH_TEMPLATE =
-            "jar:file:prebuilts/sdk/current/androidx/m2repository/androidx/"
-                    + "%1$s/%1$s/%2$s/%1$s-%2$s.aar!/res";
-
-    static {
-        AAR_VERSIONS = new HashMap<>();
-        AAR_VERSIONS.put("car", "1.0.0-alpha3");
-        AAR_VERSIONS.put("appcompat", "1.0.0-alpha1");
-    }
-
-    /**
-     * We don't actually want to change this behavior, so we just call super.
-     */
-    public CarSetupWizardLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
-        super(testClass);
-    }
-
-    private static ResourcePath createResourcePath(@NonNull String filePath) {
-        try {
-            return new ResourcePath(null, Fs.fromURL(new URL(filePath)), null);
-        } catch (MalformedURLException e) {
-            throw new RuntimeException("CarSetupWizardRobolectricTestRunner failure", e);
-        }
-    }
-
-    /**
-     * Create the resource path for a support library component's JAR.
-     */
-    private static String createSupportResourcePathFromJar(@NonNull String componentId) {
-        if (!AAR_VERSIONS.containsKey(componentId)) {
-            throw new IllegalArgumentException("Unknown component " + componentId
-                    + ". Update test with appropriate component name and version.");
-        }
-        return String.format(SUPPORT_RESOURCE_PATH_TEMPLATE, componentId,
-                AAR_VERSIONS.get(componentId));
-    }
-
-    /**
-     * We are going to create our own custom manifest so that we can add multiple resource
-     * paths to it. This lets us access resources in both Settings and SettingsLib in our tests.
-     */
-    @Override
-    protected AndroidManifest getAppManifest(Config config) {
-        // Using the manifest file's relative path, we can figure out the application directory.
-        String appRoot = "frameworks/opt/car/setupwizard/library";
-        String manifestPath = appRoot + "/AndroidManifest.xml";
-        String resDir = appRoot + "/res";
-        String assetsDir = appRoot + config.assetDir();
-
-        // By adding any resources from libraries we need to the AndroidManifest, we can access
-        // them from within the parallel universe's resource loader.
-        return new AndroidManifest(Fs.fileFromPath(manifestPath),
-                Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
-            @Override
-            public List<ResourcePath> getIncludedResourcePaths() {
-                List<ResourcePath> paths = super.getIncludedResourcePaths();
-
-                paths.add(createResourcePath("file:" + resDir));
-                paths.add(createResourcePath("file:" + appRoot + "/tests/robotests/res"));
-
-                // Support library resources. These need to point to the prebuilts of support
-                // library and not the source.
-                paths.add(createResourcePath(createSupportResourcePathFromJar("appcompat")));
-                paths.add(createResourcePath(createSupportResourcePathFromJar("car")));
-
-                return paths;
-            }
-        };
-    }
-}
diff --git a/library/Android.bp b/library/utils/Android.bp
similarity index 84%
copy from library/Android.bp
copy to library/utils/Android.bp
index aeaddf1..ac1f514 100644
--- a/library/Android.bp
+++ b/library/utils/Android.bp
@@ -1,4 +1,3 @@
-//
 // Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,10 +14,10 @@
 //
 
 android_library {
-    name: "car-setup-wizard-lib",
-    srcs: ["src/**/*.java"],
-    resource_dirs: ["res"],
-    static_libs: ["androidx.car_car"],
+    name: "car-setup-wizard-lib-utils",
+    srcs: ["src/**/*.java",
+            "src/**/*.aidl"],
+    libs: ["android.car"],
     optimize: {
         enabled: false,
     },
diff --git a/library/AndroidManifest.xml b/library/utils/AndroidManifest.xml
similarity index 76%
copy from library/AndroidManifest.xml
copy to library/utils/AndroidManifest.xml
index ae6f822..a974cc3 100644
--- a/library/AndroidManifest.xml
+++ b/library/utils/AndroidManifest.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
     Copyright (C) 2017 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 package="com.android.car.setupwizardlib">
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.car.setupwizardlib">
+    <uses-sdk
+        android:minSdkVersion="24"
+        android:targetSdkVersion="26" />
 </manifest>
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/IInitialLockSetupService.aidl b/library/utils/src/com/android/car/setupwizardlib/IInitialLockSetupService.aidl
new file mode 100644
index 0000000..1940ccb
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/IInitialLockSetupService.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import src.com.android.car.setupwizardlib.LockConfig;
+
+interface IInitialLockSetupService {
+
+    /**
+     * Returns the version of the library that the service was built on.
+     */
+    int getServiceVersion() = 0;
+
+    /**
+     * Returns a {@link LockConfig} with the lock configuration for the given
+     * {@link InitialLockSetupConstants#LockTypes} type.
+     */
+    LockConfig getLockConfig(in int lockType) = 1;
+
+
+    /**
+     * Returns the error flags for the entered password. If there is no error and the password is
+     * valid then it will return 0.
+     */
+    int checkValidLock(in int lockType, in byte[] password) = 2;
+
+    /**
+     * Sets the lock password according to the lock type. This lock should be
+     * serialized based on the methods provided by the library to ensure it is
+     * deserializable by the service.
+     */
+    int setLock(in int lockType, in byte[] password) = 3;
+}
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/InitialLockListener.java b/library/utils/src/com/android/car/setupwizardlib/InitialLockListener.java
new file mode 100644
index 0000000..39eb702
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/InitialLockListener.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import java.util.Map;
+
+/**
+ * Listener interface for callbacks from asynchronous calls made through the {@link
+ * InitialLockSetupClient} interface.
+ */
+public interface InitialLockListener {
+    /**
+     * Method to be called when the connection to the lock service is
+     * finished. Will pass through whether or not the connection was
+     * successful. This connection will fail if there is a lock already set.
+     */
+    void onConnectionAttemptFinished(boolean successful);
+
+    /**
+     * Returns a map from the
+     * {@link com.android.car.setupwizardlib.InitialLockSetupConstants.LockTypes}
+     * to the corresponding {@link LockConfig}
+     */
+    void onGetLockConfigs(Map<Integer, LockConfig> lockConfigMap);
+
+    /**
+     * Method to be called when the lock validation has been completed, passes through whether
+     * the lock is valid to be saved or if not, the error reason.
+     */
+    void onLockValidated(@InitialLockSetupConstants.ValidateLockFlags int valid);
+
+    /**
+     * Method to be called when the attempt to save the lock is finished. Will
+     * pass through whether or not the lock was set successfully.
+     */
+    void onSetLockFinished(@InitialLockSetupConstants.SetLockCodes int result);
+
+    /**
+     * Method to be called when the service disconnects.
+     */
+    void onDisconnect();
+}
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java
new file mode 100644
index 0000000..4ef851e
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java
@@ -0,0 +1,421 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.car.setupwizardlib.InitialLockSetupConstants.LockTypes;
+import com.android.car.setupwizardlib.InitialLockSetupConstants.SetLockCodes;
+import com.android.car.setupwizardlib.InitialLockSetupConstants.ValidateLockFlags;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Client for communicating with the InitialLockSetupService in Car Settings.
+ * This allows a device setup wizard to set the initial lock on a device that does not have one.
+ */
+public class InitialLockSetupClient implements ServiceConnection {
+
+    private static final String INITIAL_LOCK_SERVICE_PACKAGE = "com.android.car.settings";
+    private static final String INITIAL_LOCK_SERVICE_CLASS_NAME =
+            INITIAL_LOCK_SERVICE_PACKAGE + ".setupservice.InitialLockSetupService";
+    private static final Intent INITIAL_LOCK_SERVICE_INTENT = new Intent().setComponent(
+            new ComponentName(INITIAL_LOCK_SERVICE_PACKAGE, INITIAL_LOCK_SERVICE_CLASS_NAME));
+
+    private static final String TAG = "InitialLockSetupClient";
+    // Arbitrary timeout before giving up on connecting to service.
+    private static final long CONNECTION_TIMEOUT_MS = 5000;
+
+    private final Runnable mTimeoutRunnable = this::connectionTimeout;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private InitialLockListener mInitialLockListener;
+    private Context mContext;
+    private IInitialLockSetupService mInitialLockSetupService;
+    private ValidateLockAsyncTask mCurrentValidateLockTask;
+    private SaveLockAsyncTask mCurrentSaveLockTask;
+    private boolean mCurrentlyBinding;
+    // Tracks whether the connection has timed out to prevent duplicate callbacks to listener.
+    private boolean mTimedOut;
+
+    public InitialLockSetupClient(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Starts the connection to the service.
+     */
+    public void startConnection(InitialLockListener listener) {
+        logVerbose("startConnection");
+        mInitialLockListener = listener;
+        if (mInitialLockSetupService != null || mCurrentlyBinding) {
+            logVerbose("Unable to bind to initial setup service, already connected or connecting.");
+            return;
+        }
+        // Reset whether this has timed out.
+        mTimedOut = false;
+        try {
+            mHandler.postDelayed(mTimeoutRunnable, CONNECTION_TIMEOUT_MS);
+            mCurrentlyBinding = mContext.bindService(INITIAL_LOCK_SERVICE_INTENT, /* connection= */
+                    this,
+                    Context.BIND_AUTO_CREATE);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            mInitialLockListener.onConnectionAttemptFinished(false);
+        }
+        if (!mCurrentlyBinding) {
+            logVerbose("Unable to bind to initial setup service");
+        }
+    }
+
+    /**
+     * Stops the connection and removes the active listener. Does nothing if not connected.
+     */
+    public void stopConnection() {
+        logVerbose("stopConnection");
+        if (mInitialLockSetupService == null) {
+            logVerbose("Attempting to disconnect from service when not connected");
+            return;
+        }
+        mContext.unbindService(this);
+        mInitialLockSetupService = null;
+        mInitialLockListener = null;
+        mCurrentlyBinding = false;
+    }
+
+    /**
+     * Returns whether the client is currently connected to the service.
+     */
+    public boolean isServiceConnected() {
+        return false;
+    }
+
+    /**
+     * Fetches the set of {@link LockConfig}s that define the lock constraints for the device.
+     */
+    public void getLockConfigs() {
+        LockConfigsAsyncTask lockConfigsAsyncTask = new LockConfigsAsyncTask(mInitialLockListener,
+                mInitialLockSetupService);
+        lockConfigsAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
+    }
+
+    /**
+     * Returns whether the current password fits the Car Settings password
+     * criteria. Otherwise returns the related error code (length, character
+     * types, etc).
+     */
+    public void checkValidPassword(byte[] password) {
+        logVerbose("checkValidPassword");
+        if (mCurrentValidateLockTask != null
+                && mCurrentValidateLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            mCurrentValidateLockTask.cancel(true);
+        }
+        mCurrentValidateLockTask = new ValidateLockAsyncTask(
+                mInitialLockListener, mInitialLockSetupService, LockTypes.PASSWORD);
+        mCurrentValidateLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, password);
+    }
+
+    /**
+     * Returns whether the current PIN fits the Car Settings PIN criteria.
+     * Otherwise returns the related error code (length, character types, etc).
+     */
+    public void checkValidPin(byte[] pin) {
+        logVerbose("checkValidPin");
+        if (mCurrentValidateLockTask != null
+                && mCurrentValidateLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            mCurrentValidateLockTask.cancel(true);
+        }
+        mCurrentValidateLockTask = new ValidateLockAsyncTask(
+                mInitialLockListener, mInitialLockSetupService, LockTypes.PIN);
+        mCurrentValidateLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pin);
+    }
+
+    /**
+     * Returns whether the current pattern fits the Car Settings pattern
+     * criteria. Otherwise returns the related error code.
+     */
+    public void checkValidPattern(byte[] pattern) {
+        logVerbose("checkValidPattern");
+        if (mCurrentValidateLockTask != null
+                && mCurrentValidateLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            mCurrentValidateLockTask.cancel(true);
+        }
+        mCurrentValidateLockTask = new ValidateLockAsyncTask(
+                mInitialLockListener, mInitialLockSetupService, LockTypes.PATTERN);
+        mCurrentValidateLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pattern);
+    }
+
+    /**
+     * Calls to the service to set the given password as the initial device
+     * password.
+     */
+    public void saveLockPassword(byte[] password) {
+        if (mCurrentSaveLockTask != null
+                && mCurrentSaveLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            // If a save operation is already started, ignore this. Should not try to save
+            // multiple locks at once.
+            Log.e(TAG, "Can't save multiple passwords at once");
+            return;
+        }
+        mCurrentSaveLockTask = new SaveLockAsyncTask(mInitialLockListener,
+                mInitialLockSetupService, LockTypes.PASSWORD);
+        mCurrentSaveLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, password);
+    }
+
+    /**
+     * Calls to the service to set the given PIN as the initial device PIN.
+     */
+    public void saveLockPin(byte[] pin) {
+        if (mCurrentSaveLockTask != null
+                && mCurrentSaveLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            // If a save operation is already started, ignore this. Should not try to save
+            // multiple locks at once.
+            Log.e(TAG, "Can't save multiple passwords at once");
+            return;
+        }
+        mCurrentSaveLockTask = new SaveLockAsyncTask(mInitialLockListener,
+                mInitialLockSetupService, LockTypes.PIN);
+        mCurrentSaveLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pin);
+    }
+
+    /**
+     * Calls the service to set the given pattern as the initial device lock
+     * pattern.
+     */
+    public void saveLockPattern(byte[] pattern) {
+        if (mCurrentSaveLockTask != null
+                && mCurrentSaveLockTask.getStatus() != AsyncTask.Status.FINISHED) {
+            // If a save operation is already started, ignore this. Should not try to save
+            // multiple locks at once.
+            Log.e(TAG, "Can't save multiple passwords at once");
+            return;
+        }
+        mCurrentSaveLockTask = new SaveLockAsyncTask(mInitialLockListener,
+                mInitialLockSetupService, LockTypes.PATTERN);
+        mCurrentSaveLockTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pattern);
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        logVerbose("onServiceConnected");
+        if (mTimedOut) {
+            return;
+        }
+        mHandler.removeCallbacks(mTimeoutRunnable);
+        mCurrentlyBinding = false;
+        mInitialLockSetupService = IInitialLockSetupService.Stub.asInterface(service);
+        if (mInitialLockListener != null) {
+            mInitialLockListener.onConnectionAttemptFinished(true);
+        }
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        logVerbose("onServiceDisconnected");
+        mCurrentlyBinding = false;
+        mInitialLockSetupService = null;
+    }
+
+    @Override
+    public void onNullBinding(ComponentName name) {
+        logVerbose("onNullBinding");
+        if (mTimedOut) {
+            return;
+        }
+        mHandler.removeCallbacks(mTimeoutRunnable);
+        mCurrentlyBinding = false;
+        if (mInitialLockListener != null) {
+            mInitialLockListener.onConnectionAttemptFinished(false);
+        }
+    }
+
+    @Override
+    public void onBindingDied(ComponentName name) {
+        logVerbose("onBindingDied");
+        if (mTimedOut) {
+            return;
+        }
+        mHandler.removeCallbacks(mTimeoutRunnable);
+        mCurrentlyBinding = false;
+        if (mInitialLockListener != null) {
+            mInitialLockListener.onConnectionAttemptFinished(false);
+        }
+    }
+
+    private void connectionTimeout() {
+        logVerbose("connectionTimeout");
+        if (mInitialLockListener == null) {
+            return;
+        }
+        mInitialLockListener.onConnectionAttemptFinished(false);
+        mTimedOut = true;
+    }
+
+    private static class LockConfigsAsyncTask extends
+            AsyncTask<Void, Void, Map<Integer, LockConfig>> {
+
+        private WeakReference<InitialLockListener> mInitialLockListener;
+        private WeakReference<IInitialLockSetupService> mInitialLockSetupService;
+
+        LockConfigsAsyncTask(InitialLockListener initialLockListener,
+                IInitialLockSetupService initialLockSetupService) {
+            mInitialLockListener = new WeakReference<>(initialLockListener);
+            mInitialLockSetupService = new WeakReference<>(initialLockSetupService);
+        }
+
+        @Override
+        protected Map<Integer, LockConfig> doInBackground(Void... voids) {
+            IInitialLockSetupService initialLockSetupService = mInitialLockSetupService.get();
+            if (initialLockSetupService == null) {
+                InitialLockSetupClient.logVerbose(
+                        "Lost reference to service in LockConfigsAsyncTask");
+                return null;
+            }
+            LockConfig passwordConfig, pinConfig, patternConfig;
+            try {
+                passwordConfig = initialLockSetupService.getLockConfig(LockTypes.PASSWORD);
+                pinConfig = initialLockSetupService.getLockConfig(LockTypes.PIN);
+                patternConfig = initialLockSetupService.getLockConfig(LockTypes.PATTERN);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+                return null;
+            }
+            Map<Integer, LockConfig> map = new HashMap<>();
+            map.put(LockTypes.PASSWORD, passwordConfig);
+            map.put(LockTypes.PIN, pinConfig);
+            map.put(LockTypes.PATTERN, patternConfig);
+            return map;
+        }
+
+        @Override
+        protected void onPostExecute(Map<Integer, LockConfig> map) {
+            InitialLockListener listener = mInitialLockListener.get();
+            if (listener == null) {
+                return;
+            }
+            listener.onGetLockConfigs(map);
+        }
+    }
+
+    private static class ValidateLockAsyncTask extends AsyncTask<byte[], Void, Integer> {
+
+        private WeakReference<InitialLockListener> mInitialLockListener;
+        private WeakReference<IInitialLockSetupService> mInitialLockSetupService;
+        private int mLockType;
+
+        ValidateLockAsyncTask(
+                InitialLockListener initialLockListener,
+                IInitialLockSetupService initialLockSetupService,
+                @LockTypes int lockType) {
+            mInitialLockListener = new WeakReference<>(initialLockListener);
+            mInitialLockSetupService = new WeakReference<>(initialLockSetupService);
+            mLockType = lockType;
+        }
+
+        @Override
+        protected Integer doInBackground(byte[]... passwords) {
+            InitialLockSetupClient.logVerbose("ValidateLockAsyncTask doInBackground");
+            IInitialLockSetupService initialLockSetupService = mInitialLockSetupService.get();
+            if (initialLockSetupService == null) {
+                InitialLockSetupClient.logVerbose(
+                        "Lost reference to service in ValidateLockAsyncTask");
+                return ValidateLockFlags.INVALID_GENERIC;
+            }
+            try {
+                int output = initialLockSetupService.checkValidLock(mLockType, passwords[0]);
+                return output;
+            } catch (RemoteException e) {
+                e.printStackTrace();
+                return ValidateLockFlags.INVALID_GENERIC;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Integer resultCode) {
+            if (isCancelled()) {
+                return;
+            }
+            InitialLockListener listener = mInitialLockListener.get();
+            if (listener == null) {
+                return;
+            }
+            listener.onLockValidated(resultCode);
+        }
+    }
+
+    private static class SaveLockAsyncTask extends AsyncTask<byte[], Void, Integer> {
+
+        private WeakReference<InitialLockListener> mInitialLockListener;
+        private WeakReference<IInitialLockSetupService> mInitialLockSetupService;
+        private int mLockType;
+
+        SaveLockAsyncTask(
+                InitialLockListener initialLockListener,
+                IInitialLockSetupService initialLockSetupService,
+                @LockTypes int lockType) {
+            mInitialLockListener = new WeakReference<>(initialLockListener);
+            mInitialLockSetupService = new WeakReference<>(initialLockSetupService);
+            mLockType = lockType;
+        }
+
+        @Override
+        protected Integer doInBackground(byte[]... passwords) {
+            InitialLockSetupClient.logVerbose("SaveLockAsyncTask doInBackground");
+            IInitialLockSetupService initialLockSetupService = mInitialLockSetupService.get();
+            if (initialLockSetupService == null) {
+                InitialLockSetupClient.logVerbose(
+                        "Lost reference to service in SaveLockAsyncTask");
+                return SetLockCodes.FAIL_LOCK_GENERIC;
+            }
+            try {
+                int output = initialLockSetupService.setLock(mLockType, passwords[0]);
+                return output;
+            } catch (RemoteException e) {
+                e.printStackTrace();
+                return SetLockCodes.FAIL_LOCK_GENERIC;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Integer resultCode) {
+            InitialLockListener listener = mInitialLockListener.get();
+            if (listener == null) {
+                return;
+            }
+            listener.onSetLockFinished(resultCode);
+        }
+    }
+
+    private static void logVerbose(String logStatement) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, logStatement);
+        }
+    }
+}
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupConstants.java b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupConstants.java
new file mode 100644
index 0000000..7081e78
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupConstants.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import android.annotation.IntDef;
+
+/**
+ * Defines the constants used for the communication between the client and service in setting
+ * the initial lock.
+ */
+public interface InitialLockSetupConstants {
+
+    /**
+     * The library version. All relevant changes should bump this version number and ensure
+     * all relevant parts of the interface handle backwards compatibility.
+     */
+    int LIBRARY_VERSION = 1;
+
+    /**
+     * Lock types supported by the InitialLockSetupService.
+     */
+    @IntDef({
+            LockTypes.PASSWORD,
+            LockTypes.PIN,
+            LockTypes.PATTERN
+    })
+    @interface LockTypes {
+        int PASSWORD = 0;
+        int PIN = 1;
+        int PATTERN = 2;
+    }
+
+    /**
+     * Result codes from validating a lock. No flags (0) indicates success.
+     */
+    @IntDef(flag = true, value = {
+            ValidateLockFlags.INVALID_LENGTH,
+            ValidateLockFlags.INVALID_BAD_SYMBOLS,
+            ValidateLockFlags.INVALID_LACKS_COMPLEXITY,
+            ValidateLockFlags.INVALID_GENERIC
+    })
+    @interface ValidateLockFlags {
+        int INVALID_LENGTH = 1 << 0;
+        int INVALID_BAD_SYMBOLS = 1 << 1;
+        int INVALID_LACKS_COMPLEXITY = 1 << 2;
+        int INVALID_GENERIC = 1 << 3;
+    }
+
+    /**
+     * Result codes from attempting to set a lock.
+     */
+    @IntDef({
+            SetLockCodes.SUCCESS,
+            SetLockCodes.FAIL_LOCK_EXISTS,
+            SetLockCodes.FAIL_LOCK_INVALID,
+            SetLockCodes.FAIL_LOCK_GENERIC
+    })
+    @interface SetLockCodes {
+        int SUCCESS = 1;
+        int FAIL_LOCK_EXISTS = -1;
+        int FAIL_LOCK_INVALID = -2;
+        int FAIL_LOCK_GENERIC = -3;
+    }
+}
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupHelper.java b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupHelper.java
new file mode 100644
index 0000000..9820680
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupHelper.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import com.android.car.setupwizardlib.InitialLockSetupConstants.ValidateLockFlags;
+
+/**
+ * Provides helper methods for the usage of the InitialLockSetupService.
+ */
+public class InitialLockSetupHelper {
+
+    /**
+     * Checks the return flags from a valid lock check and returns true if the lock is valid.
+     */
+    public static boolean isValidLockResultCode(@ValidateLockFlags int flags) {
+        return flags == 0;
+    }
+
+    /**
+     * Gets the byte representation of a pattern cell based on 0 indexed row and column.
+     * This should be a 3x3 pattern format.
+     */
+    public static byte getByteFromPatternCell(int row, int col) {
+        return (byte) ((row * 3) + col + 1);
+    }
+
+    /**
+     * Returns the 0 indexed row of the pattern cell from a serialized byte pattern cell.
+     * This should be a 3x3 pattern format.
+     */
+    public static int getPatternCellRowFromByte(byte cell) {
+        return (byte) ((cell - 1) / 3);
+    }
+
+    /**
+     * Returns the 0 indexed column of the pattern cell from a serialized byte pattern cell.
+     */
+    public static int getPatternCellColumnFromByte(byte cell) {
+        return (byte) ((cell - 1) % 3);
+    }
+
+    /**
+     * Converts a {@link CharSequence} into an array of bytes. This is for security reasons to avoid
+     * storing strings in memory.
+     */
+    public static byte[] charSequenceToByteArray(CharSequence chars) {
+        if (chars == null) {
+            return null;
+        }
+        byte[] byteArray = new byte[chars.length()];
+        for (int i = 0; i < chars.length(); i++) {
+            byteArray[i] = (byte) chars.charAt(i);
+        }
+        return byteArray;
+    }
+
+    /**
+     * Converts an array of bytes into a {@link CharSequence}.
+     */
+    public static CharSequence byteArrayToCharSequence(byte[] input) {
+        if (input == null) {
+            return null;
+        }
+        StringBuffer charSequence = new StringBuffer();
+        for (int i = 0; i < input.length; i++) {
+            charSequence.append((char) input[i]);
+        }
+        return charSequence;
+    }
+}
diff --git a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java b/library/utils/src/com/android/car/setupwizardlib/LockConfig.aidl
similarity index 64%
rename from library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java
rename to library/utils/src/com/android/car/setupwizardlib/LockConfig.aidl
index a0bc9ba..a4effbe 100644
--- a/library/tests/robotests/src/com/android/car/setupwizardlib/robolectric/TestConfig.java
+++ b/library/utils/src/com/android/car/setupwizardlib/LockConfig.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.car.setupwizardlib.robolectric;
+package com.android.car.setupwizardlib;
 
-public class TestConfig {
-    public static final int SDK_VERSION = 26;
-    public static final String MANIFEST_PATH =
-            "frameworks/opt/car/setupwizard/library/AndroidManifest.xml";
-}
+parcelable LockConfig;
+
diff --git a/library/utils/src/com/android/car/setupwizardlib/LockConfig.java b/library/utils/src/com/android/car/setupwizardlib/LockConfig.java
new file mode 100644
index 0000000..085c5ae
--- /dev/null
+++ b/library/utils/src/com/android/car/setupwizardlib/LockConfig.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.android.car.setupwizardlib;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encompasses the information about a lock type's configuration.
+ */
+public class LockConfig implements Parcelable {
+
+    /**
+     * Builder object for creating lock config from parcel.
+     */
+    public static final Parcelable.Creator<LockConfig> CREATOR =
+            new Parcelable.Creator<LockConfig>() {
+
+                @Override
+                public LockConfig createFromParcel(Parcel source) {
+                    return new LockConfig(source);
+                }
+
+                @Override
+                public LockConfig[] newArray(int size) {
+                    return new LockConfig[size];
+                }
+            };
+
+    /**
+     * Whether this lock type is enabled on the device.
+     */
+    public boolean enabled;
+    /**
+     * The minimum length for the lock, this is displayed to the user so the UI must know about it.
+     */
+    public int minLockLength;
+
+    /**
+     * Creates a {@link LockConfig} from a {@link Parcel}.
+     */
+    public LockConfig(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Creates a {@link LockConfig} from the state passed in.
+     */
+    public LockConfig(boolean enabled, int minLockLength) {
+        this.enabled = enabled;
+        this.minLockLength = minLockLength;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(minLockLength);
+    }
+
+    private void readFromParcel(Parcel in) {
+        enabled = in.readInt() != 0;
+        minLockLength = in.readInt();
+    }
+}
+