Merge "Add basic settings suggestion support" into pi-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 433725d..f873183 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -412,6 +412,8 @@
     <string name="lockpattern_restart_button_text">Redraw</string>
     <!-- Choose security lock screen button. After first valid enter, they will be prompt to confirm page. [CHAR LIMIT=20] -->
     <string name="continue_button_text">Continue</string>
+    <!-- Security lock screen button. If error is encountered when saving password, PIN or pattern, the user can click this button to try saving again. [CHAR LIMIT=20] -->
+    <string name="lockscreen_retry_button_text">Retry</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] -->
@@ -436,6 +438,8 @@
     <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>
+    <!-- Message shown when error is encountered when saving pattern [CHAR LIMIT=40] -->
+    <string name="error_saving_lockpattern">Error saving pattern</string>
     <!-- Label for button to confirm picker selection [CHAR_LIMIT=20] -->
     <string name="okay">OK</string>
 
@@ -449,6 +453,8 @@
     <string name="lockpin_invalid_pin">Pin invalid, must be at least 4 digits</string>
     <!-- Header on pin confirm screen if second pin doesn't match the first. [CHAR LIMIT=30]-->
     <string name="confirm_pins_dont_match">PINs don\'t match</string>
+    <!-- Message shown when error is encountered when saving PIN [CHAR LIMIT=40] -->
+    <string name="error_saving_lockpin">Error saving PIN</string>
     <!-- Description for Choose Lock Password Screen [CHAR_LIMIT=40] -->
     <string name="choose_lock_password_message">For security, set a password</string>
     <!-- Header on password confirm screen [CHAR LIMIT=40] -->
@@ -530,6 +536,9 @@
     <string name="lockpassword_password_recently_used">Device admin doesn\'t allow using a recent
         password</string>
 
+    <!-- Message shown when error is encountered when saving password [CHAR LIMIT=40] -->
+    <string name="error_saving_password">Error saving password</string>
+
     <!-- Error shown when a user is choosing a PASSWORD for their work phone, but what they suggest is blocked by their company's IT administrator. The user should try another PASSWORD that's less common and more complicated. [CHAR LIMIT=NONE] -->
     <string name="lockpassword_password_blacklisted_by_admin">Common passwords are blocked by your IT admin. Try a different password.</string>
 
diff --git a/src/com/android/car/settings/accounts/UserDetailsFragment.java b/src/com/android/car/settings/accounts/UserDetailsFragment.java
index e9ef045..50e65d6 100644
--- a/src/com/android/car/settings/accounts/UserDetailsFragment.java
+++ b/src/com/android/car/settings/accounts/UserDetailsFragment.java
@@ -20,6 +20,7 @@
 import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.view.View;
 import android.widget.Button;
 
 import com.android.car.settings.R;
@@ -71,8 +72,12 @@
         super.onActivityCreated(savedInstanceState);
 
         mAddAccountButton = (Button) getActivity().findViewById(R.id.action_button1);
-        mAddAccountButton.setText(R.string.user_add_account_menu);
-        mAddAccountButton.setOnClickListener(v -> onAddAccountClicked());
+        if(mUserManagerHelper.canModifyAccounts()) {
+            mAddAccountButton.setText(R.string.user_add_account_menu);
+            mAddAccountButton.setOnClickListener(v -> onAddAccountClicked());
+        } else {
+            mAddAccountButton.setVisibility(View.GONE);
+        }
     }
 
     @Override
@@ -80,6 +85,9 @@
         super.onDestroy();
         mUserManagerHelper.unregisterOnUsersUpdateListener();
         mAccountManagerHelper.stopListeningToAccountUpdates();
+
+        // The action button may be hidden at some point, so make it visible again
+        mAddAccountButton.setVisibility(View.VISIBLE);
     }
 
     @Override
diff --git a/src/com/android/car/settings/security/ChooseLockPasswordActivity.java b/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
index 8a81b84..063ed25 100644
--- a/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
+++ b/src/com/android/car/settings/security/ChooseLockPasswordActivity.java
@@ -16,13 +16,6 @@
 
 package com.android.car.settings.security;
 
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
-
-import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.content.Intent;
@@ -31,6 +24,7 @@
 import android.os.Message;
 import android.os.UserHandle;
 import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
 import android.support.v4.app.FragmentActivity;
 import android.text.Editable;
 import android.text.InputType;
@@ -48,20 +42,21 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.car.settings.R;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.TextViewInputDisabler;
 
-import com.android.car.settings.R;
-
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Activity for choosing a lock password/pin.
  */
