Merge "New UX for app usage screen"
diff --git a/res/drawable-sw600dp/setup_illustration.xml b/res/drawable-sw600dp/setup_illustration.xml
deleted file mode 100644
index c0e54e2..0000000
--- a/res/drawable-sw600dp/setup_illustration.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-    android:autoMirrored="true">
-
-    <item>
-        <bitmap android:src="@drawable/setup_illustration_horizontal_tile"
-            android:gravity="top"
-            android:tileModeX="repeat"
-            android:tileModeY="disabled" />
-    </item>
-
-    <item android:id="@+id/illustration_image">
-        <bitmap android:src="@drawable/setup_illustration_wifi"
-            android:gravity="top|start" />
-    </item>
-
-</layer-list>
diff --git a/res/drawable/setup_illustration.xml b/res/drawable/setup_illustration.xml
deleted file mode 100644
index f8d96d5..0000000
--- a/res/drawable/setup_illustration.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-    android:autoMirrored="true">
-
-    <item android:id="@+id/illustration_image">
-        <bitmap android:src="@drawable/setup_illustration_wifi"
-            android:gravity="fill" />
-    </item>
-
-</layer-list>
diff --git a/res/drawable/setup_illustration_bg.xml b/res/drawable/setup_illustration_bg.xml
deleted file mode 100644
index 0a229c7..0000000
--- a/res/drawable/setup_illustration_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/setup_illustration_tile"
-    android:tileMode="repeat" />
diff --git a/res/drawable/setup_wizard_card_bg.xml b/res/drawable/setup_wizard_card_bg.xml
deleted file mode 100644
index 8621437..0000000
--- a/res/drawable/setup_wizard_card_bg.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-
-    <corners
-        android:topLeftRadius="@dimen/setup_wizard_card_corner_radius"
-        android:topRightRadius="@dimen/setup_wizard_card_corner_radius" />
-
-    <solid android:color="?android:attr/colorBackground" />
-
-</shape>
-
diff --git a/res/layout-land/setup_preference.xml b/res/layout-land/setup_preference.xml
deleted file mode 100644
index bee46ed..0000000
--- a/res/layout-land/setup_preference.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (c) 2014 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/fragment"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <FrameLayout
-        android:id="@+id/title_area"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@drawable/setup_illustration_bg"
-        android:elevation="@dimen/setup_wizard_title_area_elevation">
-
-        <TextView
-            android:id="@+id/title"
-            style="@style/SetupTitle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/wifi_setup_wizard_title" />
-
-    </FrameLayout>
-
-    <ListView
-        android:id="@android:id/list"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:cacheColorHint="@android:color/transparent"
-        android:clipToPadding="false"
-        android:drawSelectorOnTop="false"
-        android:headerDividersEnabled="false"
-        android:scrollbarAlwaysDrawVerticalTrack="true" />
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-land/setup_template.xml b/res/layout-land/setup_template.xml
deleted file mode 100644
index cc3402f..0000000
--- a/res/layout-land/setup_template.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <RelativeLayout
-        android:layout_weight="1"
-        android:layout_width="match_parent"
-        android:layout_height="0dp">
-
-        <FrameLayout
-            android:id="@+id/title_area"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:background="@drawable/setup_illustration_bg"
-            android:elevation="@dimen/setup_wizard_title_area_elevation">
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/wifi_setup_title"
-                style="@style/SetupTitle"/>
-        </FrameLayout>
-
-        <ScrollView
-            android:id="@+id/bottom_scroll_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/title_area"
-            android:layout_alignParentBottom="true"
-            android:fillViewport="true">
-
-            <FrameLayout android:id="@+id/setup_content"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:clipChildren="false"/>
-
-        </ScrollView>
-
-    </RelativeLayout>
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        android:layout_width="match_parent"
-        android:layout_height="56dip"
-        style="@style/setup_wizard_navbar_style"/>
-</LinearLayout>
-
diff --git a/res/layout-sw600dp-land/setup_preference.xml b/res/layout-sw600dp-land/setup_preference.xml
deleted file mode 100644
index 883d1de..0000000
--- a/res/layout-sw600dp-land/setup_preference.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (c) 2014 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/fragment"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <com.android.settings.widget.SetupWizardIllustration
-        android:id="@+id/setup_illustration"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:foreground="@drawable/setup_illustration"
-        android:background="@drawable/setup_illustration_bg">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:gravity="start|top"
-            android:weightSum="16">
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/SetupCardTitle"
-                android:layout_width="1dp"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
-                android:layout_weight="6"
-                android:text="@string/wifi_setup_wizard_title"/>
-
-            <LinearLayout
-                android:layout_width="1dp"
-                android:layout_height="match_parent"
-                android:layout_marginTop="@dimen/setup_wizard_card_land_margin_top"
-                android:layout_weight="8"
-                android:background="@drawable/setup_wizard_card_bg"
-                android:elevation="@dimen/setup_wizard_card_elevation"
-                android:orientation="vertical">
-
-                <ListView
-                    android:id="@android:id/list"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
-                    android:cacheColorHint="@android:color/transparent"
-                    android:clipToPadding="false"
-                    android:drawSelectorOnTop="false"
-                    android:headerDividersEnabled="false"
-                    android:scrollbarAlwaysDrawVerticalTrack="true" />
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-    </com.android.settings.widget.SetupWizardIllustration>
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp-land/setup_template.xml b/res/layout-sw600dp-land/setup_template.xml
deleted file mode 100644
index a58dd3d..0000000
--- a/res/layout-sw600dp-land/setup_template.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <com.android.settings.widget.SetupWizardIllustration
-        android:id="@+id/setup_illustration"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:background="@drawable/setup_illustration_bg"
-        android:foreground="@drawable/setup_illustration">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:gravity="start|top"
-            android:weightSum="16">
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
-                android:layout_weight="6"
-                android:text="@string/wifi_setup_title"
-                style="@style/SetupCardTitle"/>
-
-            <ScrollView
-                android:id="@+id/bottom_scroll_view"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_marginTop="@dimen/setup_wizard_card_land_margin_top"
-                android:layout_weight="8"
-                android:background="@drawable/setup_wizard_card_bg"
-                android:elevation="@dimen/setup_wizard_card_elevation"
-                android:fillViewport="true">
-
-                <FrameLayout android:id="@+id/setup_content"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:clipChildren="false"/>
-
-            </ScrollView>
-
-        </LinearLayout>
-
-    </com.android.settings.widget.SetupWizardIllustration>
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        android:layout_width="match_parent"
-        android:layout_height="56dip"
-        style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp/setup_preference.xml b/res/layout-sw600dp/setup_preference.xml
deleted file mode 100644
index cc12003..0000000
--- a/res/layout-sw600dp/setup_preference.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 Google Inc.
-
-    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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <com.android.settings.widget.SetupWizardIllustration
-        android:id="@+id/setup_illustration"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:background="@drawable/setup_illustration_bg"
-        android:foreground="@drawable/setup_illustration">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
-            android:orientation="vertical"
-            android:paddingLeft="@dimen/setup_wizard_card_port_margin_sides"
-            android:paddingRight="@dimen/setup_wizard_card_port_margin_sides">
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/SetupCardTitle"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/wifi_setup_wizard_title" />
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
-                android:background="@drawable/setup_wizard_card_bg"
-                android:elevation="@dimen/setup_wizard_card_elevation"
-                android:orientation="vertical">
-
-                <ListView
-                    android:id="@android:id/list"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
-                    android:cacheColorHint="@android:color/transparent"
-                    android:clipToPadding="false"
-                    android:drawSelectorOnTop="false"
-                    android:headerDividersEnabled="false"
-                    android:scrollbarAlwaysDrawVerticalTrack="true" />
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-    </com.android.settings.widget.SetupWizardIllustration>
-
-    <fragment
-        android:id="@+id/navigation_bar"
-        android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp/setup_template.xml b/res/layout-sw600dp/setup_template.xml
deleted file mode 100644
index 297e9fb..0000000
--- a/res/layout-sw600dp/setup_template.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <com.android.settings.widget.SetupWizardIllustration
-        android:id="@+id/setup_illustration"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:background="@drawable/setup_illustration_bg"
-        android:foreground="@drawable/setup_illustration">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
-            android:orientation="vertical"
-            android:paddingStart="@dimen/setup_wizard_card_port_margin_sides"
-            android:paddingEnd="@dimen/setup_wizard_card_port_margin_sides">
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/wifi_setup_title"
-                style="@style/SetupCardTitle"/>
-
-            <ScrollView
-                android:id="@+id/bottom_scroll_view"
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
-                android:background="@drawable/setup_wizard_card_bg"
-                android:elevation="@dimen/setup_wizard_card_elevation"
-                android:fillViewport="true">
-
-                <FrameLayout
-                    android:id="@+id/setup_content"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:clipChildren="false"/>
-
-            </ScrollView>
-
-        </LinearLayout>
-
-    </com.android.settings.widget.SetupWizardIllustration>
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        android:layout_width="match_parent"
-        android:layout_height="56dip"
-        style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout/preference_vpn.xml b/res/layout/preference_vpn.xml
new file mode 100644
index 0000000..95d3253
--- /dev/null
+++ b/res/layout/preference_vpn.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+    <View
+        android:id="@+id/divider_manage"
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:layout_marginTop="5dip"
+        android:layout_marginBottom="5dip"
+        android:background="@android:drawable/divider_horizontal_dark" />
+    <ImageView
+        android:id="@+id/manage"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:paddingStart="16dip"
+        android:paddingEnd="16dip"
+        android:src="@drawable/ic_sysbar_quicksettings"
+        android:contentDescription="@string/settings_label"
+        android:layout_gravity="center"
+        android:background="?android:attr/selectableItemBackground" />
+</LinearLayout>
diff --git a/res/layout/setup_choose_lock_generic.xml b/res/layout/setup_choose_lock_generic.xml
new file mode 100644
index 0000000..3b3b5e7
--- /dev/null
+++ b/res/layout/setup_choose_lock_generic.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2015, 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.
+-->
+
+<com.android.setupwizardlib.SetupWizardListLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+    settings:suwHeaderText="@string/wifi_setup_wizard_title"
+    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+    settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen" />
diff --git a/res/layout/setup_choose_lock_password.xml b/res/layout/setup_choose_lock_password.xml
index 59fa523..3ec5241 100644
--- a/res/layout/setup_choose_lock_password.xml
+++ b/res/layout/setup_choose_lock_password.xml
@@ -15,70 +15,82 @@
     limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.setupwizardlib.SetupWizardLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal"
-    android:orientation="vertical">
-
-    <!-- header text ('Enter Pin') -->
-    <TextView android:id="@+id/headerText"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:lines="2"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    <!-- Password entry field -->
-    <EditText android:id="@+id/password_entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_marginStart="30dip"
-        android:layout_marginEnd="30dip"
-        android:gravity="center"
-        android:inputType="textPassword"
-        android:imeOptions="actionNext|flagNoExtractUi"
-        android:textSize="24sp"
-        style="@style/TextAppearance.PasswordEntry"/>
-
-    <!-- Spacer between password entry and keyboard -->
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"/>
+    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+    settings:suwHeaderText="@string/wifi_setup_wizard_title"
+    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+    settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:visibility="gone"
-        style="@style/SecurityPreferenceButtonContainer">
+        android:layout_height="match_parent"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
 
-        <!-- left : cancel -->
-        <Button android:id="@+id/cancel_button"
-            android:layout_width="0dip"
+        <!-- header text ('Enter Pin') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/lockpassword_cancel_label"
-            style="@style/SecurityPreferenceButton"/>
+            android:gravity="center"
+            android:lines="2"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
 
