Security lock pattern in Settings.

Bug: 71642544
Test: Mojave
Change-Id: I21828d495affcd5cd369a47b4e5d3a30a9c03ec0
diff --git a/Android.mk b/Android.mk
index d278268..fdecc14 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,6 +53,7 @@
   include packages/apps/Car/libs/car-list/car-list.mk
   include packages/apps/Car/libs/car-apps-common/car-apps-common.mk
   include packages/services/Car/car-support-lib/car-support.mk
+  include frameworks/opt/setupwizard/library/common-gingerbread.mk
   include frameworks/base/packages/SettingsLib/common.mk
 
   include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4b7d65f..dbd54b4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -108,6 +108,22 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".security.ChooseSecurityLockActivity"
+                  android:configChanges="orientation|keyboardHidden|screenSize">
+            <intent-filter>
+                <action android:name="android.car.settings.CHOOSE_SECURITY_LOCK" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".security.ChooseLockPatternActivity"
+                  android:configChanges="orientation|keyboardHidden|screenSize">
+            <intent-filter>
+                <action android:name="android.car.settings.CHOOSE_LOCK_PATTERN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <service android:name=".bluetooth.BluetoothPairingService" />
 
         <receiver android:name=".bluetooth.BluetoothPairingRequest">
diff --git a/res/color/lock_pattern_regular.xml b/res/color/lock_pattern_regular.xml
new file mode 100644
index 0000000..67e4fba
--- /dev/null
+++ b/res/color/lock_pattern_regular.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_body1" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_body1" />
+</selector>
\ No newline at end of file
diff --git a/res/color/lock_pattern_regular_dark.xml b/res/color/lock_pattern_regular_dark.xml
new file mode 100644
index 0000000..d3ba4ed
--- /dev/null
+++ b/res/color/lock_pattern_regular_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_body1_dark" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_body1_dark" />
+</selector>
\ No newline at end of file
diff --git a/res/color/lock_pattern_regular_light.xml b/res/color/lock_pattern_regular_light.xml
new file mode 100644
index 0000000..5b47d6b
--- /dev/null
+++ b/res/color/lock_pattern_regular_light.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_body1_light" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_body1_light" />
+</selector>
\ No newline at end of file
diff --git a/res/color/lock_pattern_success.xml b/res/color/lock_pattern_success.xml
new file mode 100644
index 0000000..bee9145
--- /dev/null
+++ b/res/color/lock_pattern_success.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_blue_500" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_blue_500" />
+</selector>
\ No newline at end of file
diff --git a/res/color/lock_pattern_success_dark.xml b/res/color/lock_pattern_success_dark.xml
new file mode 100644
index 0000000..33ad70e
--- /dev/null
+++ b/res/color/lock_pattern_success_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_teal_700" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_teal_700" />
+</selector>
\ No newline at end of file
diff --git a/res/color/lock_pattern_success_light.xml b/res/color/lock_pattern_success_light.xml
new file mode 100644
index 0000000..eb834c4
--- /dev/null
+++ b/res/color/lock_pattern_success_light.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:color="@color/car_teal_200" />
+    <item android:state_enabled="false"
+          android:alpha="0.5"
+          android:color="@color/car_teal_200" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/ic_lock.xml b/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..edb617d
--- /dev/null
+++ b/res/drawable/ic_lock.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+    <path android:pathData="M36,16h-2v-4c0,-5.52 -4.48,-10 -10,-10S14,6.48 14,12v4h-2c-2.21,0 -4,1.79 -4,4v20c0,2.21 1.79,4 4,4h24c2.21,0 4,-1.79 4,-4L40,20c0,-2.21 -1.79,-4 -4,-4zM24,34c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4zM30.2,16L17.8,16v-4c0,-3.42 2.78,-6.2 6.2,-6.2 3.42,0 6.2,2.78 6.2,6.2v4z" android:fillColor="#000000"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/choose_lock_pattern.xml b/res/layout/choose_lock_pattern.xml
new file mode 100644
index 0000000..31737e0
--- /dev/null
+++ b/res/layout/choose_lock_pattern.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License")
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="@dimen/car_margin"
+    android:layout_marginEnd="@dimen/car_margin"
+    android:orientation="horizontal"
+    android:paddingTop="@dimen/car_app_bar_height"
+    android:paddingBottom="@dimen/car_app_bar_height">
+
+    <!-- Start side: lock pattern -->
+    <FrameLayout
+        android:layout_weight="@integer/content_weight"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical">
+
+        <com.android.internal.widget.LockPatternView
+            style="@style/LockPatternStyle"
+            android:id="@+id/lockPattern"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"/>
+    </FrameLayout>
+
+    <!-- End side: instructions and messages -->
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="@integer/content_weight"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingTop="@dimen/car_primary_icon_size">
+
+        <ImageView
+            android:id="@+id/base_icon"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_marginBottom="@dimen/car_padding_2"
+            android:gravity="center"/>
+
+        <TextView
+            android:id="@+id/title_text"
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/car_padding_2"
+            android:textAppearance="@style/CarTitle" />
+
+        <TextView
+            android:id="@+id/description_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/car_padding_5"
+            android:gravity="center"
+            android:textAppearance="@style/CarBody2" />
+
+        <!-- confirm / restart buttons -->
+        <LinearLayout
+            android:id="@+id/buttonContainer"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_alignParentBottom="true"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:gravity="center"
+            android:orientation="horizontal">
+
+            <!-- left / top button: skip, or re-try -->
+            <Button android:id="@+id/footerSecondaryButton"
+                    style="@style/SetupWizardButton.Negative"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="@dimen/car_action1_size"
+                    android:layout_marginEnd="@dimen/car_padding_4"
+                    android:text="@string/lockpattern_restart_button_text" />
+
+            <!-- right / bottom button: confirm or ok -->
+            <Button android:id="@+id/footerPrimaryButton"
+                    style="@style/SetupWizardButton.Positive"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="@dimen/car_action1_size"
+                    android:text="@string/lockpattern_confirm_button_text" />
+
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..d3ae5f0
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<resources>
+    <color name="lock_pattern_background">#353839</color>
+</resources>
\ No newline at end of file
diff --git a/res/values/integers.xml b/res/values/integers.xml
new file mode 100644
index 0000000..5942f80
--- /dev/null
+++ b/res/values/integers.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<resources>
+    <!-- Weight of the main content in the illustration_layout -->
+    <integer name="content_weight">7</integer>
+
+    <!-- Weight of the illustration logo/video in the illustration_layout -->
+    <integer name="illustration_weight">5</integer>
+
+    <!-- Timeout to clear invalid lock pin/pattern/password -->
+    <integer name="clear_content_timeout_ms">2000</integer>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5eb6e3e..bf438dc 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -393,11 +393,62 @@
     <!-- Message to inform user that the newly created user will have permissions to update apps for all other users. [CHAR LIMIT=100] -->
     <string name="user_add_user_message_update">Any user can update apps for all other users.</string>
 