-public class ChooseLockPasswordActivity extends FragmentActivity
-        implements OnEditorActionListener, TextWatcher, View.OnClickListener {
+public class ChooseLockPasswordActivity extends FragmentActivity implements
+        OnEditorActionListener,
+        TextWatcher,
+        View.OnClickListener,
+        SaveChosenLockWorkerBase.Listener {
     /**
      * Password must contain at least one number, one letter,
      * can not have white space, should be between 4 to 8 characters.
@@ -88,11 +83,9 @@
     @VisibleForTesting
     static final int BLACKLISTED = 1 << 5;
 
-    private static final String TAG = "ChooseLockPattern";
+    private static final String TAG = "ChooseLockPassword";
     private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
 
-    private static final int RESULT_FINISHED = RESULT_FIRST_USER;
-
     private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
     private boolean mIsAlphaMode;
     private String mEnteredPassword;
@@ -100,7 +93,7 @@
     private EditText mPasswordEntry;
     private TextViewInputDisabler mPasswordEntryInputDisabler;
 
-    private SaveAndFinishWorker mSaveAndFinishWorker;
+    private SaveLockPasswordWorker mSaveLockPasswordWorker;
 
     private String mFirstPassword;
 
@@ -111,25 +104,61 @@
 
     private TextChangedHandler mTextChangedHandler;
 
-    private final SaveAndFinishWorker.Listener mWorkerListener =
-            new SaveAndFinishWorker.Listener() {
-                @Override
-                public void onChosenLockSaveFinished(Intent resultData) {
-                    // TODO b/73551228 - handle failure to save
-                    setResult(RESULT_OK, resultData);
-                    finish();
-                }
-            };
+    // Keep track internally of where the user is in choosing a password.
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    protected enum Stage {
+        Introduction(
+                R.string.choose_lock_password_hints,
+                R.string.choose_lock_pin_hints,
+                R.string.continue_button_text,
+                R.string.lockpassword_cancel_label),
 
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
+        PasswordInvalid(
+                R.string.lockpassword_invalid_password,
+                R.string.lockpin_invalid_pin,
+                R.string.continue_button_text,
+                R.string.lockpassword_clear_label),
 
-        if (resultCode != Activity.RESULT_OK) {
-            setResult(RESULT_FINISHED);
-            finish();
-        } else {
-            mCurrentPassword = data.getStringExtra(SaveChosenLockWorkerBase.EXTRA_KEY_PASSWORD);
+        NeedToConfirm(
+                R.string.confirm_your_password_header,
+                R.string.confirm_your_pin_header,
+                R.string.lockpassword_confirm_label,
+                R.string.lockpassword_cancel_label),
+
+        ConfirmWrong(
+                R.string.confirm_passwords_dont_match,
+                R.string.confirm_pins_dont_match,
+                R.string.continue_button_text,
+                R.string.lockpassword_cancel_label),
+
+        SaveFailure(
+                R.string.error_saving_password,
+                R.string.error_saving_lockpin,
+                R.string.lockscreen_retry_button_text,
+                R.string.lockpassword_cancel_label);
+
+        public final int alphaHint;
+        public final int numericHint;
+        public final int primaryButtonText;
+        public final int secondaryButtonText;
+
+        Stage(int hintInAlpha,
+                int hintInNumeric,
+                int primaryButtonText,
+                int secondaryButtonText) {
+            this.alphaHint = hintInAlpha;
+            this.numericHint = hintInNumeric;
+            this.primaryButtonText = primaryButtonText;
+            this.secondaryButtonText = secondaryButtonText;
+        }
+
+        @StringRes
+        public int getHint(boolean isAlpha) {
+            if (isAlpha) {
+                return alphaHint;
+            } else {
+                return numericHint;
+            }
         }
     }
 
@@ -174,6 +203,19 @@
         }
     }
 
+    @Override
+    public void onChosenLockSaveFinished(Intent data) {
+        boolean isSaveSuccessful =
+                data.getBooleanExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+        if (isSaveSuccessful) {
+            setResult(RESULT_OK, data);
+            finish();
+        } else {
+            mSaveLockPasswordWorker = null;  // Allow new worker to be created
+            updateStage(Stage.SaveFailure);
+        }
+    }
+
     /**
      * Validates PIN/Password and returns the validation result.
      *
@@ -187,13 +229,13 @@
 
         // Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
         // user finds some way to bring up soft keyboard.
-        if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
-                || mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+        if (mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+                || mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX) {
             if (metrics.letters > 0 || metrics.symbols > 0) {
                 errorCode |= CONTAIN_NON_DIGITS;
             }
 
-            if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+            if (mRequestedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX) {
                 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
                 final int sequence = PasswordMetrics.maxLengthSequence(password);
                 if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
@@ -218,7 +260,7 @@
     }
 
     @VisibleForTesting
-    protected void setPasswordQuality(int passwordQuality) {
+    void setPasswordQuality(int passwordQuality) {
         mRequestedQuality = passwordQuality;
     }
 
@@ -270,58 +312,10 @@
     }
 
     @Override
-    protected void onStart() {
-        super.onStart();
-    }
-
-    // Keep track internally of where the user is in choosing a password.
-    private enum Stage {
-        Introduction(
-                R.string.choose_lock_password_hints,
-                R.string.choose_lock_pin_hints,
-                R.string.continue_button_text,
-                R.string.lockpassword_cancel_label),
-
-        PasswordInvalid(
-                R.string.lockpassword_invalid_password,
-                R.string.lockpin_invalid_pin,
-                R.string.continue_button_text,
-                R.string.lockpassword_clear_label),
-
-        NeedToConfirm(
-                R.string.confirm_your_password_header,
-                R.string.confirm_your_pin_header,
-                R.string.lockpassword_confirm_label,
-                R.string.lockpassword_cancel_label),
-
-        ConfirmWrong(
-                R.string.confirm_passwords_dont_match,
-                R.string.confirm_pins_dont_match,
-                R.string.continue_button_text,
-                R.string.lockpassword_cancel_label);
-
-        Stage(int hintInAlpha,
-                int hintInNumeric,
-                int primaryButtonText,
-                int secondaryButtonText) {
-            this.alphaHint = hintInAlpha;
-            this.numericHint = hintInNumeric;
-            this.primaryButtonText = primaryButtonText;
-            this.secondaryButtonText = secondaryButtonText;
-        }
-
-        public final int alphaHint;
-        public final int numericHint;
-        public final int primaryButtonText;
-        public final int secondaryButtonText;
-
-        @StringRes
-        public int getHint(boolean isAlpha) {
-            if (isAlpha) {
-                return alphaHint;
-            } else {
-                return numericHint;
-            }
+    public void onStop() {
+        super.onStop();
+        if (mSaveLockPasswordWorker != null) {
+            mSaveLockPasswordWorker.setListener(null);
         }
     }
 
@@ -333,10 +327,6 @@
         mPrimaryButton.setText(textId);
     }
 
-    private void setSecondaryButtonVisible(boolean visible) {
-        mSecondaryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
-    }
-
     private void setSecondaryButtonEnabled(boolean enabled) {
         mSecondaryButton.setEnabled(enabled);
     }
@@ -348,7 +338,7 @@
     // Updates display message and proceed to next step according to the different text on
     // the secondary button.
     private void handleSecondaryButtonClick() {
-        if (mSaveAndFinishWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
+        if (mSaveLockPasswordWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
             finish();
             return;
         }
@@ -366,7 +356,7 @@
     // the primary button.
     private void handlePrimaryButtonClick() {
         mEnteredPassword = mPasswordEntry.getText().toString();
-        if (mSaveAndFinishWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
+        if (mSaveLockPasswordWorker != null || TextUtils.isEmpty(mEnteredPassword)) {
             return;
         }
 
@@ -381,6 +371,7 @@
                 }
                 break;
             case NeedToConfirm:
+            case SaveFailure:
                 if (mFirstPassword.equals(mEnteredPassword)) {
                     startSaveAndFinish();
                 } else {
@@ -398,7 +389,7 @@
 
     // Starts an async task to save the chosen password.
     private void startSaveAndFinish() {
-        if (mSaveAndFinishWorker != null) {
+        if (mSaveLockPasswordWorker != null) {
             Log.v(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
             return;
         }
@@ -406,22 +397,20 @@
         mPasswordEntryInputDisabler.setInputEnabled(false);
         setPrimaryButtonEnabled(false);
 
-        mSaveAndFinishWorker = new SaveAndFinishWorker();
-        mSaveAndFinishWorker.setListener(mWorkerListener);
+        mSaveLockPasswordWorker = new SaveLockPasswordWorker();
+        mSaveLockPasswordWorker.setListener(this);
 
-        getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
+        getFragmentManager().beginTransaction().add(mSaveLockPasswordWorker,
                 FRAGMENT_TAG_SAVE_AND_FINISH).commit();
         getFragmentManager().executePendingTransactions();
 
-        mSaveAndFinishWorker = new SaveAndFinishWorker();
-        mSaveAndFinishWorker.setListener(mWorkerListener);
-        mSaveAndFinishWorker.start(new LockPatternUtils(this),
+        mSaveLockPasswordWorker.start(new LockPatternUtils(this),
                 mEnteredPassword, mCurrentPassword, mRequestedQuality, mUserId);
     }
 
     // Updates the hint based on current Stage and length of password entry
     private void updateUi() {
-        boolean canInput = mSaveAndFinishWorker == null;
+        boolean inputAllowed = mSaveLockPasswordWorker == null;
 
         if (mUiStage == Stage.Introduction) {
             final int errorCode = validatePassword(mEnteredPassword);
@@ -433,16 +422,17 @@
         } else {
             mHintMessage.setText(getString(mUiStage.getHint(mIsAlphaMode)));
             boolean hasPassword = mEnteredPassword == "" ? false : mEnteredPassword.length() > 0;
-            setPrimaryButtonEnabled(canInput && hasPassword);
-            setSecondaryButtonEnabled(canInput && hasPassword);
+            setPrimaryButtonEnabled(inputAllowed && hasPassword);
+            setSecondaryButtonEnabled(inputAllowed && hasPassword);
         }
 
         setPrimaryButtonTextId(mUiStage.primaryButtonText);
         setSecondaryButtonTextId(mUiStage.secondaryButtonText);
-        mPasswordEntryInputDisabler.setInputEnabled(canInput);
+        mPasswordEntryInputDisabler.setInputEnabled(inputAllowed);
     }
 
-    private void updateStage(Stage stage) {
+    @VisibleForTesting
+    void updateStage(Stage stage) {
         mUiStage = stage;
         updateUi();
     }
@@ -492,35 +482,4 @@
         }
         return messages.toArray(new String[0]);
     }
-
-    /**
-     * Worker to store chosen password using LockPatternUtils.
-     */
-    public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
-        private String mEnteredPassword;
-        private String mCurrentPassword;
-        private int mRequestedQuality;
-
-        public void start(LockPatternUtils utils,
-                String enteredPassword, String currentPassword, int requestedQuality, int userId) {
-            prepare(utils, userId);
-
-            mEnteredPassword = enteredPassword;
-            mCurrentPassword = currentPassword;
-            mRequestedQuality = requestedQuality;
-            mUserId = userId;
-
-            start();
-        }
-
-        @Override
-        protected Intent saveAndVerifyInBackground() {
-            Intent result = null;
-            mUtils.saveLockPassword(mEnteredPassword, mCurrentPassword, mRequestedQuality,
-                    mUserId);
-
-            return result;
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/car/settings/security/ChooseLockPatternActivity.java b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
index 06dd065..6209bf1 100644
--- a/src/com/android/car/settings/security/ChooseLockPatternActivity.java
+++ b/src/com/android/car/settings/security/ChooseLockPatternActivity.java
@@ -26,13 +26,12 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.car.settings.R;
 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;
@@ -42,7 +41,9 @@
 /**
  * Activity for choosing security lock pattern.
  */
-public class ChooseLockPatternActivity extends FragmentActivity implements View.OnClickListener {
+public class ChooseLockPatternActivity extends FragmentActivity implements
+        View.OnClickListener, SaveChosenLockWorkerBase.Listener {
+
     private static final String TAG = "ChooseLockPattern";
     private static final int ID_EMPTY_MESSAGE = -1;
     // How long we wait to clear a wrong pattern
@@ -55,7 +56,7 @@
 
     private List<LockPatternView.Cell> mChosenPattern;
     private String mCurrentPattern;
-    private SaveAndFinishWorker mSaveAndFinishWorker;
+    private SaveLockPatternWorker mSaveLockPatternWorker;
     private int mUserId;
 
     @Override
@@ -105,17 +106,31 @@
         super.onStart();
         updateStage(mUiStage);
 
-        if (mSaveAndFinishWorker != null) {
+        if (mSaveLockPatternWorker != null) {
             setPrimaryButtonEnabled(true);
-            mSaveAndFinishWorker.setListener(mWorkerListener);
+            mSaveLockPatternWorker.setListener(this);
         }
     }
 
     @Override
     public void onStop() {
         super.onStop();
-        if (mSaveAndFinishWorker != null) {
-            mSaveAndFinishWorker.setListener(null);
+        if (mSaveLockPatternWorker != null) {
+            mSaveLockPatternWorker.setListener(null);
+        }
+    }
+
+    @Override
+    public void onChosenLockSaveFinished(Intent data) {
+        boolean isSaveSuccessful =
+                data.getBooleanExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+
+        if (isSaveSuccessful) {
+            setResult(RESULT_OK, data);
+            finish();
+        } else {
+            mSaveLockPatternWorker = null;  // Allow new worker to be created
+            updateStage(Stage.SaveFailure);
         }
     }
 
@@ -179,8 +194,21 @@
         ChoiceConfirmed(
                 R.string.lockpattern_pattern_confirmed,
                 SecondaryButtonState.Cancel, PrimaryButtonState.Confirm,
+                false /* patternEnabled */ ),
+
+        /**
+         * Error saving pattern.
+         * Pattern disabled, primary button shows Retry, secondary button allows for cancel
+         */
+        SaveFailure(
+                R.string.error_saving_lockpattern,
+                SecondaryButtonState.Cancel, PrimaryButtonState.Retry,
                 false /* patternEnabled */ );
 
+        final int messageId;
+        final SecondaryButtonState secondaryButtonState;
+        final PrimaryButtonState primaryButtonState;
+        final boolean patternEnabled;
 
         /**
          * @param message The message displayed as instruction.
@@ -197,11 +225,6 @@
             this.primaryButtonState = primaryButtonState;
             this.patternEnabled = patternEnabled;
         }
-
-        final int messageId;
-        final SecondaryButtonState secondaryButtonState;
-        final PrimaryButtonState primaryButtonState;
-        final boolean patternEnabled;
     }
 
     /**
@@ -297,6 +320,7 @@
         ContinueDisabled(R.string.continue_button_text, false),
         Confirm(R.string.lockpattern_confirm_button_text, true),
         ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
+        Retry(R.string.lockscreen_retry_button_text, true),
         Ok(R.string.okay, true);
 
         /**
@@ -452,6 +476,13 @@
                 }
                 startSaveAndFinish();
                 break;
+            case Retry:
+                if (mUiStage != Stage.SaveFailure) {
+                    throw new IllegalStateException("expected ui stage " + Stage.SaveFailure
+                            + " when button is " + PrimaryButtonState.Retry);
+                }
+                startSaveAndFinish();
+                break;
             case Ok:
                 if (mUiStage != Stage.HelpScreen) {
                     throw new IllegalStateException("Help screen is only mode with ok button, "
@@ -468,57 +499,18 @@
 
     // 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.");
+        if (mSaveLockPatternWorker != null) {
+            Log.v(TAG, "startSaveAndFinish with an existing SaveLockPatternWorker.");
             return;
         }
 
         setPrimaryButtonEnabled(false);
 
-        mSaveAndFinishWorker = new SaveAndFinishWorker();
-        mSaveAndFinishWorker.setListener(mWorkerListener);
-        mSaveAndFinishWorker.start(new LockPatternUtils(this),
+        mSaveLockPatternWorker = new SaveLockPatternWorker();
+        mSaveLockPatternWorker.setListener(this);
+        mSaveLockPatternWorker.start(new LockPatternUtils(this),
                 mChosenPattern, mCurrentPattern, mUserId);
     }
 
-    private SaveAndFinishWorker.Listener mWorkerListener = (resultData) -> {
-        setResult(RESULT_OK);
-        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/SaveChosenLockWorkerBase.java b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
index ff8bd85..1626ea5 100644
--- a/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
+++ b/src/com/android/car/settings/security/SaveChosenLockWorkerBase.java
@@ -16,6 +16,7 @@
 
 package com.android.car.settings.security;
 
+import android.annotation.CallSuper;
 import android.annotation.WorkerThread;
 import android.app.Fragment;
 import android.content.Intent;
@@ -32,13 +33,21 @@
 
     public static final String EXTRA_KEY_PATTERN = "pattern";
     public static final String EXTRA_KEY_PASSWORD = "password";
+    public static final String EXTRA_KEY_SUCCESS = "success";
 
     private Listener mListener;
     private boolean mFinished;
     private Intent mResultData;
+    private LockPatternUtils mUtils;
+    private int mUserId;
 
-    protected LockPatternUtils mUtils;
-    protected int mUserId;
+    final LockPatternUtils getUtils() {
+        return mUtils;
+    }
+
+    final int getUserId() {
+        return mUserId;
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -74,7 +83,7 @@
     /**
      * Start executing the async task.
      */
-    protected void start() {
+    final void start() {
         new Task().execute();
     }
 
@@ -87,6 +96,7 @@
     /**
      * Send result data via the listener when task finishes.
      */
+    @CallSuper
     protected void finish(Intent resultData) {
         mFinished = true;
         mResultData = resultData;
@@ -112,6 +122,6 @@
      * Call back when finishing save the chosen lock.
      */
     interface Listener {
-        public void onChosenLockSaveFinished(Intent resultData);
+        void onChosenLockSaveFinished(Intent resultData);
     }
 }
\ No newline at end of file
diff --git a/src/com/android/car/settings/security/SaveLockPasswordWorker.java b/src/com/android/car/settings/security/SaveLockPasswordWorker.java
new file mode 100644
index 0000000..f46e3fd
--- /dev/null
+++ b/src/com/android/car/settings/security/SaveLockPasswordWorker.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.car.settings.security;
+
+
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Worker to store chosen password using LockPatternUtils.
+ */
+public class SaveLockPasswordWorker extends SaveChosenLockWorkerBase {
+
+    private final String TAG = "SavePasswordWorker";
+    private String mEnteredPassword;
+    private String mCurrentPassword;
+    private int mRequestedQuality;
+
+
+    public void start(LockPatternUtils utils,
+            String enteredPassword, String currentPassword, int requestedQuality, int userId) {
+        prepare(utils, userId);
+
+        mEnteredPassword = enteredPassword;
+        mCurrentPassword = currentPassword;
+        mRequestedQuality = requestedQuality;
+
+        start();
+    }
+
+    @VisibleForTesting
+    void saveLockPassword() {
+        getUtils().saveLockPassword(mEnteredPassword, mCurrentPassword, mRequestedQuality,
+            getUserId());
+    }
+
+    @Override
+    protected Intent saveAndVerifyInBackground() {
+        Intent result = new Intent();
+        boolean isSaveSuccessful = true;
+
+        try {
+            saveLockPassword();
+        } catch (Exception e) {
+            Log.e(TAG, "Save lock exception", e);
+            isSaveSuccessful = false;
+        }
+
+        result.putExtra(EXTRA_KEY_SUCCESS, isSaveSuccessful);
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/car/settings/security/SaveLockPatternWorker.java b/src/com/android/car/settings/security/SaveLockPatternWorker.java
new file mode 100644
index 0000000..57ed292
--- /dev/null
+++ b/src/com/android/car/settings/security/SaveLockPatternWorker.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package com.android.car.settings.security;
+
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+
+import java.util.List;
+
+/**
+ * Async task to save the chosen lock pattern.
+ */
+public class SaveLockPatternWorker extends SaveChosenLockWorkerBase {
+
+    private final String TAG = "SaveLockPatternWorker";
+    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;
+
+        start();
+    }
+
+    @VisibleForTesting
+    void saveLockPattern() {
+        getUtils().saveLockPattern(mChosenPattern, mCurrentPattern, getUserId());
+    }
+
+    @Override
+    protected Intent saveAndVerifyInBackground() {
+        Intent result = new Intent();
+        boolean isSaveSuccessful = true;
+
+        try {
+            saveLockPattern();
+        } catch (Exception e) {
+            Log.e(TAG, "Save lock exception", e);
+            isSaveSuccessful = false;
+        }
+
+        result.putExtra(EXTRA_KEY_SUCCESS, isSaveSuccessful);
+        return result;
+    }
+
+    @Override
+    protected void finish(Intent resultData) {
+        if (!getUtils().isPatternEverChosen(getUserId())) {
+            getUtils().setVisiblePatternEnabled(true, getUserId());
+        }
+
+        super.finish(resultData);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk
index 67589dc..c0ce577 100644
--- a/tests/robotests/Android.mk
+++ b/tests/robotests/Android.mk
@@ -4,20 +4,18 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := CarSettingsRoboTests
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-# Include the testing libraries (JUnit4 + Robolectric libs).
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    truth-prebuilt \
-    mockito-robolectric-prebuilt
-
+# Include the testing libraries
 LOCAL_JAVA_LIBRARIES := \
-    junit \
-    platform-robolectric-3.6.1-prebuilt \
-    sdk_vcurrent
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt
 
 LOCAL_INSTRUMENTATION_FOR := CarSettings
-LOCAL_MODULE := CarSettingsRoboTests
 
 LOCAL_MODULE_TAGS := optional
 
@@ -30,13 +28,15 @@
 
 LOCAL_MODULE := RunCarSettingsRoboTests
 
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    CarSettingsRoboTests
+LOCAL_JAVA_LIBRARIES := \
+    CarSettingsRoboTests \
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt
 
 LOCAL_TEST_PACKAGE := CarSettings
 
 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
 
-include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
+include external/robolectric-shadows/run_robotests.mk
diff --git a/tests/robotests/config/robolectric.properties b/tests/robotests/config/robolectric.properties
new file mode 100644
index 0000000..e55c12e
--- /dev/null
+++ b/tests/robotests/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+manifest=packages/apps/Car/Settings/tests/robotests/AndroidManifest.xml
+sdk=NEWEST_SDK
diff --git a/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
index 1ab07e8..93c3099 100644
--- a/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
+++ b/tests/robotests/src/com/android/car/settings/CarSettingsRobolectricTestRunner.java
@@ -15,6 +15,8 @@
  */
 package com.android.car.settings;
 
+import android.support.annotation.NonNull;
+
 import org.junit.runners.model.InitializationError;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
@@ -22,6 +24,9 @@
 import org.robolectric.res.Fs;
 import org.robolectric.res.ResourcePath;
 
+import java.net.MalformedURLException;
+
+import java.net.URL;
 import java.util.List;
 
 /**
@@ -38,6 +43,14 @@
         super(testClass);
     }
 
+    private static ResourcePath createResourcePath(@NonNull String filePath) {
+        try {
+            return new ResourcePath(null, Fs.fromURL(new URL(filePath)), null);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("CarSettingsobolectricTestRunner failure", e);
+        }
+    }
+
     /**
      * We are going to create our own custom manifest so that we can add multiple resource
      * paths to it. This lets us access resources in both Settings and SettingsLib in our tests.
@@ -53,38 +66,21 @@
         // By adding any resources from libraries we need to the AndroidManifest, we can access
         // them from within the parallel universe's resource loader.
         return new AndroidManifest(Fs.fileFromPath(manifestPath), Fs.fileFromPath(resDir),
-            Fs.fileFromPath(assetsDir), "com.android.car.settings") {
+            Fs.fileFromPath(assetsDir)) {
             @Override
             public List<ResourcePath> getIncludedResourcePaths() {
                 List<ResourcePath> paths = super.getIncludedResourcePaths();
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./packages/apps/Car/Settings/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./frameworks/base/core/res/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./frameworks/support/car/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./packages/apps/Car/libs/car-stream-ui-lib/res"),
-                        null));
-                paths.add(new ResourcePath(
-                        null,
-                        Fs.fileFromPath("./packages/apps/Car/libs/car-list/res"),
-                        null));
+                paths.add(createResourcePath("file:packages/apps/Car/Settings/res"));
+                paths.add(createResourcePath("file:frameworks/support/v7/appcompat/res"));
+                paths.add(createResourcePath("file:frameworks/support/car/res"));
+                paths.add(createResourcePath("file:packages/apps/Car/libs/car-stream-ui-lib/res "));
+                paths.add(createResourcePath("file:packages/apps/Car/libs/car-list/res"));
+                paths.add(createResourcePath("file:frameworks/base/packages/SettingsLib/res"));
+                paths.add(createResourcePath(
+                        "file:frameworks/opt/setupwizard/library/gingerbread/res"));
+                paths.add(createResourcePath(
+                        "file:frameworks/opt/setupwizard/library/main/res"));
+
                 return paths;
             }
         };
diff --git a/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java b/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
index a4d622c..e8dea99 100644
--- a/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/car/settings/security/ChooseLockPasswordActivityTest.java
@@ -16,31 +16,31 @@
 
 package com.android.car.settings.security;
 
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+
 import com.android.car.settings.CarSettingsRobolectricTestRunner;
-import com.android.car.settings.TestConfig;
-import com.android.car.settings.testutils.ShadowActivityManager;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
 
 /**
  * Tests for ChooseLockPasswordActivity class.
  */
 @RunWith(CarSettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
-        shadows = {ShadowActivityManager.class})
-@Ignore
 public class ChooseLockPasswordActivityTest {
     private ChooseLockPasswordActivity mActivity;
 
@@ -146,4 +146,34 @@
         assertThat(mActivity.validatePassword(password))
                 .isEqualTo(ChooseLockPasswordActivity.CONTAIN_NON_DIGITS);
     }
+
+    /**
+     * A test to verify that the activity is finished when save worker succeeds
+     */
+    @Test
+    public void testActivityIsFinishedWhenSaveWorkerSucceeds() {
+        ChooseLockPasswordActivity spyActivity = spy(mActivity);
+
+        Intent intent = new Intent();
+        intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, true);
+        spyActivity.onChosenLockSaveFinished(intent);
+
+        verify(spyActivity).finish();
+    }
+
+    /**
+     * A test to verify that the UI stage is updated when save worker fails
+     */
+    @Test
+    public void testStageIsUpdatedWhenSaveWorkerFails() {
+        ChooseLockPasswordActivity spyActivity = spy(mActivity);
+        doNothing().when(spyActivity).updateStage(ChooseLockPasswordActivity.Stage.SaveFailure);
+
+        Intent intent = new Intent();
+        intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+        spyActivity.onChosenLockSaveFinished(intent);
+
+        verify(spyActivity, never()).finish();
+        verify(spyActivity).updateStage(ChooseLockPasswordActivity.Stage.SaveFailure);
+    }
 }
diff --git a/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java b/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java
new file mode 100644
index 0000000..cce0e64
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/ChooseLockPatternActivityTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package com.android.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+
+/**
+ * Tests for ChooseLockPatternActivity class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class ChooseLockPatternActivityTest {
+    private ChooseLockPatternActivity mActivity;
+
+    @Before
+    public void setUp() {
+        mActivity = Robolectric.buildActivity(ChooseLockPatternActivity.class)
+                .create()
+                .get();
+    }
+
+    /**
+     * A test to verify that the activity is finished when save worker succeeds
+     */
+    @Test
+    public void testActivityIsFinishedWhenSaveWorkerSucceeds() {
+        ChooseLockPatternActivity spyActivity = spy(mActivity);
+
+        Intent intent = new Intent();
+        intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, true);
+        spyActivity.onChosenLockSaveFinished(intent);
+
+        verify(spyActivity).finish();
+    }
+
+    /**
+     * A test to verify that the UI stage is updated when save worker fails
+     */
+    @Test
+    public void testStageIsUpdatedWhenSaveWorkerFails() {
+        ChooseLockPatternActivity spyActivity = spy(mActivity);
+        doNothing().when(spyActivity).updateStage(ChooseLockPatternActivity.Stage.SaveFailure);
+
+        Intent intent = new Intent();
+        intent.putExtra(SaveChosenLockWorkerBase.EXTRA_KEY_SUCCESS, false);
+        spyActivity.onChosenLockSaveFinished(intent);
+
+        verify(spyActivity, never()).finish();
+        verify(spyActivity).updateStage(ChooseLockPatternActivity.Stage.SaveFailure);
+    }
+}
diff --git a/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java b/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java
new file mode 100644
index 0000000..94c5329
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/SaveLockPasswordWorkerTest.java
@@ -0,0 +1,66 @@
+/*
+* 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.
+*/
+
+package com.android.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for SaveLockPasswordWorker class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class SaveLockPasswordWorkerTest {
+    /**
+     * A test to check return value when save worker succeeds
+     */
+    @Test
+    public void testSaveLockSuccessReturnsTrue() {
+        SaveLockPasswordWorker worker = spy(new SaveLockPasswordWorker());
+
+        doNothing().when(worker).saveLockPassword();
+
+        assertThat(worker
+                .saveAndVerifyInBackground()
+                .getBooleanExtra(SaveLockPasswordWorker.EXTRA_KEY_SUCCESS,
+                        false))
+                .isTrue();
+    }
+
+    /**
+     * A test to check return value when save worker fails
+     */
+    @Test
+    public void testSaveLockFailureReturnsFalse() {
+        SaveLockPasswordWorker worker = spy(new SaveLockPasswordWorker());
+
+        doThrow(new RuntimeException()).when(worker).saveLockPassword();
+
+        assertThat(worker
+                .saveAndVerifyInBackground()
+                .getBooleanExtra(SaveLockPasswordWorker.EXTRA_KEY_SUCCESS,
+                        true))
+                .isFalse();
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java b/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java
new file mode 100644
index 0000000..11de1dd
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/security/SaveLockPatternWorkerTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package com.android.car.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for SaveLockPatternWorker class.
+ */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+public class SaveLockPatternWorkerTest {
+    /**
+     * A test to check return value when save worker succeeds
+     */
+    @Test
+    public void testSaveLockSuccessReturnsTrue() {
+        SaveLockPatternWorker worker = spy(new SaveLockPatternWorker());
+
+        doNothing().when(worker).saveLockPattern();
+
+        assertThat(worker
+                .saveAndVerifyInBackground()
+                .getBooleanExtra(SaveLockPatternWorker.EXTRA_KEY_SUCCESS,
+                        false))
+                .isTrue();
+    }
+
+    /**
+     * A test to check return value when save worker fails
+     */
+    @Test
+    public void testSaveLockFailureReturnsFalse() {
+        SaveLockPatternWorker worker = spy(new SaveLockPatternWorker());
+
+        doThrow(new RuntimeException()).when(worker).saveLockPattern();
+
+        assertThat(worker
+                .saveAndVerifyInBackground()
+                .getBooleanExtra(SaveLockPatternWorker.EXTRA_KEY_SUCCESS,
+                        true))
+                .isFalse();
+    }
+}
\ No newline at end of file