-        <!-- right : continue -->
-        <Button android:id="@+id/next_button"
-            android:layout_width="0dip"
+        <!-- Password entry field -->
+        <EditText android:id="@+id/password_entry"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/lockpassword_continue_label"
-            style="@style/SecurityPreferenceButton"/>
+            android:layout_gravity="center"
+            android:layout_marginStart="30dip"
+            android:layout_marginEnd="30dip"
+            android:gravity="center"
+            android:inputType="textPassword"
+            android:imeOptions="actionNext|flagNoExtractUi"
+            android:textSize="24sp"
+            style="@style/TextAppearance.PasswordEntry"/>
+
+        <!-- Spacer between password entry and keyboard -->
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:visibility="gone"
+            style="@style/SecurityPreferenceButtonContainer">
+
+            <!-- left : cancel -->
+            <Button android:id="@+id/cancel_button"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/lockpassword_cancel_label"
+                style="@style/SecurityPreferenceButton"/>
+
+            <!-- right : continue -->
+            <Button android:id="@+id/next_button"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/lockpassword_continue_label"
+                style="@style/SecurityPreferenceButton"/>
+
+        </LinearLayout>
+
+        <!-- Alphanumeric keyboard -->
+        <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="#00000000"
+            android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
+            android:visibility="gone"/>
 
     </LinearLayout>
 
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-        android:visibility="gone"/>
-
-</LinearLayout>
+</com.android.setupwizardlib.SetupWizardLayout>
diff --git a/res/layout/setup_choose_lock_pattern.xml b/res/layout/setup_choose_lock_pattern.xml
index a906826..00c7018 100644
--- a/res/layout/setup_choose_lock_pattern.xml
+++ b/res/layout/setup_choose_lock_pattern.xml
@@ -15,80 +15,91 @@
     limitations under the License.
 -->
 
-<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+<com.android.setupwizardlib.SetupWizardLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/topLayout"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+    settings:suwHeaderText="@string/wifi_setup_wizard_title"
+    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+    settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
 
-    <!-- takes up all space above button bar at bottom -->
-    <LinearLayout
+    <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+        android:id="@+id/topLayout"
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"
-        android:gravity="center"
+        android:layout_height="match_parent"
         android:orientation="vertical">
 
-        <TextView android:id="@+id/headerText"
+        <!-- takes up all space above button bar at bottom -->
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="0dip"
             android:layout_weight="1"
             android:gravity="center"
-            android:minHeight="50dp"
-            android:textSize="18sp"/>
+            android:orientation="vertical">
 
-        <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+            <TextView android:id="@+id/headerText"
+                android:layout_width="match_parent"
+                android:layout_height="0dip"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:minHeight="50dip"
+                android:textSize="18sp"/>
+
+            <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+                android:layout_width="match_parent"
+                android:layout_height="0dip"
+                android:layout_weight="4"
+                android:background="@color/lock_pattern_background"/>
+
+        </LinearLayout>
+
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="4"
-            android:background="@color/lock_pattern_background"/>
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:orientation="horizontal">
-
-        <Button android:id="@+id/retryButton"
-            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/lockpattern_retry_button_text"/>
+            android:gravity="center_horizontal"
+            android:orientation="horizontal">
 
-        <TextView android:id="@+id/footerText"
-            android:layout_width="wrap_content"
+            <Button android:id="@+id/retryButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/lockpattern_retry_button_text"/>
+
+            <TextView android:id="@+id/footerText"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minHeight="50dip"
+                android:textSize="14sp"/>
+
+        </LinearLayout>
+
+        <!-- Buttons are hidden during setup, and use the buttons in setup navigation bar instead -->
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="50dp"
-            android:textSize="14sp"/>
+            android:orientation="horizontal"
+            android:visibility="gone"
+            style="@style/SecurityPreferenceButtonContainer">
 
-    </LinearLayout>
+            <!-- left : cancel, or re-try -->
+            <Button android:id="@+id/footerLeftButton"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/lockpattern_tutorial_cancel_label"
+                style="@style/SecurityPreferenceButton"/>
 
-    <!-- Buttons are hidden during setup, and use the buttons in setup navigation bar instead -->
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:visibility="gone"
-        style="@style/SecurityPreferenceButtonContainer">
+            <!-- right : confirm or ok -->
+            <Button android:id="@+id/footerRightButton"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/lockpattern_tutorial_continue_label"
+                style="@style/SecurityPreferenceButton"/>
 
-        <!-- left : cancel, or re-try -->
-        <Button android:id="@+id/footerLeftButton"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/lockpattern_tutorial_cancel_label"
-            style="@style/SecurityPreferenceButton"/>
+        </LinearLayout>
 
-        <!-- right : confirm or ok -->
-        <Button android:id="@+id/footerRightButton"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/lockpattern_tutorial_continue_label"
-            style="@style/SecurityPreferenceButton"/>
+    </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
 
-    </LinearLayout>
-
-</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+</com.android.setupwizardlib.SetupWizardLayout>
diff --git a/res/layout/setup_preference.xml b/res/layout/setup_preference.xml
deleted file mode 100644
index d07d4a3..0000000
--- a/res/layout/setup_preference.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/fragment"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <com.android.settings.widget.StickyHeaderListView
-        android:id="@android:id/list"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:cacheColorHint="@android:color/transparent"
-        android:clipToPadding="false"
-        android:drawSelectorOnTop="false"
-        android:headerDividersEnabled="false"
-        android:scrollbarAlwaysDrawVerticalTrack="true" />
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
diff --git a/res/layout/setup_screen_lock_fingerprint_details.xml b/res/layout/setup_screen_lock_fingerprint_details.xml
index 988468f..06ca778 100644
--- a/res/layout/setup_screen_lock_fingerprint_details.xml
+++ b/res/layout/setup_screen_lock_fingerprint_details.xml
@@ -20,6 +20,6 @@
     android:layout_height="wrap_content"
     android:paddingTop="6dip"
     android:paddingBottom="6dip"
-    android:paddingStart="@dimen/setup_wizard_margin_sides"
-    android:paddingEnd="@dimen/setup_wizard_margin_sides"
+    android:paddingStart="@dimen/suw_layout_margin_sides"
+    android:paddingEnd="@dimen/suw_layout_margin_sides"
     android:text="@string/unlock_setup_wizard_fingerprint_details" />
diff --git a/res/layout/setup_template.xml b/res/layout/setup_template.xml
deleted file mode 100644
index 0623e25..0000000
--- a/res/layout/setup_template.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <ScrollView
-        android:id="@+id/bottom_scroll_view"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:fillViewport="true">
-
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:clipChildren="false">
-
-            <com.android.settings.widget.SetupWizardIllustration
-                android:id="@+id/setup_illustration"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:elevation="@dimen/setup_wizard_title_area_elevation"
-                android:background="@drawable/setup_illustration_bg"
-                android:foreground="@drawable/setup_illustration"
-                android:tag="stickyContainer"
-                settings:aspectRatio="2.22">
-
-                <TextView
-                    android:id="@+id/title"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:tag="sticky"
-                    android:text="@string/wifi_setup_title"
-                    style="@style/SetupTitle"/>
-
-            </com.android.settings.widget.SetupWizardIllustration>
-
-            <FrameLayout
-                android:id="@+id/setup_content"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/setup_illustration"
-                android:clipChildren="false"/>
-
-        </RelativeLayout>
-
-    </ScrollView>
-
-    <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
-        android:id="@+id/navigation_bar"
-        android:layout_width="match_parent"
-        android:layout_height="56dip"
-        style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout/setup_wifi_empty.xml b/res/layout/setup_wifi_empty.xml
index 52864f9..b1659d7 100644
--- a/res/layout/setup_wifi_empty.xml
+++ b/res/layout/setup_wifi_empty.xml
@@ -19,6 +19,6 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingTop="@dimen/setup_wizard_margin_top"
-    android:paddingStart="@dimen/setup_wizard_margin_sides"
-    android:paddingEnd="@dimen/setup_wizard_margin_sides"
+    android:paddingStart="@dimen/suw_layout_margin_sides"
+    android:paddingEnd="@dimen/suw_layout_margin_sides"
     android:textAppearance="@style/TextAppearance.SetupWizardDescription" />
diff --git a/res/layout/setup_wifi_layout.xml b/res/layout/setup_wifi_layout.xml
new file mode 100644
index 0000000..62bb6a1
--- /dev/null
+++ b/res/layout/setup_wifi_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2015, 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.
+-->
+
+<com.android.setupwizardlib.SetupWizardListLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+    settings:suwHeaderText="@string/wifi_setup_wizard_title"
+    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+    settings:suwIllustrationImage="@drawable/setup_illustration_wifi" />
diff --git a/res/layout/setup_wizard_header.xml b/res/layout/setup_wizard_header.xml
deleted file mode 100644
index b030ad1..0000000
--- a/res/layout/setup_wizard_header.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 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.
--->
-
-<com.android.settings.widget.SetupWizardIllustration
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
-    android:id="@+id/setup_illustration"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@drawable/setup_illustration_bg"
-    android:elevation="@dimen/setup_wizard_title_area_elevation"
-    android:foreground="@drawable/setup_illustration"
-    android:tag="stickyContainer"
-    settings:aspectRatio="2.22">
-
-    <TextView
-        android:id="@+id/title"
-        style="@style/SetupTitle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:tag="sticky"
-        android:text="@string/wifi_setup_wizard_title"/>
-
-</com.android.settings.widget.SetupWizardIllustration>
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 034b6bf..d7e7f95 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -20,7 +20,7 @@
     <LinearLayout android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
-            android:padding="3mm">
+            android:padding="8dp">
 
         <LinearLayout android:id="@+id/editor"
                 android:layout_width="match_parent"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 5f524af..79529e2 100755
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -45,9 +45,4 @@
     <!-- Dashboard tile image margin start / end -->
     <dimen name="dashboard_tile_image_margin_start">12dp</dimen>
 
-    <dimen name="setup_wizard_card_title_padding_end">32dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_start">56dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_top">24dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_bottom">0dp</dimen>
-
 </resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index d9521db..03809c4 100755
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -24,7 +24,6 @@
     <dimen name="content_margin_left">16dip</dimen>
     <dimen name="description_margin_top">26dip</dimen>
     <dimen name="crypt_clock_size">120sp</dimen>
-    <dimen name="setup_title_size">24sp</dimen>
     <dimen name="appwidget_min_width">325dip</dimen>
     <dimen name="appwidget_min_height">50dip</dimen>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8a58c5c..cd11c2c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -38,28 +38,10 @@
 
     <dimen name="crypt_clock_size">100sp</dimen>
 
-    <dimen name="setup_margin_bottom">0dip</dimen>
-    <dimen name="setup_title_size">24sp</dimen>
-    <dimen name="setup_title_margin_bottom">16dp</dimen>
-    <!-- This is the extra spacing required to make the leading exactly 32sp -->
-    <dimen name="setup_title_line_spacing_extra">3.67sp</dimen>
-    <dimen name="setup_title_padding_bottom">2dp</dimen>
-    <dimen name="setup_title_padding_top">16dp</dimen>
     <!-- Size of padding to give in the wifi list when there is no icon -->
     <dimen name="setup_list_no_icon_padding">56dp</dimen>
     <dimen name="setup_add_network_item_height">56dp</dimen>
-    <dimen name="setup_wizard_card_corner_radius">2dp</dimen>
-    <dimen name="setup_wizard_card_elevation">5dp</dimen>
-    <dimen name="setup_wizard_tablet_illustration_height">256dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_end">0dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_start">0dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_top">16dp</dimen>
-    <dimen name="setup_wizard_card_title_padding_bottom">28dp</dimen>
-    <dimen name="setup_wizard_card_port_margin_sides">56dp</dimen>
-    <dimen name="setup_wizard_card_land_margin_top">128dp</dimen>
-    <dimen name="setup_wizard_margin_sides">40dp</dimen>
     <dimen name="setup_wizard_margin_top">24dp</dimen>
-    <dimen name="setup_wizard_title_area_elevation">3dp</dimen>
 
     <dimen name="divider_height">3dip</dimen>
     <dimen name="divider_margin_top">6dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 080cd3e..3c8e8ce 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5237,14 +5237,24 @@
 
     <!-- Button label to cancel changing a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_cancel">Cancel</string>