-    <!-- generic --><skip />
+    <!-- security lock -->
+    <!-- Title for security settings [CHAR LIMIT=20] -->
+    <string name="security_settings_title">Security</string>
+    <!-- Subtitle for security settings [CHAR LIMIT=20] -->
+    <string name="security_settings_subtitle">Screen lock</string>
+    <!-- Title string shown in choose screen lock for lock pattern [CHAR LIMIT=40] -->
+    <string name="security_lock_pattern">Pattern</string>
+    <!-- Title string shown in choose screen lock for lock pin [CHAR LIMIT=40] -->
+    <string name="security_lock_pin">Pin</string>
+    <!-- Title string shown in choose screen lock for lock password [CHAR LIMIT=40] -->
+    <string name="security_lock_password">Password</string>
+    <!--  Title for security picker to choose the unlock method: None/Pattern/PIN/Password [CHAR LIMIT=22] -->
+    <string name="lock_settings_picker_title">Choose a lock type</string>
+    <!-- Security & location settings screen, change unlock pattern screen button, on bottom of screen.  After they draw a pattern and release their finger, we display the pattern so they remember.  When they are ready to draw it once again to confirm it, they press this button. -->
+    <string name="lockpattern_confirm_button_text">Confirm</string>
+    <!-- Security & location settings screen, change unlock pattern screen button, on bottom of screen.  After they draw a pattern and release their finger, we display the pattern so they remember.  If they are not satisfied with this pattern, they click this button to redraw the pattern. -->
+    <string name="lockpattern_restart_button_text">Redraw</string>
+    <!-- Choose unlock pattern screen button. After first valid enter, they will be prompt to confirm page. [CHAR LIMIT=20] -->
+    <string name="lockpattern_continue_button_text">Continue</string>
+    <!-- Title string shown in choose screen lock [CHAR LIMIT=40] -->
+    <string name="set_screen_lock">Set a screen lock</string>
+    <!-- Message on first screen of choose pattern flow [CHAR LIMIT=40] -->
+    <string name="choose_lock_pattern_message">For security, set a pattern</string>
+    <!-- Choose unlock pattern screen button. If they are supposed to enter their current pattern before being able to draw another one, and they made a mistake, they hit this button to try again [CHAR LIMIT=20] -->
+    <string name="lockpattern_retry_button_text">Clear</string>
+    <!-- Choose unlock pattern screen button. They can choose to cancel the setting of lock pattern. [CHAR LIMIT=20] -->
+    <string name="lockpattern_cancel_button_text">Cancel</string>
+    <!-- Choose unlock pattern screen message, confirm pattern success.[CHAR LIMIT=40] -->
+    <string name="lockpattern_pattern_confirmed">Your new unlock pattern</string>
+    <!-- Choose unlock pattern screen instruction on top of screen. When they are supposed to draw a new unlock pattern (for example, if they are changing their unlock patterns). [CHAR LIMIT=40] -->
+    <string name="lockpattern_recording_intro_header">Draw an unlock pattern</string>
+    <!-- Choose unlock pattern screen instruction on top of screen while drawing pattern. [CHAR LIMIT=40] -->
+    <string name="lockpattern_recording_inprogress">Release finger when done</string>
+    <!-- Choose unlock pattern screen message after drawing pattern. [CHAR LIMIT=40] -->
+    <string name="lockpattern_pattern_entered">Pattern recorded</string>
+    <!-- Choose unlock pattern screen instruction to confirm pattern. [CHAR LIMIT=40] -->
+    <string name="lockpattern_need_to_confirm">Draw pattern again to confirm</string>
+    <!-- Choose unlock pattern screen instruction on top of screen if user doesn't connect enough dots. [CHAR LIMIT=40] -->
+    <string name="lockpattern_recording_incorrect_too_short">Connect at least 4 dots. Try again.</string>
+    <!-- Choose unlock pattern screen instruction if user draws incorrect pattern [CHAR LIMIT=30] -->
+    <string name="lockpattern_pattern_wrong">Wrong pattern</string>
+    <!-- Choose unlock pattern screen instruction, the help instructions (an animation) caption. [CHAR LIMIT=40] -->
+    <string name="lockpattern_settings_help_how_to_record">How to draw an unlock pattern</string>
+    <!-- Label for button to confirm picker selection [CHAR_LIMIT=20] -->
+    <string name="okay">OK</string>
+
+    <!-- Label for button in screen lock settings, allowing users to choose other types of screen locks. [CHAR LIMIT=40] -->
+    <string name="setup_lock_settings_options_button_label">Screen lock options</string>
+
+    <!-- generic -->
     <!-- Button label for generic forget action [CHAR LIMIT=20] -->
     <string name="forget">Forget</string>
     <!-- Delete button text [CHAR LIMIT=20] -->
     <string name="delete_button">Delete</string>
     <!-- Remove button text [CHAR LIMIT=20] -->
     <string name="remove_button">Remove</string>