+    <!-- Button label to finish editing a VPN profile. [CHAR LIMIT=40] -->
+    <string name="vpn_done">Dismiss</string>
     <!-- Button label to save a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_save">Save</string>
     <!-- Button label to connect to a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_connect">Connect</string>
     <!-- Dialog title to edit a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_edit">Edit VPN profile</string>
+    <!-- Button label to forget a VPN profile. [CHAR LIMIT=40] -->
+    <string name="vpn_forget">Forget</string>
     <!-- Dialog title to connect to a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_connect_to">Connect to <xliff:g id="profile" example="School">%s</xliff:g></string>
+    <!-- Dialog message body to disconnect from a VPN profile. -->
+    <string name="vpn_disconnect_confirm">Disconnect this VPN.</string>
+    <!-- Button label to disconnect from a VPN profile. [CHAR LIMIT=40] -->
+    <string name="vpn_disconnect">Disconnect</string>
+    <!-- Field label to show the version number for a VPN app. [CHAR LIMIT=40] -->
+    <string name="vpn_version">Version <xliff:g id="version" example="3.3.0">%s</xliff:g></string>
 
     <!-- Preference title for VPN settings. [CHAR LIMIT=40] -->
     <string name="vpn_title">VPN</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 22061e6..f9f4561 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -146,45 +146,6 @@
         <item name="android:clickable">false</item>
     </style>
 
-    <!-- We'd like to have this as 16dip hight including paddingTop/paddingBottom to
-         be consistent with ProgressBar -->
-    <style name="TopDivider">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">@dimen/divider_height</item>
-        <item name="android:background">?attr/setup_divider_color</item>
-        <item name="android:focusable">false</item>
-        <item name="android:clickable">false</item>
-        <item name="android:layout_marginTop">@dimen/divider_margin_top</item>
-        <item name="android:layout_marginBottom">@dimen/divider_margin_bottom</item>
-    </style>
-
-    <style name="SetupTitle">
-        <item name="android:layout_marginBottom">@dimen/setup_title_margin_bottom</item>
-        <item name="android:layout_marginEnd">@dimen/setup_wizard_margin_sides</item>
-        <item name="android:layout_marginStart">@dimen/setup_wizard_margin_sides</item>
-        <item name="android:clickable">false</item>
-        <item name="android:fontFamily">sans-serif</item>
-        <item name="android:lineSpacingExtra">@dimen/setup_title_line_spacing_extra</item>
-        <item name="android:longClickable">false</item>
-        <item name="android:textColor">@android:color/white</item>
-        <item name="android:textSize">@dimen/setup_title_size</item>
-        <item name="android:paddingBottom">@dimen/setup_title_padding_bottom</item>
-        <item name="android:paddingTop">@dimen/setup_title_padding_top</item>
-    </style>
-
-    <!-- Alternate title style used for some tablet layouts -->
-    <style name="SetupCardTitle">
-        <item name="android:paddingBottom">@dimen/setup_wizard_card_title_padding_bottom</item>
-        <item name="android:paddingEnd">@dimen/setup_wizard_card_title_padding_end</item>
-        <item name="android:paddingStart">@dimen/setup_wizard_card_title_padding_start</item>
-        <item name="android:paddingTop">@dimen/setup_wizard_card_title_padding_top</item>
-        <item name="android:textAppearance">@style/TextAppearance.SetupWizardCardTitle</item>
-    </style>
-
-    <style name="TextAppearance.SetupWizardCardTitle" parent="@android:style/TextAppearance.Material.Display1">
-        <item name="android:textColor">@android:color/white</item>
-    </style>
-
     <style name="TextAppearance.SetupWizardDescription" parent="@android:style/TextAppearance.Material.Subhead">
     </style>
 
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 2c900bb..a52ea04 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -31,8 +31,8 @@
 
     <style name="SetupWizardTheme" parent="SuwThemeMaterial">
         <item name="android:alertDialogTheme">@style/Theme.WifiDialog</item>
-        <item name="android:listPreferredItemPaddingEnd">@dimen/setup_wizard_margin_sides</item>
-        <item name="android:listPreferredItemPaddingStart">@dimen/setup_wizard_margin_sides</item>
+        <item name="android:listPreferredItemPaddingEnd">@dimen/suw_layout_margin_sides</item>
+        <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
         <item name="android:windowBackground">?android:attr/colorBackground</item>
         <item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
         <item name="ic_menu_add">@drawable/ic_menu_add_dark</item>
@@ -52,8 +52,8 @@
 
     <style name="SetupWizardTheme.Light" parent="SuwThemeMaterial.Light">
         <item name="android:alertDialogTheme">@style/Theme.Light.WifiDialog</item>
-        <item name="android:listPreferredItemPaddingEnd">@dimen/setup_wizard_margin_sides</item>
-        <item name="android:listPreferredItemPaddingStart">@dimen/setup_wizard_margin_sides</item>
+        <item name="android:listPreferredItemPaddingEnd">@dimen/suw_layout_margin_sides</item>
+        <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
         <item name="android:windowBackground">?android:attr/colorBackground</item>
         <item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
         <item name="ic_menu_add">@drawable/ic_menu_add_light</item>
diff --git a/src/com/android/settings/SetupChooseLockGeneric.java b/src/com/android/settings/SetupChooseLockGeneric.java
index f2da8cc..82157c0 100644
--- a/src/com/android/settings/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/SetupChooseLockGeneric.java
@@ -16,8 +16,10 @@
 
 package com.android.settings;
 
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardListLayout;
+import com.android.setupwizardlib.view.NavigationBar;
 
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
@@ -37,8 +39,7 @@
  * Other changes should be done to ChooseLockGeneric class instead and let this class inherit
  * those changes.
  */
-public class SetupChooseLockGeneric extends ChooseLockGeneric
-        implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockGeneric extends ChooseLockGeneric {
 
     @Override
     protected boolean isValidFragment(String fragmentName) {
@@ -56,33 +57,16 @@
         super.onApplyThemeResource(theme, resid, first);
     }
 
-    @Override
-    public void onNavigationBarCreated(SetupWizardNavBar bar) {
-        SetupWizardUtils.setImmersiveMode(this);
-        bar.getNextButton().setEnabled(false);
-    }
-
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-    }
-
-    public static class SetupChooseLockGenericFragment extends ChooseLockGenericFragment {
+    public static class SetupChooseLockGenericFragment extends ChooseLockGenericFragment
+            implements NavigationBar.NavigationBarListener {
 
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            final View view = inflater.inflate(R.layout.setup_preference, container, false);
-            ListView list = (ListView) view.findViewById(android.R.id.list);
-            View title = view.findViewById(R.id.title);
-            if (title == null) {
-                final View header = inflater.inflate(R.layout.setup_wizard_header, list, false);
-                list.addHeaderView(header, null, false);
-            }
+            final SetupWizardListLayout layout = (SetupWizardListLayout) inflater.inflate(
+                    R.layout.setup_choose_lock_generic, container, false);
+            layout.setHeaderText(getActivity().getTitle());
+            ListView list = layout.getListView();
             final FingerprintManager fpm = (FingerprintManager)
                     getActivity().getSystemService(Context.FINGERPRINT_SERVICE);
             if (fpm != null && fpm.isHardwareDetected()) {
@@ -90,15 +74,18 @@
                         R.layout.setup_screen_lock_fingerprint_details, list, false);
                 list.addFooterView(footer, null, false);
             }
-            return view;
+
+            final NavigationBar navigationBar = layout.getNavigationBar();
+            navigationBar.getNextButton().setEnabled(false);
+            navigationBar.setNavigationBarListener(this);
+
+            return layout;
         }
 
         @Override
         public void onViewCreated(View view, Bundle savedInstanceState) {
             super.onViewCreated(view, savedInstanceState);
-            SetupWizardUtils.setIllustration(getActivity(),
-                    R.drawable.setup_illustration_lock_screen);
-            SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+            SetupWizardUtils.setImmersiveMode(getActivity());
         }
 
         @Override
@@ -192,5 +179,17 @@
             SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
             return intent;
         }
+
+        @Override
+        public void onNavigateBack() {
+            Activity activity = getActivity();
+            if (activity != null) {
+                activity.onBackPressed();
+            }
+        }
+
+        @Override
+        public void onNavigateNext() {
+        }
     }
 }
diff --git a/src/com/android/settings/SetupChooseLockPassword.java b/src/com/android/settings/SetupChooseLockPassword.java
index 461b67c..6f384a4 100644
--- a/src/com/android/settings/SetupChooseLockPassword.java
+++ b/src/com/android/settings/SetupChooseLockPassword.java
@@ -16,9 +16,11 @@
 
 package com.android.settings;
 
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
 import com.android.setupwizardlib.util.SystemBarHelper;
+import com.android.setupwizardlib.view.NavigationBar;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
@@ -35,8 +37,7 @@
  * Other changes should be done to ChooseLockPassword class instead and let this class inherit
  * those changes.
  */
-public class SetupChooseLockPassword extends ChooseLockPassword
-        implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockPassword extends ChooseLockPassword {
 
     public static Intent createIntent(Context context, int quality,
             int minLength, final int maxLength, boolean requirePasswordToDecrypt,
@@ -66,9 +67,6 @@
         return intent;
     }
 
-    private SetupWizardNavBar mNavigationBar;
-    private SetupChooseLockPasswordFragment mFragment;
-
     @Override
     protected boolean isValidFragment(String fragmentName) {
         return SetupChooseLockPasswordFragment.class.getName().equals(fragmentName);
@@ -85,51 +83,22 @@
         super.onApplyThemeResource(theme, resid, first);
     }
 
-    @Override
-    public void onNavigationBarCreated(SetupWizardNavBar bar) {
-        mNavigationBar = bar;
-        SetupWizardUtils.setImmersiveMode(this);
-    }
+    public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment
+            implements NavigationBar.NavigationBarListener {
 
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-        if (mFragment != null) {
-            mFragment.handleNext();
-        }
-    }
-
-    @Override
-    public void onAttachFragment(Fragment fragment) {
-        super.onAttachFragment(fragment);
-        if (fragment instanceof SetupChooseLockPasswordFragment) {
-            mFragment = (SetupChooseLockPasswordFragment) fragment;
-        }
-    }
-
-    public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment {
+        private NavigationBar mNavigationBar;
 
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            final View view = inflater.inflate(R.layout.setup_template, container, false);
-            View scrollView = view.findViewById(R.id.bottom_scroll_view);
-            SystemBarHelper.setImeInsetView(scrollView);
-            ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
-            inflater.inflate(R.layout.setup_choose_lock_password, setupContent, true);
-            return view;
-        }
-
-        @Override
-        public void onViewCreated(View view, Bundle savedInstanceState) {
-            super.onViewCreated(view, savedInstanceState);
-            SetupWizardUtils.setIllustration(getActivity(),
-                    R.drawable.setup_illustration_lock_screen);
-            SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+            final SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(
+                    R.layout.setup_choose_lock_password, container, false);
+            SystemBarHelper.setImeInsetView(layout.findViewById(R.id.suw_bottom_scroll_view));
+            mNavigationBar = layout.getNavigationBar();
+            mNavigationBar.setNavigationBarListener(this);
+            layout.setHeaderText(getActivity().getTitle());
+            SetupWizardUtils.setImmersiveMode(getActivity());
+            return layout;
         }
 
         @Override
@@ -141,14 +110,25 @@
 
         @Override
         protected void setNextEnabled(boolean enabled) {
-            SetupChooseLockPassword activity = (SetupChooseLockPassword) getActivity();
-            activity.mNavigationBar.getNextButton().setEnabled(enabled);
+            mNavigationBar.getNextButton().setEnabled(enabled);
         }
 
         @Override
         protected void setNextText(int text) {
-            SetupChooseLockPassword activity = (SetupChooseLockPassword) getActivity();
-            activity.mNavigationBar.getNextButton().setText(text);
+            mNavigationBar.getNextButton().setText(text);
+        }
+
+        @Override
+        public void onNavigateBack() {
+            final Activity activity = getActivity();
+            if (activity != null) {
+                activity.onBackPressed();
+            }
+        }
+
+        @Override
+        public void onNavigateNext() {
+            handleNext();
         }
     }
 }