+    <!-- Cancel button text [CHAR LIMIT=20] -->
+    <string name="cancel">Cancel</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6835877..8411b79 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -63,6 +63,21 @@
            parent="Widget.Car.Button.Borderless.Colored">
         <item name="android:minWidth">@dimen/car_button_min_width</item>
         <item name="android:fontFamily">roboto-regular</item>
-        <item name="android:textColor">@color/action_bar_btn</item>
+        <item name="android:textColor">@color/car_accent</item>
+    </style>
+
+    <style name="SetupWizardButton.Negative" parent="@style/SuwGlifButton.Secondary">
+        <!-- Negative margin to offset for padding of the button itself. We want the label to be
+             aligned with the text above it -->
+        <item name="android:layout_marginStart">-16dp</item>
+    </style>
+
+    <style name="SetupWizardButton.Positive" parent="@style/SuwGlifButton.Primary" />
+
+    <!-- Style for security lock pattern. -->
+    <style name="LockPatternStyle">
+        <item name="*android:regularColor">@color/lock_pattern_regular</item>
+        <item name="*android:successColor">@color/lock_pattern_success</item>
+        <item name="*android:errorColor">?android:attr/colorError</item>
     </style>
 </resources>
diff --git a/src/com/android/car/settings/home/HomepageFragment.java b/src/com/android/car/settings/home/HomepageFragment.java
index ff80fcd..bd9244e 100644
--- a/src/com/android/car/settings/home/HomepageFragment.java
+++ b/src/com/android/car/settings/home/HomepageFragment.java
@@ -36,6 +36,7 @@
 import com.android.car.settings.common.ListSettingsFragment;
 import com.android.car.settings.datetime.DatetimeSettingsFragment;
 import com.android.car.settings.display.DisplaySettingsFragment;