diff --git a/src/com/android/settings/SetupChooseLockPattern.java b/src/com/android/settings/SetupChooseLockPattern.java
index ca79744..2ea8d4a 100644
--- a/src/com/android/settings/SetupChooseLockPattern.java
+++ b/src/com/android/settings/SetupChooseLockPattern.java
@@ -16,8 +16,10 @@
 
 package com.android.settings;
 
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
@@ -35,8 +37,7 @@
  * Other changes should be done to ChooseLockPattern class instead and let this class inherit
  * those changes.
  */
-public class SetupChooseLockPattern extends ChooseLockPattern
-        implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockPattern extends ChooseLockPattern {
 
     public static Intent createIntent(Context context, boolean requirePassword,
             boolean confirmCredentials) {
@@ -58,9 +59,6 @@
         return intent;
     }
 
-    private SetupWizardNavBar mNavigationBar;
-    private SetupChooseLockPatternFragment mFragment;
-
     @Override
     protected boolean isValidFragment(String fragmentName) {
         return SetupChooseLockPatternFragment.class.getName().equals(fragmentName);
@@ -77,43 +75,21 @@
         super.onApplyThemeResource(theme, resid, first);
     }
 
-    @Override
-    public void onNavigationBarCreated(SetupWizardNavBar bar) {
-        mNavigationBar = bar;
-        SetupWizardUtils.setImmersiveMode(this);
-    }
+    public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment
+            implements NavigationBar.NavigationBarListener {
 
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-        if (mFragment != null) {
-            mFragment.handleRightButton();
-        }
-    }
-
-    @Override
-    public void onAttachFragment(Fragment fragment) {
-        super.onAttachFragment(fragment);
-        if (fragment instanceof ChooseLockPatternFragment) {
-            mFragment = (SetupChooseLockPatternFragment) fragment;
-        }
-    }
-
-    public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment {
-
+        private NavigationBar mNavigationBar;
         private Button mRetryButton;
 
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            final View view = inflater.inflate(R.layout.setup_template, container, false);
-            ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
-            inflater.inflate(R.layout.setup_choose_lock_pattern, setupContent, true);
-            return view;
+            final SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(
+                    R.layout.setup_choose_lock_pattern, container, false);
+            mNavigationBar = layout.getNavigationBar();
+            mNavigationBar.setNavigationBarListener(this);
+            layout.setHeaderText(getActivity().getTitle());
+            return layout;
         }
 
         @Override
@@ -121,9 +97,7 @@
             mRetryButton = (Button) view.findViewById(R.id.retryButton);
             mRetryButton.setOnClickListener(this);
             super.onViewCreated(view, savedInstanceState);
-            SetupWizardUtils.setIllustration(getActivity(),
-                    R.drawable.setup_illustration_lock_screen);
-            SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+            SetupWizardUtils.setImmersiveMode(getActivity());
         }
 
         @Override
@@ -144,14 +118,12 @@
 
         @Override
         protected void setRightButtonEnabled(boolean enabled) {
-            SetupChooseLockPattern activity = (SetupChooseLockPattern) getActivity();
-            activity.mNavigationBar.getNextButton().setEnabled(enabled);
+            mNavigationBar.getNextButton().setEnabled(enabled);
         }
 
         @Override
         protected void setRightButtonText(int text) {
-            SetupChooseLockPattern activity = (SetupChooseLockPattern) getActivity();
-            activity.mNavigationBar.getNextButton().setText(text);
+            mNavigationBar.getNextButton().setText(text);
         }
 
         @Override
@@ -160,5 +132,18 @@
             // Only enable the button for retry
             mRetryButton.setEnabled(stage == Stage.FirstChoiceValid);
         }
+
+        @Override
+        public void onNavigateBack() {
+            final Activity activity = getActivity();
+            if (activity != null) {
+                activity.onBackPressed();
+            }
+        }
+
+        @Override
+        public void onNavigateNext() {
+            handleRightButton();
+        }
     }
 }
diff --git a/src/com/android/settings/SetupEncryptionInterstitial.java b/src/com/android/settings/SetupEncryptionInterstitial.java
index 8d5f613..61ff81c 100644
--- a/src/com/android/settings/SetupEncryptionInterstitial.java
+++ b/src/com/android/settings/SetupEncryptionInterstitial.java
@@ -16,8 +16,10 @@
 
 package com.android.settings;
 
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
 
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
@@ -34,8 +36,7 @@
  * Setup Wizard. Other changes should be done to EncryptionInterstitial class instead and let this
  * class inherit those changes.
  */
-public class SetupEncryptionInterstitial extends EncryptionInterstitial
-        implements SetupWizardNavBar.NavigationBarListener{
+public class SetupEncryptionInterstitial extends EncryptionInterstitial {
 
     public static Intent createStartIntent(Context ctx, int quality,
             boolean requirePasswordDefault) {
@@ -66,32 +67,29 @@
         super.onApplyThemeResource(theme, resid, first);
     }
 
-    @Override
-    public void onNavigationBarCreated(SetupWizardNavBar bar) {
-        SetupWizardUtils.setImmersiveMode(this);
-    }
-
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-        setResult(RESULT_OK, getResultIntentData());
-        finish();
-    }
-
-    public static class SetupEncryptionInterstitialFragment extends EncryptionInterstitialFragment {
+    public static class SetupEncryptionInterstitialFragment extends EncryptionInterstitialFragment
+            implements NavigationBar.NavigationBarListener {
 
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            View view = inflater.inflate(R.layout.setup_template, container, false);
-            ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
-            View content = super.onCreateView(inflater, setupContent, savedInstanceState);
-            setupContent.addView(content);
-            return view;
+            final SetupWizardLayout layout = new SetupWizardLayout(inflater.getContext());
+            layout.setIllustration(R.drawable.setup_illustration_lock_screen,
+                    R.drawable.setup_illustration_horizontal_tile);
+            layout.setBackgroundTile(R.drawable.setup_illustration_tile);
+            final int headerTextResource = getHeaderTextResource();
+            layout.setHeaderText(headerTextResource);
+
+            View content = super.onCreateView(inflater, layout, savedInstanceState);
+            layout.addView(content);
+            layout.getNavigationBar().setNavigationBarListener(this);
+
+            Activity activity = getActivity();
+            if (activity != null) {
+                activity.setTitle(headerTextResource);
+                SetupWizardUtils.setImmersiveMode(activity);
+            }
+            return layout;
         }
 
         private int getHeaderTextResource() {
@@ -108,13 +106,21 @@
         }
 
         @Override
-        public void onViewCreated(View view, Bundle savedInstanceState) {
-            super.onViewCreated(view, savedInstanceState);
-            SetupWizardUtils.setIllustration(getActivity(),
-                    R.drawable.setup_illustration_lock_screen);
-            final int title = getHeaderTextResource();
-            getActivity().setTitle(title);
-            SetupWizardUtils.setHeaderText(getActivity(), title);
+        public void onNavigateBack() {
+            final Activity activity = getActivity();
+            if (activity != null) {
+                activity.onBackPressed();
+            }
+        }
+
+        @Override
+        public void onNavigateNext() {
+            final SetupEncryptionInterstitial activity =
+                    (SetupEncryptionInterstitial) getActivity();
+            if (activity != null) {
+                activity.setResult(RESULT_OK, activity.getResultIntentData());
+                finish();
+            }
         }
     }
 }
diff --git a/src/com/android/settings/SetupRedactionInterstitial.java b/src/com/android/settings/SetupRedactionInterstitial.java
index 266a0490..c6437e0 100644
--- a/src/com/android/settings/SetupRedactionInterstitial.java
+++ b/src/com/android/settings/SetupRedactionInterstitial.java
@@ -17,8 +17,10 @@
 package com.android.settings;
 
 import com.android.settings.notification.RedactionInterstitial;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -34,8 +36,7 @@
  * Wizard. Other changes should be done to RedactionInterstitial class instead and let this class
  * inherit those changes.
  */
-public class SetupRedactionInterstitial extends RedactionInterstitial
-        implements SetupWizardNavBar.NavigationBarListener{
+public class SetupRedactionInterstitial extends RedactionInterstitial {
 
     public static Intent createStartIntent(Context ctx) {
         Intent startIntent = RedactionInterstitial.createStartIntent(ctx);
@@ -64,41 +65,43 @@
         super.onApplyThemeResource(theme, resid, first);
     }
 
-    @Override
-    public void onNavigationBarCreated(SetupWizardNavBar bar) {
-        SetupWizardUtils.setImmersiveMode(this);
-        bar.getBackButton().setEnabled(false);
-    }
-
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-        setResult(RESULT_OK, getResultIntentData());
-        finish();
-    }
-
-    public static class SetupEncryptionInterstitialFragment extends RedactionInterstitialFragment {
+    public static class SetupEncryptionInterstitialFragment extends RedactionInterstitialFragment
+            implements NavigationBar.NavigationBarListener {
 
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            View view = inflater.inflate(R.layout.setup_template, container, false);
-            ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
-            View content = super.onCreateView(inflater, setupContent, savedInstanceState);
-            setupContent.addView(content);
-            return view;
+            final SetupWizardLayout layout = new SetupWizardLayout(inflater.getContext());
+            layout.setIllustration(R.drawable.setup_illustration_lock_screen,
+                    R.drawable.setup_illustration_horizontal_tile);
+            layout.setBackgroundTile(R.drawable.setup_illustration_tile);
+            layout.setHeaderText(R.string.notification_section_header);
+
+            View content = super.onCreateView(inflater, layout, savedInstanceState);
+            layout.addView(content);
+
+            final NavigationBar navigationBar = layout.getNavigationBar();
+            navigationBar.setNavigationBarListener(this);
+            navigationBar.getBackButton().setEnabled(false);
+            SetupWizardUtils.setImmersiveMode(getActivity());
+            return layout;
         }
 
         @Override
-        public void onViewCreated(View view, Bundle savedInstanceState) {
-            super.onViewCreated(view, savedInstanceState);
-            SetupWizardUtils.setIllustration(getActivity(),
-                    R.drawable.setup_illustration_lock_screen);
-            SetupWizardUtils.setHeaderText(getActivity(), R.string.notification_section_header);
+        public void onNavigateBack() {
+            final Activity activity = getActivity();
+            if (activity != null) {
+                activity.onBackPressed();
+            }
+        }
+
+        @Override
+        public void onNavigateNext() {
+            final SetupRedactionInterstitial activity = (SetupRedactionInterstitial) getActivity();
+            if (activity != null) {
+                activity.setResult(RESULT_OK, activity.getResultIntentData());
+                finish();
+            }
         }
     }
 }
diff --git a/src/com/android/settings/SetupWizardUtils.java b/src/com/android/settings/SetupWizardUtils.java
index 0f93c67..61043ca 100644
--- a/src/com/android/settings/SetupWizardUtils.java
+++ b/src/com/android/settings/SetupWizardUtils.java
@@ -16,17 +16,12 @@
 
 package com.android.settings;
 
-import com.android.settings.widget.SetupWizardIllustration;
 import com.android.setupwizardlib.util.SystemBarHelper;
 import com.android.setupwizardlib.util.WizardManagerHelper;
 
 import android.app.Activity;
 import android.app.Dialog;
 import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.widget.TextView;
 
 public class SetupWizardUtils {
     private static final String TAG = "SetupWizardUtils";
@@ -62,42 +57,10 @@
         SystemBarHelper.hideSystemBars(dialog);
     }
 
-    public static TextView getHeader(Activity activity) {
-        return (TextView) activity.findViewById(R.id.title);
-    }
-
-    public static void setHeaderText(Activity activity, int text) {
-        getHeader(activity).setText(text);
-    }
-
-    public static void setHeaderText(Activity activity, CharSequence text) {
-        getHeader(activity).setText(text);
-    }
-
     public static void copySetupExtras(Intent fromIntent, Intent toIntent) {
         toIntent.putExtra(WizardManagerHelper.EXTRA_THEME,
                 fromIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME));
         toIntent.putExtra(WizardManagerHelper.EXTRA_USE_IMMERSIVE_MODE,
                 fromIntent.getBooleanExtra(WizardManagerHelper.EXTRA_USE_IMMERSIVE_MODE, false));
     }
-
-    public static void setIllustration(Activity activity, int asset) {
-        SetupWizardIllustration illustration =
-                (SetupWizardIllustration) activity.findViewById(R.id.setup_illustration);
-        if (illustration != null) {
-            Drawable drawable = activity.getDrawable(R.drawable.setup_illustration);
-            Drawable newIllustration = activity.getDrawable(asset);
-            if (drawable instanceof LayerDrawable) {
-                LayerDrawable layers = (LayerDrawable) drawable;
-                Drawable oldIllustration = layers.findDrawableByLayerId(R.id.illustration_image);
-                if (newIllustration instanceof BitmapDrawable
-                        && oldIllustration instanceof BitmapDrawable) {
-                    final int gravity = ((BitmapDrawable) oldIllustration).getGravity();
-                    ((BitmapDrawable) newIllustration).setGravity(gravity);
-                }
-                layers.setDrawableByLayerId(R.id.illustration_image, newIllustration);
-                illustration.setForeground(layers);
-            }
-        }
-    }
 }