+import com.android.car.settings.security.ChooseLockTypeFragment;
 import com.android.car.settings.sound.SoundSettingsFragment;
 import com.android.car.settings.system.SystemSettingsFragment;
 import com.android.car.settings.users.UsersListFragment;
@@ -180,6 +181,13 @@
                 UsersListFragment.newInstance(),
                 mFragmentController));
         lineItems.add(new SimpleIconTransitionLineItem(
+                R.string.security_settings_title,
+                R.drawable.ic_lock,
+                getContext(),
+                null,
+                ChooseLockTypeFragment.newInstance(),
+                mFragmentController));
+        lineItems.add(new SimpleIconTransitionLineItem(
                 R.string.system_setting_title,
                 R.drawable.ic_settings_about,
                 getContext(),
diff --git a/src/com/android/car/settings/security/ChooseLockPatternActivity.java b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
new file mode 100644
index 0000000..c278c82
--- /dev/null
+++ b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.car.settings.security;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.support.v4.app.FragmentActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockPatternView.Cell;
+import com.android.internal.widget.LockPatternView.DisplayMode;
+
+import com.android.car.settings.R;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Activity for choosing security lock pattern.
+ */
+public class ChooseLockPatternActivity extends FragmentActivity implements View.OnClickListener {
+    private static final String TAG = "ChooseLockPattern";
+    private static final int ID_EMPTY_MESSAGE = -1;
+    // How long we wait to clear a wrong pattern
+    private int mWrongPatternClearTimeOut;
+    private Stage mUiStage = Stage.Introduction;
+    private LockPatternView mLockPatternView;
+    private TextView mMessageText;
+    private Button mSecondaryButton;
+    private Button mPrimaryButton;
+
+    private List<LockPatternView.Cell> mChosenPattern;
+    private String mCurrentPattern;
+    private SaveAndFinishWorker mSaveAndFinishWorker;
+    private int mUserId;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.choose_lock_pattern);
+
+        ImageView iconImg = (ImageView) findViewById(R.id.base_icon);
+        iconImg.setImageResource(R.drawable.ic_lock);
+
+        TextView titleText = (TextView) findViewById(R.id.title_text);
+        titleText.setText(getString(R.string.set_screen_lock));
+
+        mMessageText = (TextView) findViewById(R.id.description_text);
+        mMessageText.setText(getString(R.string.choose_lock_pattern_message));
+
+        mPrimaryButton = (Button) findViewById(R.id.footerPrimaryButton);
+        mPrimaryButton.setOnClickListener(this);
+        mSecondaryButton = (Button) findViewById(R.id.footerSecondaryButton);
+        mSecondaryButton.setOnClickListener(this);
+
+        mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
+        mLockPatternView.setVisibility(View.VISIBLE);
+        mLockPatternView.setEnabled(true);
+        mLockPatternView.clearPattern();
+        mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
+
+        mUserId = UserHandle.myUserId();
+        mCurrentPattern = getIntent()
+                .getStringExtra(SaveChosenLockWorkerBase.EXTRA_KEY_PATTERN);
+
+        mWrongPatternClearTimeOut = getResources().getInteger(R.integer.clear_content_timeout_ms);
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view == mPrimaryButton) {
+            handlePrimaryButtonClick();
+        } else if (view == mSecondaryButton) {
+            handleSecondaryButtonClick();
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        updateStage(mUiStage);
+
+        if (mSaveAndFinishWorker != null) {
+            setPrimaryButtonEnabled(true);
+            mSaveAndFinishWorker.setListener(mWorkerListener);
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mSaveAndFinishWorker != null) {
+            mSaveAndFinishWorker.setListener(null);
+        }
+    }
+
+    /**
+     * Keep track internally of where the user is in choosing a pattern.
+     */
+    protected enum Stage {
+
+        /**
+         * Initial stage when first launching choose a lock pattern.
+         * Pattern enabled, secondary button allow for Cancel, primary button disabled.
+         */
+        Introduction(
+                R.string.lockpattern_recording_intro_header,
+                SecondaryButtonState.Cancel, PrimaryButtonState.ContinueDisabled,
+                true /* patternEnabled */ ),
+        /**
+         * Help screen to show how a valid pattern looks like.
+         * Pattern disabled, primary button shows Ok. No secondary button.
+         */
+        HelpScreen(
+                R.string.lockpattern_settings_help_how_to_record,
+                SecondaryButtonState.Gone, PrimaryButtonState.Ok,
+                false /* patternEnabled */ ),
+        /**
+         * Invalid pattern is entered, hint message show required number of dots.
+         * Secondary button allows for Retry, primary button disabled.
+         */
+        ChoiceTooShort(
+                R.string.lockpattern_recording_incorrect_too_short,
+                SecondaryButtonState.Retry, PrimaryButtonState.ContinueDisabled,
+                true /* patternEnabled */ ),
+        /**
+         * First drawing on the pattern is valid, primary button shows Continue,
+         * can proceed to next screen.
+         */
+        FirstChoiceValid(
+                R.string.lockpattern_recording_intro_header,
+                SecondaryButtonState.Retry, PrimaryButtonState.Continue,
+                false /* patternEnabled */ ),
+        /**
+         * Need to draw pattern again to confirm.
+         * Secondary button allows for Cancel, primary button disabled.
+         */
+        NeedToConfirm(
+                R.string.lockpattern_need_to_confirm,
+                SecondaryButtonState.Cancel, PrimaryButtonState.ConfirmDisabled,
+                true /* patternEnabled */ ),
+        /**
+         * Confirmation of previous drawn pattern failed, didn't enter the same pattern.
+         * Need to re-draw the pattern to match the fist pattern.
+         */
+        ConfirmWrong(
+                R.string.lockpattern_pattern_wrong,
+                SecondaryButtonState.Cancel, PrimaryButtonState.ConfirmDisabled,
+                true /* patternEnabled */ ),
+        /**
+         * Pattern is confirmed after drawing the same pattern twice.
+         * Pattern disabled.
+         */
+        ChoiceConfirmed(
+                R.string.lockpattern_pattern_confirmed,
+                SecondaryButtonState.Cancel, PrimaryButtonState.Confirm,
+                false /* patternEnabled */ );
+
+
+        /**
+         * @param message The message displayed as instruction.
+         * @param secondaryButtonState The state of the secondary button.
+         * @param primaryButtonState The state of the primary button.
+         * @param patternEnabled Whether the pattern widget is enabled.
+         */
+        Stage(int messageId,
+                SecondaryButtonState secondaryButtonState,
+                PrimaryButtonState primaryButtonState,
+                boolean patternEnabled) {
+            this.messageId = messageId;
+            this.secondaryButtonState = secondaryButtonState;
+            this.primaryButtonState = primaryButtonState;
+            this.patternEnabled = patternEnabled;
+        }
+
+        final int messageId;
+        final SecondaryButtonState secondaryButtonState;
+        final PrimaryButtonState primaryButtonState;
+        final boolean patternEnabled;
+    }
+
+    /**
+     * Updates the messages and buttons appropriate to what stage the user
+     * is at in choosing a pattern. This doesn't handle clearing out the pattern;
+     * the pattern is expected to be in the right state.
+     * @param stage The stage UI should be updated to match with.
+     */
+    protected void updateStage(Stage stage) {
+        mUiStage = stage;
+
+        // Message text, visibility and
+        // enabled state all known from the stage
+        mMessageText.setText(stage.messageId);
+
+        if (stage.secondaryButtonState == SecondaryButtonState.Gone) {
+            setSecondaryButtonVisible(false);
+        } else {
+            setSecondaryButtonVisible(true);
+            setSecondaryButtonTextId(stage.secondaryButtonState.textResId);
+            setSecondaryButtonEnabled(stage.secondaryButtonState.enabled);
+        }
+
+        setPrimaryButtonTextId(stage.primaryButtonState.text);
+        setPrimaryButtonEnabled(stage.primaryButtonState.enabled);
+
+        // same for whether the pattern is enabled
+        if (stage.patternEnabled) {
+            mLockPatternView.enableInput();
+        } else {
+            mLockPatternView.disableInput();
+        }
+
+        // the rest of the stuff varies enough that it is easier just to handle
+        // on a case by case basis.
+        mLockPatternView.setDisplayMode(DisplayMode.Correct);
+
+        switch (mUiStage) {
+            case Introduction:
+                mLockPatternView.clearPattern();
+                break;
+            case HelpScreen:
+                mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
+                break;
+            case ChoiceTooShort:
+                mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+                postClearPatternRunnable();
+                break;
+            case FirstChoiceValid:
+                break;
+            case NeedToConfirm:
+                mLockPatternView.clearPattern();
+                break;
+            case ConfirmWrong:
+                mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+                postClearPatternRunnable();
+                break;
+            case ChoiceConfirmed:
+                break;
+            default:
+                // Do nothing.
+        }
+    }
+
+    /**
+     * The states of the secondary footer button.
+     */
+    enum SecondaryButtonState {
+        Cancel(R.string.lockpattern_cancel_button_text, true),
+        CancelDisabled(R.string.lockpattern_cancel_button_text, false),
+        Retry(R.string.lockpattern_retry_button_text, true),
+        RetryDisabled(R.string.lockpattern_retry_button_text, false),
+        Gone(ID_EMPTY_MESSAGE, false);
+
+        /**
+         * @param text The displayed text for this mode.
+         * @param enabled Whether the button should be enabled.
+         */
+        SecondaryButtonState(int textId, boolean enabled) {
+            this.textResId = textId;
+            this.enabled = enabled;
+        }
+
+        final int textResId;
+        final boolean enabled;
+    }
+
+    /**
+     * The states of the primary footer button.
+     */
+    enum PrimaryButtonState {
+        Continue(R.string.lockpattern_continue_button_text, true),
+        ContinueDisabled(R.string.lockpattern_continue_button_text, false),
+        Confirm(R.string.lockpattern_confirm_button_text, true),
+        ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
+        Ok(R.string.okay, true);
+
+        /**
+         * @param text The displayed text for this mode.
+         * @param enabled Whether the button should be enabled.
+         */
+        PrimaryButtonState(int text, boolean enabled) {
+            this.text = text;
+            this.enabled = enabled;
+        }
+
+        final int text;
+        final boolean enabled;
+    }
+
+    // The pattern listener that responds according to a user choosing a new
+    // lock pattern.
+    private final LockPatternView.OnPatternListener mChooseNewLockPatternListener =
+            new LockPatternView.OnPatternListener() {
+                @Override
+                public void onPatternStart() {
+                    mLockPatternView.removeCallbacks(mClearPatternRunnable);
+                    updateUIWhenPatternInProgress();
+                }
+
+                @Override
+                public void onPatternCleared() {
+                    mLockPatternView.removeCallbacks(mClearPatternRunnable);
+                }
+
+                @Override
+                public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+                    switch(mUiStage) {
+                        case Introduction:
+                        case ChoiceTooShort:
+                            handlePatternEntered(pattern);
+                            break;
+                        case ConfirmWrong:
+                        case NeedToConfirm:
+                            handleConfirmPattern(pattern);
+                            break;
+                        default:
+                            throw new IllegalStateException("Unexpected stage " + mUiStage
+                                    + " when entering the pattern.");
+                    }
+                }
+
+                @Override
+                public void onPatternCellAdded(List<Cell> pattern) {}
+
+                private void handleConfirmPattern(List<LockPatternView.Cell> pattern) {
+                    if (mChosenPattern == null) {
+                        throw new IllegalStateException(
+                                "null chosen pattern in stage 'need to confirm");
+                    }
+                    if (mChosenPattern.equals(pattern)) {
+                        updateStage(Stage.ChoiceConfirmed);
+                    } else {
+                        updateStage(Stage.ConfirmWrong);
+                    }
+                }
+
+                private void handlePatternEntered(List<LockPatternView.Cell> pattern) {
+                    if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+                        updateStage(Stage.ChoiceTooShort);
+                    } else {
+                        mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
+                        updateStage(Stage.FirstChoiceValid);
+                    }
+                }
+            };
+
+    private void updateUIWhenPatternInProgress() {
+        mMessageText.setText(R.string.lockpattern_recording_inprogress);
+        setPrimaryButtonEnabled(false);
+        setSecondaryButtonEnabled(false);
+    }
+
+    // clear the wrong pattern unless they have started a new one
+    // already
+    private void postClearPatternRunnable() {
+        mLockPatternView.removeCallbacks(mClearPatternRunnable);
+        mLockPatternView.postDelayed(mClearPatternRunnable, mWrongPatternClearTimeOut);
+    }
+
+    private void setPrimaryButtonEnabled(boolean enabled) {
+        mPrimaryButton.setEnabled(enabled);
+    }
+
+    private void setPrimaryButtonTextId(int textId) {
+        mPrimaryButton.setText(textId);
+    }
+
+    private void setSecondaryButtonVisible(boolean visible) {
+        mSecondaryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    private void setSecondaryButtonEnabled(boolean enabled) {
+        mSecondaryButton.setEnabled(enabled);
+    }
+
+    private void setSecondaryButtonTextId(int textId) {
+        mSecondaryButton.setText(textId);
+    }
+
+    /**
+     * The patten used during the help screen to show how to draw a pattern.
+     */
+    private final List<LockPatternView.Cell> mAnimatePattern =
+            Collections.unmodifiableList(Lists.newArrayList(
+                    LockPatternView.Cell.of(0, 0),
+                    LockPatternView.Cell.of(0, 1),
+                    LockPatternView.Cell.of(1, 1),
+                    LockPatternView.Cell.of(2, 1)
+            ));
+
+    private Runnable mClearPatternRunnable = () -> mLockPatternView.clearPattern();
+
+    // Update display message and proceed to next step according to the different text on
+    // the secondary button.
+    private void handleSecondaryButtonClick() {
+        switch(mUiStage.secondaryButtonState) {
+            case Retry:
+                mChosenPattern = null;
+                mLockPatternView.clearPattern();
+                updateStage(Stage.Introduction);
+                break;
+            case Cancel:
+                finish();
+                break;
+            default:
+                throw new IllegalStateException("secondary footer button pressed, but stage of " +
+                        mUiStage + " doesn't make sense");
+        }
+    }
+
+    // Update display message and decide on next step according to the different text
+    // on the primary button
+    private void handlePrimaryButtonClick() {
+        switch(mUiStage.primaryButtonState) {
+            case Continue:
+                if (mUiStage != Stage.FirstChoiceValid) {
+                    throw new IllegalStateException("expected ui stage "
+                            + Stage.FirstChoiceValid + " when button is "
+                            + PrimaryButtonState.Continue);
+                }
+                updateStage(Stage.NeedToConfirm);
+                break;
+            case Confirm:
+                if (mUiStage != Stage.ChoiceConfirmed) {
+                    throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+                            + " when button is " + PrimaryButtonState.Confirm);
+                }
+                startSaveAndFinish();
+                break;
+            case Ok:
+                if (mUiStage != Stage.HelpScreen) {
+                    throw new IllegalStateException("Help screen is only mode with ok button, "
+                            + "but stage is " + mUiStage);
+                }
+                mLockPatternView.clearPattern();
+                mLockPatternView.setDisplayMode(DisplayMode.Correct);
+                updateStage(Stage.Introduction);
+                break;
+            default:
+                // Do nothing.
+        }
+    }
+
+    // Save recorded pattern as an async task and proceed to next
+    private void startSaveAndFinish() {
+        if (mSaveAndFinishWorker != null) {
+            Log.v(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
+            return;
+        }
+
+        setPrimaryButtonEnabled(false);
+
+        mSaveAndFinishWorker = new SaveAndFinishWorker();
+        mSaveAndFinishWorker.setListener(mWorkerListener);
+        mSaveAndFinishWorker.start(new LockPatternUtils(this),
+                mChosenPattern, mCurrentPattern, mUserId);
+    }
+
+    private SaveAndFinishWorker.Listener mWorkerListener = (resultData) -> finish();
+
+    /**
+     * Async task to save the chosen lock pattern.
+     */
+    public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
+        private List<LockPatternView.Cell> mChosenPattern;
+        private String mCurrentPattern;
+
+        public void start(LockPatternUtils utils,
+                List<LockPatternView.Cell> chosenPattern, String currentPattern, int userId) {
+            prepare(utils, userId);
+
+            mCurrentPattern = currentPattern;
+            mChosenPattern = chosenPattern;
+            mUserId = userId;
+
+            start();
+        }
+
+        @Override
+        protected Intent saveAndVerifyInBackground() {
+            Intent result = null;
+            final int userId = mUserId;
+            mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
+            return result;
+        }
+
+        @Override
+        protected void finish(Intent resultData) {
+            if (!mUtils.isPatternEverChosen(mUserId)) {
+                mUtils.setVisiblePatternEnabled(true, mUserId);
+            }
+
+            super.finish(resultData);
+        }
+    }
+}
diff --git a/src/com/android/car/settings/security/ChooseLockTypeFragment.java b/src/com/android/car/settings/security/ChooseLockTypeFragment.java
new file mode 100644
index 0000000..9292407
--- /dev/null
+++ b/src/com/android/car/settings/security/ChooseLockTypeFragment.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.car.settings.security;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.car.settings.R;
+import com.android.car.settings.common.ListItemSettingsFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.ListItem;
+import androidx.car.widget.TextListItem;
+
+/**
+ * Give user choices of lock screen type: Pin/Pattern/Password or None.
+ */
+public class ChooseLockTypeFragment extends ListItemSettingsFragment {
+    static final String LOCK_PATTERN = "lockPattern";
+    static final String LOCK_PIN = "lockPin";
+    static final String LOCK_PASSWORD = "lockPassword";
+    private final List<ListItem> mItems = new ArrayList<>();
+    private ListItemProvider mItemProvider;
+
+    public static ChooseLockTypeFragment newInstance() {
+        ChooseLockTypeFragment chooseLockTypeFragment = new ChooseLockTypeFragment();
+        Bundle bundle = ListItemSettingsFragment.getBundle();
+        bundle.putInt(EXTRA_TITLE_ID, R.string.lock_settings_picker_title);
+        bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar_with_button);
+        chooseLockTypeFragment.setArguments(bundle);
+        return chooseLockTypeFragment;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    @Override
+    public ListItemProvider getItemProvider() {
+        if (mItemProvider == null) {
+            mItemProvider = new ListItemProvider.ListProvider(getListItems());
+        }
+        return mItemProvider;
+    }
+
+    private List<ListItem> getListItems() {
+        List<ListItem> items = new ArrayList<>();
+        items.add(createLockPatternLineItem());
+        return items;
+    }
+
+    private ListItem createLockPatternLineItem() {
+        TextListItem item = new TextListItem(getContext());
+        item.setTitle(getString(R.string.security_lock_pattern));
+        item.setOnClickListener(view -> startChooseSecurityLockActivity(LOCK_PATTERN));
+        return item;
+    }
+
+    private void startChooseSecurityLockActivity(String lockType) {
+        Intent intent = new Intent(getContext(), ChooseSecurityLockActivity.class);
+        intent.putExtra(ChooseSecurityLockActivity.EXTRA_LOCK_TYPE, lockType);
+        getContext().startActivity(intent);
+    }
+}
diff --git a/src/com/android/car/settings/security/ChooseSecurityLockActivity.java b/src/com/android/car/settings/security/ChooseSecurityLockActivity.java
new file mode 100644
index 0000000..7246fba
--- /dev/null
+++ b/src/com/android/car/settings/security/ChooseSecurityLockActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.car.settings.security;
+
+import android.app.Activity;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Bring up different activities to handle choice of security lock types.
+ * i.e. Lock pattern, pin, password.
+ */
+public class ChooseSecurityLockActivity extends Activity {
+    /**
+     * A string to keep the security lock type being chosen.
+     */
+    static final String EXTRA_LOCK_TYPE = "lockType";
+    // Arbitrary request code for choose security lock activity.
+    private static final int REQUEST_CHOOSE_LOCK = 10001;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = new Intent(this, ChooseLockPatternActivity.class);
+        startActivityForResult(intent, REQUEST_CHOOSE_LOCK);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        finish();
+    }
+}
diff --git a/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
new file mode 100644
index 0000000..ff8bd85
--- /dev/null
+++ b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.car.settings.security;
+
+import android.annotation.WorkerThread;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * An invisible retained worker fragment to track the AsyncWork that saves
+ * the chosen lock credential (pattern/pin/password).
+ */
+abstract class SaveChosenLockWorkerBase extends Fragment {
+
+    public static final String EXTRA_KEY_PATTERN = "pattern";
+    public static final String EXTRA_KEY_PASSWORD = "password";
+
+    private Listener mListener;
+    private boolean mFinished;
+    private Intent mResultData;
+
+    protected LockPatternUtils mUtils;
+    protected int mUserId;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    /**
+     * Set the listener to get callback when finished saving the chosen lock.
+     */
+    public void setListener(Listener listener) {
+        if (mListener == listener) {
+            return;
+        }
+
+        mListener = listener;
+        if (mFinished && mListener != null) {
+            mListener.onChosenLockSaveFinished(mResultData);
+        }
+    }
+
+    /**
+     * Set the initial state for the async task.
+     */
+    protected void prepare(LockPatternUtils utils, int userId) {
+        mUtils = utils;
+        mUserId = userId;
+
+        mFinished = false;
+        mResultData = null;
+    }
+
+    /**
+     * Start executing the async task.
+     */
+    protected void start() {
+        new Task().execute();
+    }
+
+    /**
+     * Executes the save and verify work in background.
+     */
+    @WorkerThread
+    protected abstract Intent saveAndVerifyInBackground();
+
+    /**
+     * Send result data via the listener when task finishes.
+     */
+    protected void finish(Intent resultData) {
+        mFinished = true;
+        mResultData = resultData;
+        if (mListener != null) {
+            mListener.onChosenLockSaveFinished(mResultData);
+        }
+    }
+
+    // Save chosen lock task.
+    private class Task extends AsyncTask<Void, Void, Intent> {
+        @Override
+        protected Intent doInBackground(Void... params){
+            return saveAndVerifyInBackground();
+        }
+
+        @Override
+        protected void onPostExecute(Intent resultData) {
+            finish(resultData);
+        }
+    }
+
+    /**
+     * Call back when finishing save the chosen lock.
+     */
+    interface Listener {
+        public void onChosenLockSaveFinished(Intent resultData);
+    }
+}
\ No newline at end of file