diff --git a/src/com/android/settings/sim/SimSettings.java b/src/com/android/settings/sim/SimSettings.java
index 184e882..efc71d8 100644
--- a/src/com/android/settings/sim/SimSettings.java
+++ b/src/com/android/settings/sim/SimSettings.java
@@ -29,6 +29,7 @@
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.provider.SearchIndexableResource;
+import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -52,6 +53,8 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.R;
+import android.os.SystemProperties;
+import com.android.internal.telephony.TelephonyProperties;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -245,11 +248,39 @@
         // FIXME: b/18385348, needs to handle null from getActiveSubscriptionInfoList
         if (DBG) log("[onResme] mSubInfoList=" + mSubInfoList);
 
+        final TelephonyManager tm =
+                (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
         updateAvailableSubInfos();
         updateAllOptions();
     }
 
     @Override
+    public void onPause() {
+        super.onPause();
+        final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+    }
+
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        // Disable Sim selection for Data when voice call is going on as changing the default data
+        // sim causes a modem reset currently and call gets disconnected
+        // ToDo : Add subtext on disabled preference to let user know that default data sim cannot
+        // be changed while call is going on
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state);
+             final Preference pref = findPreference(KEY_CELLULAR_DATA);
+            if (pref != null) {
+                final boolean ecbMode = SystemProperties.getBoolean(
+                        TelephonyProperties.PROPERTY_INECM_MODE, false);
+                pref.setEnabled((state == TelephonyManager.CALL_STATE_IDLE) && !ecbMode);
+            }
+        }
+    };
+
+    @Override
     public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen,
             final Preference preference) {
         final Context context = getActivity();
diff --git a/src/com/android/settings/vpn2/AppDialog.java b/src/com/android/settings/vpn2/AppDialog.java
new file mode 100644
index 0000000..2145297
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppDialog.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * UI for managing the connection controlled by an app.
+ *
+ * Among the actions available are (depending on context):
+ * <ul>
+ *   <li><strong>Forget</strong>: revoke the managing app's VPN permission</li>
+ *   <li><strong>Dismiss</strong>: continue to use the VPN</li>
+ * </ul>
+ *
+ * {@see ConfigDialog}
+ */
+class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
+    private final PackageInfo mPkgInfo;
+    private final Listener mListener;
+    private final boolean mConnected;
+
+    AppDialog(Context context, Listener listener, PackageInfo pkgInfo, boolean connected) {
+        super(context);
+
+        mListener = listener;
+        mPkgInfo = pkgInfo;
+        mConnected = connected;
+    }
+
+    public final PackageInfo getPackageInfo() {
+        return mPkgInfo;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        CharSequence vpnName;
+        try {
+            vpnName = VpnConfig.getVpnLabel(getContext(), mPkgInfo.packageName);
+        } catch (PackageManager.NameNotFoundException ex) {
+            vpnName = mPkgInfo.packageName;
+        }
+
+        setTitle(vpnName);
+        setMessage(getContext().getString(R.string.vpn_version, mPkgInfo.versionName));
+
+        createButtons();
+        super.onCreate(savedState);
+    }
+
+    protected void createButtons() {
+        Context context = getContext();
+
+        if (mConnected) {
+            // Forget the network
+            setButton(DialogInterface.BUTTON_NEGATIVE,
+                    context.getString(R.string.vpn_forget), this);
+        }
+
+        // Dismiss
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                context.getString(R.string.vpn_done), this);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_NEGATIVE) {
+            mListener.onForget(dialog);
+        }
+        dismiss();
+    }
+
+    public interface Listener {
+        public void onForget(DialogInterface dialog);
+    }
+}
diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java
new file mode 100644
index 0000000..fc8d9e3
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppDialogFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * Fragment wrapper around an {@link AppDialog}.
+ */
+public class AppDialogFragment extends DialogFragment implements AppDialog.Listener {
+    private static final String TAG_APP_DIALOG = "vpnappdialog";
+    private static final String TAG = "AppDialogFragment";
+
+    private static final String ARG_MANAGING = "managing";
+    private static final String ARG_PACKAGE = "package";
+    private static final String ARG_CONNECTED = "connected";
+
+    private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+    public static void show(VpnSettings parent, PackageInfo pkgInfo, boolean managing,
+            boolean connected) {
+        if (!parent.isAdded()) return;
+
+        Bundle args = new Bundle();
+        args.putParcelable(ARG_PACKAGE, pkgInfo);
+        args.putBoolean(ARG_MANAGING, managing);
+        args.putBoolean(ARG_CONNECTED, connected);
+
+        final AppDialogFragment frag = new AppDialogFragment();
+        frag.setArguments(args);
+        frag.setTargetFragment(parent, 0);
+        frag.show(parent.getFragmentManager(), TAG_APP_DIALOG);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Bundle args = getArguments();
+        PackageInfo pkgInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
+        boolean managing = args.getBoolean(ARG_MANAGING);
+        boolean connected = args.getBoolean(ARG_CONNECTED);
+
+        if (managing) {
+            return new AppDialog(getActivity(), this, pkgInfo, connected);
+        } else {
+            // Build an AlertDialog with an option to disconnect.
+
+            CharSequence vpnName;
+            try {
+                vpnName = VpnConfig.getVpnLabel(getActivity(), pkgInfo.packageName);
+            } catch (PackageManager.NameNotFoundException ex) {
+                vpnName = pkgInfo.packageName;
+            }
+
+            AlertDialog.Builder dlog = new AlertDialog.Builder(getActivity())
+                    .setTitle(vpnName)
+                    .setMessage(getActivity().getString(R.string.vpn_disconnect_confirm))
+                    .setNegativeButton(getActivity().getString(R.string.vpn_cancel), null);
+
+            if (connected) {
+                dlog.setPositiveButton(getActivity().getString(R.string.vpn_disconnect),
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                onDisconnect(dialog);
+                            }
+                        });
+            }
+            return dlog.create();
+        }
+    }
+
+    @Override
+    public void dismiss() {
+        ((VpnSettings) getTargetFragment()).update();
+        super.dismiss();
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        dismiss();
+        super.onCancel(dialog);
+    }
+
+    @Override
+    public void onForget(final DialogInterface dialog) {
+        PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
+        final String pkg = pkgInfo.packageName;
+        try {
+            VpnConfig vpnConfig = mService.getVpnConfig();
+            if (vpnConfig != null && pkg.equals(vpnConfig.user) && !vpnConfig.legacy) {
+                mService.setVpnPackageAuthorization(false);
+                onDisconnect(dialog);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to forget authorization for " + pkg, e);
+        }
+    }
+
+    private void onDisconnect(final DialogInterface dialog) {
+        PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
+        try {
+            mService.prepareVpn(pkgInfo.packageName, VpnConfig.LEGACY_VPN);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to disconnect package " + pkgInfo.packageName, e);
+        }
+    }
+}
diff --git a/src/com/android/settings/vpn2/AppPreference.java b/src/com/android/settings/vpn2/AppPreference.java
new file mode 100644
index 0000000..1935dd8
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppPreference.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.view.View.OnClickListener;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * {@link android.preference.Preference} containing information about a VPN
+ * application. Tracks the package name and connection state.
+ */
+public class AppPreference extends ManageablePreference {
+    public static final int STATE_CONNECTED = LegacyVpnInfo.STATE_CONNECTED;
+    public static final int STATE_DISCONNECTED = LegacyVpnInfo.STATE_DISCONNECTED;
+
+    private int mState = STATE_DISCONNECTED;
+    private String mPackageName;
+    private String mName;
+    private int mUid;
+
+    public AppPreference(Context context, OnClickListener onManage, final String packageName,
+            int uid) {
+        super(context, null /* attrs */, onManage);
+        mPackageName = packageName;
+        mUid = uid;
+        update();
+    }
+
+    public PackageInfo getPackageInfo() {
+        UserHandle user = new UserHandle(UserHandle.getUserId(mUid));
+        try {
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            return ipm.getPackageInfo(mPackageName, 0 /* flags */, user.getIdentifier());
+        } catch (RemoteException rme) {
+            return null;
+        }
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    public void setState(int state) {
+        mState = state;
+        update();
+    }
+
+    private void update() {
+        final String[] states = getContext().getResources().getStringArray(R.array.vpn_states);
+        setSummary(mState != STATE_DISCONNECTED ? states[mState] : "");
+
+        mName = mPackageName;
+        Drawable icon = null;
+        try {
+            // Make all calls to the package manager as the appropriate user.
+            int userId = UserHandle.getUserId(mUid);
+            Context userContext = getContext().createPackageContextAsUser(
+                    getContext().getPackageName(), 0 /* flags */, new UserHandle(userId));
+            PackageManager pm = userContext.getPackageManager();
+
+            // Fetch icon and VPN label
+            PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
+            if (pkgInfo != null) {
+                icon = pkgInfo.applicationInfo.loadIcon(pm);
+                mName = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
+            }
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            // Failed - use default app label and icon as fallback
+        }
+        if (icon == null) {
+            icon = getContext().getPackageManager().getDefaultActivityIcon();
+        }
+        setTitle(mName);
+        setIcon(icon);
+
+        notifyHierarchyChanged();
+    }
+
+    public int compareTo(Preference preference) {
+        if (preference instanceof AppPreference) {
+            AppPreference another = (AppPreference) preference;
+            int result;
+            if ((result = another.mState - mState) == 0 &&
+                    (result = mName.compareToIgnoreCase(another.mName)) == 0 &&
+                    (result = mPackageName.compareTo(another.mPackageName)) == 0) {
+                result = mUid - another.mUid;
+            }
+            return result;
+        } else if (preference instanceof ConfigPreference) {
+            // Use comparator from ConfigPreference
+            ConfigPreference another = (ConfigPreference) preference;
+            return -another.compareTo(this);
+        } else {
+            return super.compareTo(preference);
+        }
+    }
+}
+
diff --git a/src/com/android/settings/vpn2/VpnDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
similarity index 95%
rename from src/com/android/settings/vpn2/VpnDialog.java
rename to src/com/android/settings/vpn2/ConfigDialog.java
index 2f95bce..57f43f4 100644
--- a/src/com/android/settings/vpn2/VpnDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -16,9 +16,6 @@
 
 package com.android.settings.vpn2;
 
-import com.android.internal.net.VpnProfile;
-import com.android.settings.R;
-
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -35,15 +32,26 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
 import java.net.InetAddress;
 
-class VpnDialog extends AlertDialog implements TextWatcher,
+/**
+ * Dialog showing information about a VPN configuration. The dialog
+ * can be launched to either edit or prompt for credentials to connect
+ * to a user-added VPN.
+ *
+ * {@see AppDialog}
+ */
+class ConfigDialog extends AlertDialog implements TextWatcher,
         View.OnClickListener, AdapterView.OnItemSelectedListener {
     private final KeyStore mKeyStore = KeyStore.getInstance();
     private final DialogInterface.OnClickListener mListener;
     private final VpnProfile mProfile;
 
     private boolean mEditing;
+    private boolean mExists;
 
     private View mView;
 
@@ -64,19 +72,20 @@
     private Spinner mIpsecServerCert;
     private CheckBox mSaveLogin;
 
-    VpnDialog(Context context, DialogInterface.OnClickListener listener,
-            VpnProfile profile, boolean editing) {
+    ConfigDialog(Context context, DialogInterface.OnClickListener listener,
+            VpnProfile profile, boolean editing, boolean exists) {
         super(context);
+
         mListener = listener;
         mProfile = profile;
         mEditing = editing;
+        mExists = exists;
     }
 
     @Override
     protected void onCreate(Bundle savedState) {
         mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null);
         setView(mView);
-        setInverseBackgroundForced(true);
 
         Context context = getContext();
 
@@ -154,6 +163,12 @@
                 onClick(showOptions);
             }
 
+            // Create a button to forget the profile if it has already been saved..
+            if (mExists) {
+                setButton(DialogInterface.BUTTON_NEUTRAL,
+                        context.getString(R.string.vpn_forget), mListener);
+            }
+
             // Create a button to save the profile.
             setButton(DialogInterface.BUTTON_POSITIVE,
                     context.getString(R.string.vpn_save), mListener);
@@ -173,7 +188,7 @@
                 context.getString(R.string.vpn_cancel), mListener);
 
         // Let AlertDialog create everything.
-        super.onCreate(null);
+        super.onCreate(savedState);
 
         // Disable the action button if necessary.
         getButton(DialogInterface.BUTTON_POSITIVE)
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
new file mode 100644
index 0000000..42e1614
--- /dev/null
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
+/**
+ * Fragment wrapper around a {@link ConfigDialog}.
+ */
+public class ConfigDialogFragment extends DialogFragment implements
+        DialogInterface.OnClickListener {
+    private static final String TAG_CONFIG_DIALOG = "vpnconfigdialog";
+    private static final String TAG = "ConfigDialogFragment";
+
+    private static final String ARG_PROFILE = "profile";
+    private static final String ARG_EDITING = "editing";
+    private static final String ARG_EXISTS = "exists";
+
+    private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+    private boolean mUnlocking = false;
+
+    public static void show(VpnSettings parent, VpnProfile profile, boolean edit, boolean exists) {
+        if (!parent.isAdded()) return;
+
+        Bundle args = new Bundle();
+        args.putParcelable(ARG_PROFILE, profile);
+        args.putBoolean(ARG_EDITING, edit);
+        args.putBoolean(ARG_EXISTS, exists);
+
+        final ConfigDialogFragment frag = new ConfigDialogFragment();
+        frag.setArguments(args);
+        frag.setTargetFragment(parent, 0);
+        frag.show(parent.getFragmentManager(), TAG_CONFIG_DIALOG);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        // Check KeyStore here, so others do not need to deal with it.
+        if (!KeyStore.getInstance().isUnlocked()) {
+            if (!mUnlocking) {
+                // Let us unlock KeyStore. See you later!
+                Credentials.getInstance().unlock(getActivity());
+            } else {
+                // We already tried, but it is still not working!
+                dismiss();
+            }
+            mUnlocking = !mUnlocking;
+            return;
+        }
+
+        // Now KeyStore is always unlocked. Reset the flag.
+        mUnlocking = false;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Bundle args = getArguments();
+        VpnProfile profile = (VpnProfile) args.getParcelable(ARG_PROFILE);
+        boolean editing = args.getBoolean(ARG_EDITING);
+        boolean exists = args.getBoolean(ARG_EXISTS);
+
+        return new ConfigDialog(getActivity(), this, profile, editing, exists);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialogInterface, int button) {
+        ConfigDialog dialog = (ConfigDialog) getDialog();
+        VpnProfile profile = dialog.getProfile();
+
+        if (button == DialogInterface.BUTTON_POSITIVE) {
+            // Update KeyStore entry
+            KeyStore.getInstance().put(Credentials.VPN + profile.key, profile.encode(),
+                    KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+
+            // Flush out old version of profile
+            disconnect(profile);
+
+            // If we are not editing, connect!
+            if (!dialog.isEditing()) {
+                try {
+                    connect(profile);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to connect", e);
+                }
+            }
+        } else if (button == DialogInterface.BUTTON_NEUTRAL) {
+            // Disable profile if connected
+            disconnect(profile);
+
+            // Delete from KeyStore
+            KeyStore.getInstance().delete(Credentials.VPN + profile.key, KeyStore.UID_SELF);
+        }
+        dismiss();
+    }
+
+    @Override
+    public void dismiss() {
+        ((VpnSettings) getTargetFragment()).update();
+        super.dismiss();
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        dismiss();
+        super.onCancel(dialog);
+    }
+
+    private void connect(VpnProfile profile) throws RemoteException {
+        try {
+            mService.startLegacyVpn(profile);
+        } catch (IllegalStateException e) {
+            Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private void disconnect(VpnProfile profile) {
+        try {
+            LegacyVpnInfo connected = mService.getLegacyVpnInfo();
+            if (connected != null && profile.key.equals(connected.key)) {
+                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to disconnect", e);
+        }
+    }
+}
diff --git a/src/com/android/settings/vpn2/ConfigPreference.java b/src/com/android/settings/vpn2/ConfigPreference.java
new file mode 100644
index 0000000..4e6e16f
--- /dev/null
+++ b/src/com/android/settings/vpn2/ConfigPreference.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.view.View.OnClickListener;
+
+import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED;
+
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
+/**
+ * {@link android.preference.Preference} referencing a VPN
+ * configuration. Tracks the underlying profile and its connection
+ * state.
+ */
+public class ConfigPreference extends ManageablePreference {
+    private VpnProfile mProfile;
+    private int mState = -1;
+
+    ConfigPreference(Context context, OnClickListener onManage, VpnProfile profile) {
+        super(context, null /* attrs */, onManage);
+        setProfile(profile);
+    }
+
+    public VpnProfile getProfile() {
+        return mProfile;
+    }
+
+    public void setProfile(VpnProfile profile) {
+        mProfile = profile;
+        update();
+    }
+
+    public void setState(int state) {
+        mState = state;
+        update();
+    }
+
+    private void update() {
+        if (mState < 0) {
+            setSummary("");
+        } else {
+            String[] states = getContext().getResources()
+                    .getStringArray(R.array.vpn_states);
+            setSummary(states[mState]);
+        }
+        setIcon(R.mipmap.ic_launcher_settings);
+        setTitle(mProfile.name);
+        notifyHierarchyChanged();
+    }
+
+    @Override
+    public int compareTo(Preference preference) {
+        if (preference instanceof ConfigPreference) {
+            ConfigPreference another = (ConfigPreference) preference;
+            int result;
+            if ((result = another.mState - mState) == 0 &&
+                    (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
+                    (result = mProfile.type - another.mProfile.type) == 0) {
+                result = mProfile.key.compareTo(another.mProfile.key);
+            }
+            return result;
+        } else if (preference instanceof AppPreference) {
+            // Try to sort connected VPNs first
+            AppPreference another = (AppPreference) preference;
+            if (mState != STATE_CONNECTED && another.getState() == AppPreference.STATE_CONNECTED) {
+                return 1;
+            }
+            // Show configured VPNs before app VPNs
+            return -1;
+        } else {
+            return super.compareTo(preference);
+        }
+    }
+}
+
diff --git a/src/com/android/settings/vpn2/LockdownConfigFragment.java b/src/com/android/settings/vpn2/LockdownConfigFragment.java
new file mode 100644
index 0000000..f36cb46
--- /dev/null
+++ b/src/com/android/settings/vpn2/LockdownConfigFragment.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Dialog to configure always-on VPN.
+ */
+public class LockdownConfigFragment extends DialogFragment {
+    private List<VpnProfile> mProfiles;
+    private List<CharSequence> mTitles;
+    private int mCurrentIndex;
+
+    private static final String TAG_LOCKDOWN = "lockdown";
+
+    private static class TitleAdapter extends ArrayAdapter<CharSequence> {
+        public TitleAdapter(Context context, List<CharSequence> objects) {
+            super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
+                    android.R.id.text1, objects);
+        }
+    }
+
+    public static void show(VpnSettings parent) {
+        if (!parent.isAdded()) return;
+
+        final LockdownConfigFragment dialog = new LockdownConfigFragment();
+        dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
+    }
+
+    private static String getStringOrNull(KeyStore keyStore, String key) {
+        if (!keyStore.isUnlocked()) {
+            return null;
+        }
+        final byte[] value = keyStore.get(key);
+        return value == null ? null : new String(value);
+    }
+
+    private void initProfiles(KeyStore keyStore, Resources res) {
+        final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
+
+        mProfiles = VpnSettings.loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
+        mTitles = new ArrayList<>(1 + mProfiles.size());
+        mTitles.add(res.getText(R.string.vpn_lockdown_none));
+
+        mCurrentIndex = 0;
+        for (VpnProfile profile : mProfiles) {
+            if (TextUtils.equals(profile.key, lockdownKey)) {
+                mCurrentIndex = mTitles.size();
+            }
+            mTitles.add(profile.name);
+        }
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Context context = getActivity();
+        final KeyStore keyStore = KeyStore.getInstance();
+
+        initProfiles(keyStore, context.getResources());
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+        builder.setTitle(R.string.vpn_menu_lockdown);
+
+        final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
+        final ListView listView = (ListView) view.findViewById(android.R.id.list);
+        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        listView.setAdapter(new TitleAdapter(context, mTitles));
+        listView.setItemChecked(mCurrentIndex, true);
+        builder.setView(view);
+
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                final int newIndex = listView.getCheckedItemPosition();
+                if (mCurrentIndex == newIndex) return;
+
+                if (newIndex == 0) {
+                    keyStore.delete(Credentials.LOCKDOWN_VPN);
+                } else {
+                    final VpnProfile profile = mProfiles.get(newIndex - 1);
+                    if (!profile.isValidLockdownProfile()) {
+                        Toast.makeText(context, R.string.vpn_lockdown_config_error,
+                                Toast.LENGTH_LONG).show();
+                        return;
+                    }
+                    keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(),
+                            KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+                }
+
+                // kick profiles since we changed them
+                ConnectivityManager.from(getActivity()).updateLockdownVpn();
+            }
+        });
+
+        return builder.create();
+    }
+}
+
diff --git a/src/com/android/settings/vpn2/ManageablePreference.java b/src/com/android/settings/vpn2/ManageablePreference.java
new file mode 100644
index 0000000..5e507c1
--- /dev/null
+++ b/src/com/android/settings/vpn2/ManageablePreference.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.settings.vpn2;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.settings.R;
+
+/**
+ * Preference with an additional gear icon. Touching the gear icon triggers an
+ * onChange event.
+ */
+public class ManageablePreference extends Preference {
+    OnClickListener mListener;
+    View mManageView;
+
+    public ManageablePreference(Context context, AttributeSet attrs, OnClickListener onManage) {
+        super(context, attrs);
+        mListener = onManage;
+        setPersistent(false);
+        setOrder(0);
+        setWidgetLayoutResource(R.layout.preference_vpn);
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        mManageView = view.findViewById(R.id.manage);
+        mManageView.setOnClickListener(mListener);
+        mManageView.setTag(this);
+        super.onBindView(view);
+    }
+}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 04853f1..a333de9 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -16,39 +16,36 @@
 
 package com.android.settings.vpn2;
 
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
+import android.app.AppOpsManager;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.Preference;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
 import android.security.Credentials;
 import android.security.KeyStore;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
+import android.util.SparseArray;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.net.LegacyVpnInfo;
@@ -61,33 +58,39 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 
-public class VpnSettings extends SettingsPreferenceFragment implements
-        Handler.Callback, Preference.OnPreferenceClickListener,
-        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
-    private static final String TAG = "VpnSettings";
+import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
 
-    private static final String TAG_LOCKDOWN = "lockdown";
+/**
+ * Settings screen listing VPNs. Configured VPNs and networks managed by apps
+ * are shown in the same list.
+ */
+public class VpnSettings extends SettingsPreferenceFragment implements
+        Handler.Callback, Preference.OnPreferenceClickListener {
+    private static final String LOG_TAG = "VpnSettings";
 
     private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
+    private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+            .build();
 
-    // TODO: migrate to using DialogFragment when editing
-
-    private final IConnectivityManager mService = IConnectivityManager.Stub
+    private final IConnectivityManager mConnectivityService = IConnectivityManager.Stub
             .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-    private final KeyStore mKeyStore = KeyStore.getInstance();
-    private boolean mUnlocking = false;
+    private ConnectivityManager mConnectivityManager;
+    private UserManager mUserManager;
 
-    private HashMap<String, VpnPreference> mPreferences = new HashMap<String, VpnPreference>();
-    private VpnDialog mDialog;
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    private HashMap<String, ConfigPreference> mConfigPreferences = new HashMap<>();
+    private HashMap<String, AppPreference> mAppPreferences = new HashMap<>();
 
     private Handler mUpdater;
-    private LegacyVpnInfo mInfo;
-    private UserManager mUm;
-
-    // The key of the profile for the current ContextMenu.
-    private String mSelectedKey;
+    private LegacyVpnInfo mConnectedLegacyVpn;
+    private HashSet<String> mConnectedVpns = new HashSet<>();
 
     private boolean mUnavailable;
 
@@ -100,25 +103,24 @@
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
-        mUm = (UserManager) getSystemService(Context.USER_SERVICE);
-
-        if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
             mUnavailable = true;
             setPreferenceScreen(new PreferenceScreen(getActivity(), null));
             return;
         }
 
+        mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+        mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
+
         setHasOptionsMenu(true);
         addPreferencesFromResource(R.xml.vpn_settings2);
+    }
 
-        if (savedState != null) {
-            VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
-                    savedState.getByteArray("VpnProfile"));
-            if (profile != null) {
-                mDialog = new VpnDialog(getActivity(), this, profile,
-                        savedState.getBoolean("VpnEditing"));
-            }
-        }
+    @Override
+    public void onDestroy() {
+        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+        super.onDestroy();
     }
 
     @Override
@@ -143,13 +145,11 @@
             case R.id.vpn_create: {
                 // Generate a new key. Here we just use the current time.
                 long millis = System.currentTimeMillis();
-                while (mPreferences.containsKey(Long.toHexString(millis))) {
+                while (mConfigPreferences.containsKey(Long.toHexString(millis))) {
                     ++millis;
                 }
-                mDialog = new VpnDialog(
-                        getActivity(), this, new VpnProfile(Long.toHexString(millis)), true);
-                mDialog.setOnDismissListener(this);
-                mDialog.show();
+                VpnProfile profile = new VpnProfile(Long.toHexString(millis));
+                ConfigDialogFragment.show(this, profile, true /* editing */, false /* exists */);
                 return true;
             }
             case R.id.vpn_lockdown: {
@@ -161,18 +161,6 @@
     }
 
     @Override
-    public void onSaveInstanceState(Bundle savedState) {
-        // We do not save view hierarchy, as they are just profiles.
-        if (mDialog != null) {
-            VpnProfile profile = mDialog.getProfile();
-            savedState.putString("VpnKey", profile.key);
-            savedState.putByteArray("VpnProfile", profile.encode());
-            savedState.putBoolean("VpnEditing", mDialog.isEditing());
-        }
-        // else?
-    }
-
-    @Override
     public void onResume() {
         super.onResume();
 
@@ -191,42 +179,32 @@
             LockdownConfigFragment.show(this);
         }
 
-        // Check KeyStore here, so others do not need to deal with it.
-        if (!mKeyStore.isUnlocked()) {
-            if (!mUnlocking) {
-                // Let us unlock KeyStore. See you later!
-                Credentials.getInstance().unlock(getActivity());
-            } else {
-                // We already tried, but it is still not working!
-                finishFragment();
-            }
-            mUnlocking = !mUnlocking;
-            return;
+        update();
+    }
+
+    public void update() {
+        // Pref group within which to list VPNs
+        PreferenceGroup vpnGroup = getPreferenceScreen();
+        vpnGroup.removeAll();
+        mConfigPreferences.clear();
+        mAppPreferences.clear();
+
+        // Fetch configured VPN profiles from KeyStore
+        for (VpnProfile profile : loadVpnProfiles(mKeyStore)) {
+            final ConfigPreference pref = new ConfigPreference(getActivity(), mManageListener,
+                    profile);
+            pref.setOnPreferenceClickListener(this);
+            mConfigPreferences.put(profile.key, pref);
+            vpnGroup.addPreference(pref);
         }
 
-        // Now KeyStore is always unlocked. Reset the flag.
-        mUnlocking = false;
-
-        // Currently we are the only user of profiles in KeyStore.
-        // Assuming KeyStore and KeyGuard do the right thing, we can
-        // safely cache profiles in the memory.
-        if (mPreferences.size() == 0) {
-            PreferenceGroup group = getPreferenceScreen();
-
-            final Context context = getActivity();
-            final List<VpnProfile> profiles = loadVpnProfiles(mKeyStore);
-            for (VpnProfile profile : profiles) {
-                final VpnPreference pref = new VpnPreference(context, profile);
-                pref.setOnPreferenceClickListener(this);
-                mPreferences.put(profile.key, pref);
-                group.addPreference(pref);
-            }
-        }
-
-        // Show the dialog if there is one.
-        if (mDialog != null) {
-            mDialog.setOnDismissListener(this);
-            mDialog.show();
+        // 3rd-party VPN apps can change elsewhere. Reload them every time.
+        for (AppOpsManager.PackageOps pkg : getVpnApps()) {
+            final AppPreference pref = new AppPreference(getActivity(), mManageListener,
+                    pkg.getPackageName(), pkg.getUid());
+            pref.setOnPreferenceClickListener(this);
+            mAppPreferences.put(pkg.getPackageName(), pref);
+            vpnGroup.addPreference(pref);
         }
 
         // Start monitoring.
@@ -234,172 +212,111 @@
             mUpdater = new Handler(this);
         }
         mUpdater.sendEmptyMessage(0);
-
-        // Register for context menu. Hmmm, getListView() is hidden?
-        registerForContextMenu(getListView());
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-
-        if (mUnavailable) {
-            return;
-        }
-
-        // Hide the dialog if there is one.
-        if (mDialog != null) {
-            mDialog.setOnDismissListener(null);
-            mDialog.dismiss();
-        }
-
-        // Unregister for context menu.
-        if (getView() != null) {
-            unregisterForContextMenu(getListView());
-        }
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        // Here is the exit of a dialog.
-        mDialog = null;
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int button) {
-        if (button == DialogInterface.BUTTON_POSITIVE) {
-            // Always save the profile.
-            VpnProfile profile = mDialog.getProfile();
-            mKeyStore.put(Credentials.VPN + profile.key, profile.encode(), KeyStore.UID_SELF,
-                    KeyStore.FLAG_ENCRYPTED);
-
-            // Update the preference.
-            VpnPreference preference = mPreferences.get(profile.key);
-            if (preference != null) {
-                disconnect(profile.key);
-                preference.update(profile);
-            } else {
-                preference = new VpnPreference(getActivity(), profile);
-                preference.setOnPreferenceClickListener(this);
-                mPreferences.put(profile.key, preference);
-                getPreferenceScreen().addPreference(preference);
-            }
-
-            // If we are not editing, connect!
-            if (!mDialog.isEditing()) {
-                try {
-                    connect(profile);
-                } catch (Exception e) {
-                    Log.e(TAG, "connect", e);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
-        if (mDialog != null) {
-            Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
-            return;
-        }
-
-        if (info instanceof AdapterContextMenuInfo) {
-            Preference preference = (Preference) getListView().getItemAtPosition(
-                    ((AdapterContextMenuInfo) info).position);
-            if (preference instanceof VpnPreference) {
-                VpnProfile profile = ((VpnPreference) preference).getProfile();
-                mSelectedKey = profile.key;
-                menu.setHeaderTitle(profile.name);
-                menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
-                menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
-            }
-        }
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        if (mDialog != null) {
-            Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
-            return false;
-        }
-
-        VpnPreference preference = mPreferences.get(mSelectedKey);
-        if (preference == null) {
-            Log.v(TAG, "onContextItemSelected() is called but no preference is found");
-            return false;
-        }
-
-        switch (item.getItemId()) {
-            case R.string.vpn_menu_edit:
-                mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
-                mDialog.setOnDismissListener(this);
-                mDialog.show();
-                return true;
-            case R.string.vpn_menu_delete:
-                disconnect(mSelectedKey);
-                getPreferenceScreen().removePreference(preference);
-                mPreferences.remove(mSelectedKey);
-                mKeyStore.delete(Credentials.VPN + mSelectedKey);
-                return true;
-        }
-        return false;
     }
 
     @Override
     public boolean onPreferenceClick(Preference preference) {
-        if (mDialog != null) {
-            Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
-            return true;
-        }
-
-        if (preference instanceof VpnPreference) {
-            VpnProfile profile = ((VpnPreference) preference).getProfile();
-            if (mInfo != null && profile.key.equals(mInfo.key) &&
-                    mInfo.state == LegacyVpnInfo.STATE_CONNECTED) {
+        if (preference instanceof ConfigPreference) {
+            VpnProfile profile = ((ConfigPreference) preference).getProfile();
+            if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) &&
+                    mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) {
                 try {
-                    mInfo.intent.send();
+                    mConnectedLegacyVpn.intent.send();
                     return true;
                 } catch (Exception e) {
                     // ignore
                 }
             }
-            mDialog = new VpnDialog(getActivity(), this, profile, false);
-        } else {
-            // Generate a new key. Here we just use the current time.
-            long millis = System.currentTimeMillis();
-            while (mPreferences.containsKey(Long.toHexString(millis))) {
-                ++millis;
+            ConfigDialogFragment.show(this, profile, false /* editing */, true /* exists */);
+            return true;
+        } else if (preference instanceof AppPreference) {
+            AppPreference pref = (AppPreference) preference;
+            boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
+
+            if (!connected) {
+                try {
+                    UserHandle user = new UserHandle(UserHandle.getUserId(pref.getUid()));
+                    Context userContext = getActivity().createPackageContextAsUser(
+                            getActivity().getPackageName(), 0 /* flags */, user);
+                    PackageManager pm = userContext.getPackageManager();
+                    Intent appIntent = pm.getLaunchIntentForPackage(pref.getPackageName());
+                    if (appIntent != null) {
+                        userContext.startActivityAsUser(appIntent, user);
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException nnfe) {
+                    // Fall through
+                }
             }
-            mDialog = new VpnDialog(getActivity(), this,
-                    new VpnProfile(Long.toHexString(millis)), true);
+
+            // Already onnected or no launch intent available - show an info dialog
+            PackageInfo pkgInfo = pref.getPackageInfo();
+            AppDialogFragment.show(this, pkgInfo, false /* editing */, connected);
+            return true;
         }
-        mDialog.setOnDismissListener(this);
-        mDialog.show();
-        return true;
+        return false;
     }
 
+    private View.OnClickListener mManageListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            Object tag = view.getTag();
+
+            if (tag instanceof ConfigPreference) {
+                ConfigPreference pref = (ConfigPreference) tag;
+                ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */,
+                        true /* exists */);
+            } else if (tag instanceof AppPreference) {
+                AppPreference pref = (AppPreference) tag;
+                AppDialogFragment.show(VpnSettings.this, pref.getPackageInfo(), true /* editing */,
+                        (pref.getState() == AppPreference.STATE_CONNECTED) /* connected */);
+            }
+        }
+    };
+
     @Override
     public boolean handleMessage(Message message) {
         mUpdater.removeMessages(0);
 
         if (isResumed()) {
             try {
-                LegacyVpnInfo info = mService.getLegacyVpnInfo();
-                if (mInfo != null) {
-                    VpnPreference preference = mPreferences.get(mInfo.key);
+                // Legacy VPNs
+                LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
+                if (mConnectedLegacyVpn != null) {
+                    ConfigPreference preference = mConfigPreferences.get(mConnectedLegacyVpn.key);
                     if (preference != null) {
-                        preference.update(-1);
+                        preference.setState(-1);
                     }
-                    mInfo = null;
+                    mConnectedLegacyVpn = null;
                 }
                 if (info != null) {
-                    VpnPreference preference = mPreferences.get(info.key);
+                    ConfigPreference preference = mConfigPreferences.get(info.key);
                     if (preference != null) {
-                        preference.update(info.state);
-                        mInfo = info;
+                        preference.setState(info.state);
+                        mConnectedLegacyVpn = info;
                     }
                 }
-            } catch (Exception e) {
+
+                // VPN apps
+                for (String key : mConnectedVpns) {
+                    AppPreference preference = mAppPreferences.get(key);
+                    if (preference != null) {
+                        preference.setState(AppPreference.STATE_DISCONNECTED);
+                    }
+                }
+                mConnectedVpns.clear();
+                // TODO: also query VPN services in user profiles STOPSHIP
+                VpnConfig cfg = mConnectivityService.getVpnConfig();
+                if (cfg != null) {
+                    mConnectedVpns.add(cfg.user);
+                }
+                for (String key : mConnectedVpns) {
+                    AppPreference preference = mAppPreferences.get(key);
+                    if (preference != null) {
+                        preference.setState(AppPreference.STATE_CONNECTED);
+                    }
+                }
+            } catch (RemoteException e) {
                 // ignore
             }
             mUpdater.sendEmptyMessageDelayed(0, 1000);
@@ -407,186 +324,76 @@
         return true;
     }
 
-    private void connect(VpnProfile profile) throws Exception {
-        try {
-            mService.startLegacyVpn(profile);
-        } catch (IllegalStateException e) {
-            Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
-        }
-    }
-
-    private void disconnect(String key) {
-        if (mInfo != null && key.equals(mInfo.key)) {
-            try {
-                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
-            } catch (Exception e) {
-                // ignore
+    private NetworkCallback mNetworkCallback = new NetworkCallback() {
+        @Override
+        public void onAvailable(Network network) {
+            if (mUpdater != null) {
+                mUpdater.sendEmptyMessage(0);
             }
         }
-    }
+
+        @Override
+        public void onLost(Network network) {
+            if (mUpdater != null) {
+                mUpdater.sendEmptyMessage(0);
+            }
+        }
+    };
 
     @Override
     protected int getHelpResource() {
         return R.string.help_url_vpn;
     }
 
-    private static class VpnPreference extends Preference {
-        private VpnProfile mProfile;
-        private int mState = -1;
+    private List<AppOpsManager.PackageOps> getVpnApps() {
+        List<AppOpsManager.PackageOps> result = Lists.newArrayList();
 
-        VpnPreference(Context context, VpnProfile profile) {
-            super(context);
-            setPersistent(false);
-            setOrder(0);
-
-            mProfile = profile;
-            update();
+        // Build a filter of currently active user profiles.
+        SparseArray<Boolean> currentProfileIds = new SparseArray<>();
+        for (UserHandle profile : mUserManager.getUserProfiles()) {
+            currentProfileIds.put(profile.getIdentifier(), Boolean.TRUE);
         }
 
-        VpnProfile getProfile() {
-            return mProfile;
-        }
-
-        void update(VpnProfile profile) {
-            mProfile = profile;
-            update();
-        }
-
-        void update(int state) {
-            mState = state;
-            update();
-        }
-
-        void update() {
-            if (mState < 0) {
-                String[] types = getContext().getResources()
-                        .getStringArray(R.array.vpn_types_long);
-                setSummary(types[mProfile.type]);
-            } else {
-                String[] states = getContext().getResources()
-                        .getStringArray(R.array.vpn_states);
-                setSummary(states[mState]);
-            }
-            setTitle(mProfile.name);
-            notifyHierarchyChanged();
-        }
-
-        @Override
-        public int compareTo(Preference preference) {
-            int result = -1;
-            if (preference instanceof VpnPreference) {
-                VpnPreference another = (VpnPreference) preference;
-                if ((result = another.mState - mState) == 0 &&
-                        (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
-                        (result = mProfile.type - another.mProfile.type) == 0) {
-                    result = mProfile.key.compareTo(another.mProfile.key);
+        // Fetch VPN-enabled apps from AppOps.
+        AppOpsManager aom = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> apps = aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN});
+        if (apps != null) {
+            for (AppOpsManager.PackageOps pkg : apps) {
+                int userId = UserHandle.getUserId(pkg.getUid());
+                if (currentProfileIds.get(userId) == null) {
+                    // Skip packages for users outside of our profile group.
+                    continue;
+                }
+                // Look for a MODE_ALLOWED permission to activate VPN.
+                boolean allowed = false;
+                for (AppOpsManager.OpEntry op : pkg.getOps()) {
+                    if (op.getOp() == OP_ACTIVATE_VPN &&
+                            op.getMode() == AppOpsManager.MODE_ALLOWED) {
+                        allowed = true;
+                    }
+                }
+                if (allowed) {
+                    result.add(pkg);
                 }
             }
+        }
+        return result;
+    }
+
+    protected static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
+        final ArrayList<VpnProfile> result = Lists.newArrayList();
+
+        // This might happen if the user does not yet have a keystore. Quietly short-circuit because
+        // no keystore means no VPN configs.
+        if (!keyStore.isUnlocked()) {
             return result;
         }
-    }
 
-    /**
-     * Dialog to configure always-on VPN.
-     */
-    public static class LockdownConfigFragment extends DialogFragment {
-        private List<VpnProfile> mProfiles;
-        private List<CharSequence> mTitles;
-        private int mCurrentIndex;
-
-        private static class TitleAdapter extends ArrayAdapter<CharSequence> {
-            public TitleAdapter(Context context, List<CharSequence> objects) {
-                super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
-                        android.R.id.text1, objects);
-            }
-        }
-
-        public static void show(VpnSettings parent) {
-            if (!parent.isAdded()) return;
-
-            final LockdownConfigFragment dialog = new LockdownConfigFragment();
-            dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
-        }
-
-        private static String getStringOrNull(KeyStore keyStore, String key) {
-            final byte[] value = keyStore.get(Credentials.LOCKDOWN_VPN);
-            return value == null ? null : new String(value);
-        }
-
-        private void initProfiles(KeyStore keyStore, Resources res) {
-            final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
-
-            mProfiles = loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
-            mTitles = Lists.newArrayList();
-            mTitles.add(res.getText(R.string.vpn_lockdown_none));
-            mCurrentIndex = 0;
-
-            for (VpnProfile profile : mProfiles) {
-                if (TextUtils.equals(profile.key, lockdownKey)) {
-                    mCurrentIndex = mTitles.size();
-                }
-                mTitles.add(profile.name);
-            }
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            final KeyStore keyStore = KeyStore.getInstance();
-
-            initProfiles(keyStore, context.getResources());
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
-
-            builder.setTitle(R.string.vpn_menu_lockdown);
-
-            final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
-            final ListView listView = (ListView) view.findViewById(android.R.id.list);
-            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-            listView.setAdapter(new TitleAdapter(context, mTitles));
-            listView.setItemChecked(mCurrentIndex, true);
-            builder.setView(view);
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final int newIndex = listView.getCheckedItemPosition();
-                    if (mCurrentIndex == newIndex) return;
-
-                    if (newIndex == 0) {
-                        keyStore.delete(Credentials.LOCKDOWN_VPN);
-
-                    } else {
-                        final VpnProfile profile = mProfiles.get(newIndex - 1);
-                        if (!profile.isValidLockdownProfile()) {
-                            Toast.makeText(context, R.string.vpn_lockdown_config_error,
-                                    Toast.LENGTH_LONG).show();
-                            return;
-                        }
-                        keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(),
-                                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
-                    }
-
-                    // kick profiles since we changed them
-                    ConnectivityManager.from(getActivity()).updateLockdownVpn();
-                }
-            });
-
-            return builder.create();
-        }
-    }
-
-    private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
-        final ArrayList<VpnProfile> result = Lists.newArrayList();
-        final String[] keys = keyStore.saw(Credentials.VPN);
-        if (keys != null) {
-            for (String key : keys) {
-                final VpnProfile profile = VpnProfile.decode(
-                        key, keyStore.get(Credentials.VPN + key));
-                if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
-                    result.add(profile);
-                }
+        // We are the only user of profiles in KeyStore so no locks are needed.
+        for (String key : keyStore.saw(Credentials.VPN)) {
+            final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key));
+            if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
+                result.add(profile);
             }
         }
         return result;
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
index 4dfea08..ba78794 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
@@ -30,6 +30,8 @@
 
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.SetupWizardListLayout;
+import com.android.setupwizardlib.view.NavigationBar;
 
 /**
  * This customized version of WifiSettings is shown to the user only during Setup Wizard. Menu
@@ -48,15 +50,9 @@
     @Override
     public View onCreateView(final LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-
-        final View view = inflater.inflate(R.layout.setup_preference, container, false);
-
-        final ListView list = (ListView) view.findViewById(android.R.id.list);
-        final View title = view.findViewById(R.id.title);
-        if (title == null) {
-            final View header = inflater.inflate(R.layout.setup_wizard_header, list, false);
-            list.addHeaderView(header, null, false);
-        }
+        final SetupWizardListLayout layout = (SetupWizardListLayout) inflater.inflate(
+                R.layout.setup_wifi_layout, container, false);
+        final ListView list = layout.getListView();
 
         mAddOtherNetworkItem = inflater.inflate(R.layout.setup_wifi_add_network, list, false);
         list.addFooterView(mAddOtherNetworkItem, null, true);
@@ -69,19 +65,19 @@
             }
         });
 
-        return view;
+        final NavigationBar navigationBar = layout.getNavigationBar();
+        if (navigationBar != null) {
+            WifiSetupActivity activity = (WifiSetupActivity) getActivity();
+            activity.onNavigationBarCreated(navigationBar);
+        }
+
+        return layout;
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
-        getView().setSystemUiVisibility(
-                View.STATUS_BAR_DISABLE_HOME |
-                View.STATUS_BAR_DISABLE_RECENT |
-                View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS |
-                View.STATUS_BAR_DISABLE_CLOCK);
-
         if (hasNextButton()) {
             getNextButton().setVisibility(View.GONE);
         }
diff --git a/src/com/android/settings/wifi/WifiSetupActivity.java b/src/com/android/settings/wifi/WifiSetupActivity.java
index 321ecb6..38fc32d 100644
--- a/src/com/android/settings/wifi/WifiSetupActivity.java
+++ b/src/com/android/settings/wifi/WifiSetupActivity.java
@@ -35,11 +35,10 @@
 import com.android.settings.ButtonBarHandler;
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar.NavigationBarListener;
+import com.android.setupwizardlib.view.NavigationBar;
 
 public class WifiSetupActivity extends WifiPickerActivity
-        implements ButtonBarHandler, NavigationBarListener {
+        implements ButtonBarHandler, NavigationBar.NavigationBarListener {
     private static final String TAG = "WifiSetupActivity";
 
     // this boolean extra specifies whether to auto finish when connection is established
@@ -72,7 +71,7 @@
     // Whether the device is connected to WiFi
     private boolean mWifiConnected;
 
-    private SetupWizardNavBar mNavigationBar;
+    private NavigationBar mNavigationBar;
 
     private IntentFilter mFilter = new IntentFilter();
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -203,9 +202,9 @@
         finish();
     }
 
-    @Override
-    public void onNavigationBarCreated(final SetupWizardNavBar bar) {
+    public void onNavigationBarCreated(final NavigationBar bar) {
         mNavigationBar = bar;
+        bar.setNavigationBarListener(this);
         SetupWizardUtils.setImmersiveMode(this);
     }