Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 46eaaaf..88d2f1a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1132,6 +1132,8 @@
     <string name="puk2_blocked">PUK2 is permanently blocked.</string>
     <!-- SIM PIN2 screen: error message -->
     <string name="pin2_attempts">\nYou have <xliff:g id="number">%d</xliff:g> remaining attempts.</string>
+    <!-- SIM PIN2 screen: error message displayed in a dialog -->
+    <string name="puk2_locked">PUK2 locked. Contact service provider to unlock.</string>
     <!-- SIM PIN screen: status message displayed in a popup (toast) -->
     <string name="pin2_unblocked">PIN2 no longer blocked</string>
     <!-- SIM PIN screen: error message shown in dialog when there is a network or sim error.
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index efd2f1b..2b91a24 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -321,13 +321,13 @@
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
                                     unbindIfBound(mContext, conn, phoneId);
+                                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT,
+                                            getMessageToken(phoneId));
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
                                         return;
                                     }
-                                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT,
-                                            getMessageToken(phoneId));
                                     if (resultCode == RESULT_ERROR || resultData == null) {
                                         // On error, abort config fetching.
                                         loge("Failed to get carrier config");
@@ -451,13 +451,13 @@
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
                                     unbindIfBound(mContext, conn, phoneId);
+                                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT,
+                                            getMessageToken(phoneId));
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
                                         return;
                                     }
-                                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT,
-                                            getMessageToken(phoneId));
                                     if (resultCode == RESULT_ERROR || resultData == null) {
                                         // On error, abort config fetching.
                                         loge("Failed to get carrier config from carrier app: "
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 83d71f1..127e08b 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -6560,8 +6560,9 @@
 
     private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid,
             Phone phone) {
-        if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
-            // Skip the check if it's one of these special uids
+        if (uid == Process.PHONE_UID) {
+            // Skip the check if it's the phone UID (system UID removed in b/184713596)
+            // TODO (b/184954344): Check for system/phone UID at call site instead of here
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
         }
 
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureManager.java b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
index 818994a..efb149e 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureManager.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
@@ -96,7 +96,8 @@
                 InputStream input,
                 Executor executor,
                 OutcomeReceiver<Uri, CallLog.CallComposerLoggingException> callback) {
-            CallLog.storeCallComposerPictureAsUser(context, user, input, executor, callback);
+            CallLog.storeCallComposerPicture(context.createContextAsUser(user, 0),
+                    input, executor, callback);
         }
     }
 
diff --git a/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java b/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java
new file mode 100644
index 0000000..5beff34
--- /dev/null
+++ b/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2020 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.phone.settings.fdn;
+
+import static android.view.Window.PROGRESS_VISIBILITY_OFF;
+import static android.view.Window.PROGRESS_VISIBILITY_ON;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Window;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneGlobals;
+import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
+
+/**
+ * Base activity for FDN contact screen.
+ */
+public abstract class BaseFdnContactScreen extends Activity
+        implements Pin2LockedDialogFragment.Listener {
+    protected static final String LOG_TAG = PhoneGlobals.LOG_TAG;
+    protected static final boolean DBG = false;
+
+    protected static final int EVENT_PIN2_ENTRY_COMPLETE = 10;
+    protected static final int PIN2_REQUEST_CODE = 100;
+
+    protected static final String INTENT_EXTRA_NAME = "name";
+    protected static final String INTENT_EXTRA_NUMBER = "number";
+
+    protected String mName;
+    protected String mNumber;
+    protected String mPin2;
+
+    protected SubscriptionInfoHelper mSubscriptionInfoHelper;
+    protected BaseFdnContactScreen.QueryHandler mQueryHandler;
+
+    protected Handler mHandler = new Handler();
+    protected Phone mPhone;
+
+    protected abstract void pin2AuthenticationSucceed();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        resolveIntent();
+        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+    }
+
+    protected void authenticatePin2() {
+        Intent intent = new Intent();
+        intent.setClass(this, GetPin2Screen.class);
+        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
+        startActivityForResult(intent, PIN2_REQUEST_CODE);
+    }
+
+    protected void displayProgress(boolean flag) {
+        getWindow().setFeatureInt(
+                Window.FEATURE_INDETERMINATE_PROGRESS,
+                flag ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
+    }
+
+    protected void handleResult(boolean success) {
+    }
+
+    protected void handleResult(boolean success, boolean invalidNumber) {
+    }
+
+    protected void log(String msg) {
+        Log.d(LOG_TAG, getClass().getSimpleName() + " : " + msg);
+    }
+
+    // Add method to check if Pin2 supplied is correct.
+    protected void processPin2(String pin) {
+        Message onComplete = mFDNHandler
+                .obtainMessage(EVENT_PIN2_ENTRY_COMPLETE);
+        mPhone.getIccCard().supplyPin2(pin, onComplete);
+    }
+
+    protected void resolveIntent() {
+        Intent intent = getIntent();
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
+        mPhone = mSubscriptionInfoHelper.getPhone();
+
+        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
+        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
+    }
+
+    /**
+     * Removed the status field, with preference to displaying a toast
+     * to match the rest of settings UI.
+     */
+    protected void showStatus(CharSequence statusMsg) {
+        if (statusMsg != null) {
+            Toast.makeText(this, statusMsg, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private Handler mFDNHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_PIN2_ENTRY_COMPLETE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        // see if PUK2 is requested and alert the user accordingly.
+                        CommandException ce = (CommandException) ar.exception;
+                        if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
+                            // make sure we set the PUK2 state so that we can skip some
+                            // redundant behaviour.
+                            showPin2LockedDialog();
+                        } else {
+                            final int attemptsRemaining = msg.arg1;
+                            if (attemptsRemaining > 0) {
+                                Toast.makeText(
+                                        BaseFdnContactScreen.this,
+                                        getString(R.string.pin2_invalid)
+                                                + getString(R.string.pin2_attempts,
+                                                        attemptsRemaining), Toast.LENGTH_LONG)
+                                        .show();
+                                finish();
+                            }
+                        }
+                    } else {
+                        pin2AuthenticationSucceed();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    protected class QueryHandler extends AsyncQueryHandler {
+        protected QueryHandler(ContentResolver cr) {
+            super(cr);
+        }
+
+        @Override
+        protected void onInsertComplete(int token, Object cookie, Uri uri) {
+            if (DBG) log("onInsertComplete");
+            displayProgress(false);
+            handleResult(uri != null, false);
+        }
+
+        @Override
+        protected void onUpdateComplete(int token, Object cookie, int result) {
+            if (DBG) log("onUpdateComplete");
+            displayProgress(false);
+            handleResult(result > 0, false);
+        }
+
+        @Override
+        protected void onDeleteComplete(int token, Object cookie, int result) {
+            if (DBG) log("onDeleteComplete");
+            displayProgress(false);
+            handleResult(result > 0);
+        }
+    }
+
+    private void showPin2LockedDialog() {
+        final FragmentManager fragmentManager = getFragmentManager();
+        Pin2LockedDialogFragment dialogFragment = (Pin2LockedDialogFragment) fragmentManager
+                .findFragmentByTag(Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        if (dialogFragment == null) {
+            dialogFragment = new Pin2LockedDialogFragment();
+            Bundle args = new Bundle();
+            args.putInt(Pin2LockedDialogFragment.KEY_DIALOG_ID,
+                    Pin2LockedDialogFragment.DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY);
+            dialogFragment.setArguments(args);
+            dialogFragment.show(fragmentManager, Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        } else {
+            FragmentTransaction transaction = fragmentManager.beginTransaction();
+            transaction.show(dialogFragment);
+            transaction.commitNow();
+        }
+    }
+
+    @Override
+    public void onRequestPuk2(int id) {
+        finish();
+    }
+}
diff --git a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
index 8b17cfb..7cd4c93 100644
--- a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
@@ -16,58 +16,24 @@
 
 package com.android.phone.settings.fdn;
 
-import static android.view.Window.PROGRESS_VISIBILITY_OFF;
-import static android.view.Window.PROGRESS_VISIBILITY_ON;
-
-import android.app.Activity;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
 import android.content.Intent;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.text.TextUtils;
-import android.util.Log;
-import android.view.Window;
-import android.widget.Toast;
 
-import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
-import com.android.phone.SubscriptionInfoHelper;
 
 /**
  * Activity to let the user delete an FDN contact.
  */
-public class DeleteFdnContactScreen extends Activity {
-    private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
-    private static final boolean DBG = false;
-
-    private static final String INTENT_EXTRA_NAME = "name";
-    private static final String INTENT_EXTRA_NUMBER = "number";
-
-    private static final int PIN2_REQUEST_CODE = 100;
-
-    private SubscriptionInfoHelper mSubscriptionInfoHelper;
-
-    private String mName;
-    private String mNumber;
-    private String mPin2;
-
-    protected QueryHandler mQueryHandler;
-
-    private Handler mHandler = new Handler();
+public class DeleteFdnContactScreen extends BaseFdnContactScreen {
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        resolveIntent();
-
         // Starts PIN2 authentication only for the first time.
         if (icicle == null) authenticatePin2();
-
-        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setContentView(R.layout.delete_fdn_contact_screen);
     }
 
@@ -80,9 +46,7 @@
                 Bundle extras = (intent != null) ? intent.getExtras() : null;
                 if (extras != null) {
                     mPin2 = extras.getString("pin2");
-                    showStatus(getResources().getText(
-                            R.string.deleting_fdn_contact));
-                    deleteContact();
+                    processPin2(mPin2);
                 } else {
                     // if they cancelled, then we just cancel too.
                     if (DBG) log("onActivityResult: CANCELLED");
@@ -93,13 +57,9 @@
         }
     }
 
-    private void resolveIntent() {
-        Intent intent = getIntent();
-
-        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
-
-        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
-        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
+    @Override
+    protected void resolveIntent() {
+        super.resolveIntent();
 
         if (TextUtils.isEmpty(mNumber)) {
             finish();
@@ -127,29 +87,8 @@
         displayProgress(true);
     }
 
-    private void authenticatePin2() {
-        Intent intent = new Intent();
-        intent.setClass(this, GetPin2Screen.class);
-        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
-        startActivityForResult(intent, PIN2_REQUEST_CODE);
-    }
-
-    private void displayProgress(boolean flag) {
-        getWindow().setFeatureInt(
-                Window.FEATURE_INDETERMINATE_PROGRESS,
-                flag ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
-    }
-
-    // Replace the status field with a toast to make things appear similar
-    // to the rest of the settings.  Removed the useless status field.
-    private void showStatus(CharSequence statusMsg) {
-        if (statusMsg != null) {
-            Toast.makeText(this, statusMsg, Toast.LENGTH_SHORT)
-            .show();
-        }
-    }
-
-    private void handleResult(boolean success) {
+    @Override
+    protected void handleResult(boolean success) {
         if (success) {
             if (DBG) log("handleResult: success!");
             showStatus(getResources().getText(R.string.fdn_contact_deleted));
@@ -157,43 +96,12 @@
             if (DBG) log("handleResult: failed!");
             showStatus(getResources().getText(R.string.pin2_invalid));
         }
-
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                finish();
-            }
-        }, 2000);
-
+        mHandler.postDelayed(() -> finish(), 2000);
     }
 
-    private class QueryHandler extends AsyncQueryHandler {
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
-        }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor c) {
-        }
-
-        @Override
-        protected void onInsertComplete(int token, Object cookie, Uri uri) {
-        }
-
-        @Override
-        protected void onUpdateComplete(int token, Object cookie, int result) {
-        }
-
-        @Override
-        protected void onDeleteComplete(int token, Object cookie, int result) {
-            if (DBG) log("onDeleteComplete");
-            displayProgress(false);
-            handleResult(result > 0);
-        }
-
-    }
-
-    private void log(String msg) {
-        Log.d(LOG_TAG, "[DeleteFdnContact] " + msg);
+    @Override
+    protected void pin2AuthenticationSucceed() {
+        showStatus(getResources().getText(R.string.deleting_fdn_contact));
+        deleteContact();
     }
 }
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index 140cc74..468d38f 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -16,21 +16,18 @@
 
 package com.android.phone.settings.fdn;
 
-import static android.view.Window.PROGRESS_VISIBILITY_OFF;
-import static android.view.Window.PROGRESS_VISIBILITY_ON;
 
-import android.app.Activity;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
+import static android.app.Activity.RESULT_OK;
+
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.PersistableBundle;
 import android.provider.ContactsContract.CommonDataKinds;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.Editable;
 import android.text.Selection;
@@ -42,50 +39,31 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.Window;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.telephony.PhoneFactory;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
-import com.android.phone.SubscriptionInfoHelper;
-import android.telephony.CarrierConfigManager;
 
 /**
  * Activity to let the user add or edit an FDN contact.
  */
-public class EditFdnContactScreen extends Activity {
-    private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
-    private static final boolean DBG = false;
+public class EditFdnContactScreen extends BaseFdnContactScreen {
 
     // Menu item codes
     private static final int MENU_IMPORT = 1;
     private static final int MENU_DELETE = 2;
 
-    private static final String INTENT_EXTRA_NAME = "name";
-    private static final String INTENT_EXTRA_NUMBER = "number";
-
-    private static final int PIN2_REQUEST_CODE = 100;
-
-    private SubscriptionInfoHelper mSubscriptionInfoHelper;
-
-    private String mName;
-    private String mNumber;
-    private String mPin2;
     private boolean mAddContact;
-    private QueryHandler mQueryHandler;
 
     private EditText mNameField;
     private EditText mNumberField;
     private LinearLayout mPinFieldContainer;
     private Button mButton;
 
-    private Handler mHandler = new Handler();
-
     /**
      * Constants used in importing from contacts
      */
@@ -108,13 +86,10 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        resolveIntent();
-
-        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setContentView(R.layout.edit_fdn_contact_screen);
         setupView();
         setTitle(mAddContact ? R.string.add_fdn_contact : R.string.edit_fdn_contact);
-        PersistableBundle b = null;
+        PersistableBundle b;
         if (mSubscriptionInfoHelper.hasSubId()) {
             b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
                     mSubscriptionInfoHelper.getSubId());
@@ -145,11 +120,7 @@
                 Bundle extras = (intent != null) ? intent.getExtras() : null;
                 if (extras != null) {
                     mPin2 = extras.getString("pin2");
-                    if (mAddContact) {
-                        addContact();
-                    } else {
-                        updateContact();
-                    }
+                    processPin2(mPin2);
                 } else if (resultCode != RESULT_OK) {
                     // if they cancelled, then we just cancel too.
                     if (DBG) log("onActivityResult: cancelled.");
@@ -231,20 +202,15 @@
         return super.onOptionsItemSelected(item);
     }
 
-    private void resolveIntent() {
-        Intent intent = getIntent();
-
-        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
-
-        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
-        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
-
+    @Override
+    protected void resolveIntent() {
+        super.resolveIntent();
         mAddContact = TextUtils.isEmpty(mNumber);
     }
 
     /**
      * We have multiple layouts, one to indicate that the user needs to
-     * open the keyboard to enter information (if the keybord is hidden).
+     * open the keyboard to enter information (if the keyboard is hidden).
      * So, we need to make sure that the layout here matches that in the
      * layout file.
      */
@@ -374,36 +340,18 @@
         finish();
     }
 
-    private void authenticatePin2() {
-        Intent intent = new Intent();
-        intent.setClass(this, GetPin2Screen.class);
-        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
-        startActivityForResult(intent, PIN2_REQUEST_CODE);
-    }
-
-    private void displayProgress(boolean flag) {
+    @Override
+    protected void displayProgress(boolean flag) {
+        super.displayProgress(flag);
         // indicate we are busy.
         mDataBusy = flag;
-        getWindow().setFeatureInt(
-                Window.FEATURE_INDETERMINATE_PROGRESS,
-                mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
         // make sure we don't allow calls to save when we're
         // not ready for them.
         mButton.setClickable(!mDataBusy);
     }
 
-    /**
-     * Removed the status field, with preference to displaying a toast
-     * to match the rest of settings UI.
-     */
-    private void showStatus(CharSequence statusMsg) {
-        if (statusMsg != null) {
-            Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
-                    .show();
-        }
-    }
-
-    private void handleResult(boolean success, boolean invalidNumber) {
+    @Override
+    protected void handleResult(boolean success, boolean invalidNumber) {
         if (success) {
             if (DBG) log("handleResult: success!");
             showStatus(getResources().getText(mAddContact ?
@@ -426,13 +374,7 @@
             }
         }
 
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                finish();
-            }
-        }, 2000);
-
+        mHandler.postDelayed(() -> finish(), 2000);
     }
 
     private final View.OnClickListener mClicked = new View.OnClickListener() {
@@ -486,35 +428,12 @@
         }
     };
 
-    private class QueryHandler extends AsyncQueryHandler {
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
+    @Override
+    protected void pin2AuthenticationSucceed() {
+        if (mAddContact) {
+            addContact();
+        } else {
+            updateContact();
         }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor c) {
-        }
-
-        @Override
-        protected void onInsertComplete(int token, Object cookie, Uri uri) {
-            if (DBG) log("onInsertComplete");
-            displayProgress(false);
-            handleResult(uri != null, false);
-        }
-
-        @Override
-        protected void onUpdateComplete(int token, Object cookie, int result) {
-            if (DBG) log("onUpdateComplete");
-            displayProgress(false);
-            handleResult(result > 0, false);
-        }
-
-        @Override
-        protected void onDeleteComplete(int token, Object cookie, int result) {
-        }
-    }
-
-    private void log(String msg) {
-        Log.d(LOG_TAG, "[EditFdnContact] " + msg);
     }
 }
diff --git a/src/com/android/phone/settings/fdn/FdnSetting.java b/src/com/android/phone/settings/fdn/FdnSetting.java
index 8b5afa4..8f46c85 100644
--- a/src/com/android/phone/settings/fdn/FdnSetting.java
+++ b/src/com/android/phone/settings/fdn/FdnSetting.java
@@ -17,8 +17,8 @@
 package com.android.phone.settings.fdn;
 
 import android.app.ActionBar;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
@@ -27,7 +27,6 @@
 import android.preference.PreferenceScreen;
 import android.util.Log;
 import android.view.MenuItem;
-import android.view.WindowManager;
 import android.widget.Toast;
 
 import com.android.internal.telephony.CommandException;
@@ -42,7 +41,7 @@
  * Rewritten to look and behave closer to the other preferences.
  */
 public class FdnSetting extends PreferenceActivity
-        implements EditPinPreference.OnPinEnteredListener, DialogInterface.OnCancelListener {
+        implements EditPinPreference.OnPinEnteredListener, Pin2LockedDialogFragment.Listener {
 
     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
     private static final boolean DBG = false;
@@ -56,6 +55,7 @@
      */
     private static final int EVENT_PIN2_ENTRY_COMPLETE = 100;
     private static final int EVENT_PIN2_CHANGE_COMPLETE = 200;
+    private static final int EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN = 300;
 
     // String keys for preference lookup
     private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key";
@@ -82,8 +82,11 @@
     private static final String PIN_CHANGE_STATE_KEY = "pin_change_state_key";
     private static final String OLD_PIN_KEY = "old_pin_key";
     private static final String NEW_PIN_KEY = "new_pin_key";
+    private static final String PUK_KEY = "puk_key";
     private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
     private static final String DIALOG_PIN_ENTRY_KEY = "dialog_pin_entry_key";
+    private static final String FDN_DIALOG_MESSAGE_KEY = "fdn_dialog_message_key";
+    private static final String FDN_DIALOG_PIN_ENTRY_KEY = "fdn_dialog_pin_entry_key";
 
     // size limits for the pin.
     private static final int MIN_PIN_LENGTH = 4;
@@ -94,10 +97,10 @@
      */
     @Override
     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
-        if (preference == mButtonEnableFDN) {
+        if (preference == mButtonEnableFDN && (!mIsPuk2Locked || !positiveResult)) {
             toggleFDNEnable(positiveResult);
-        } else if (preference == mButtonChangePin2){
-            updatePINChangeState(positiveResult);
+        } else {
+            updatePINChangeState(preference, positiveResult);
         }
     }
 
@@ -106,6 +109,12 @@
      */
     private void toggleFDNEnable(boolean positiveResult) {
         if (!positiveResult) {
+            // reset the state on cancel, either to expect PUK2 or PIN2
+            if (!mIsPuk2Locked) {
+                resetPinChangeState();
+            } else {
+                resetPinChangeStateForPUK2();
+            }
             return;
         }
 
@@ -129,10 +138,10 @@
     /**
      * Attempt to change the pin.
      */
-    private void updatePINChangeState(boolean positiveResult) {
+    private void updatePINChangeState(EditPinPreference button, boolean positiveResult) {
         if (DBG) log("updatePINChangeState positive=" + positiveResult
                 + " mPinChangeState=" + mPinChangeState
-                + " mSkipOldPin=" + mIsPuk2Locked);
+                + " mIsPuk2Locked=" + mIsPuk2Locked);
 
         if (!positiveResult) {
             // reset the state on cancel, either to expect PUK2 or PIN2
@@ -155,80 +164,95 @@
         // appears with text to indicate what the issue is.
         switch (mPinChangeState) {
             case PIN_CHANGE_OLD:
-                mOldPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mOldPin = button.getText();
+                button.setText("");
                 // if the pin is not valid, display a message and reset the state.
                 if (validatePin (mOldPin, false)) {
                     mPinChangeState = PIN_CHANGE_NEW;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_NEW:
-                mNewPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mNewPin = button.getText();
+                button.setText("");
                 // if the new pin is not valid, display a message and reset the state.
                 if (validatePin (mNewPin, false)) {
                     mPinChangeState = PIN_CHANGE_REENTER;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_REENTER:
-                // if the re-entered pin is not valid, display a message and reset the state.
-                if (!mNewPin.equals(mButtonChangePin2.getText())) {
-                    mPinChangeState = PIN_CHANGE_NEW;
-                    mButtonChangePin2.setText("");
-                    displayPinChangeDialog(R.string.mismatchPin2, true);
+                if (validatePin(button.getText(), false)) {
+                    // if the re-entered pin is not valid, display a message and reset the state.
+                    if (!mNewPin.equals(button.getText())) {
+                        mPinChangeState = PIN_CHANGE_NEW;
+                        button.setText("");
+                        displayPinChangeDialog(button, R.string.mismatchPin2, true);
+                    } else {
+                        // If the PIN is valid, then we submit the change PIN request or
+                        // display the PUK2 dialog if we KNOW that we're PUK2 locked.
+                        button.setText("");
+                        Message onComplete = mFDNHandler.obtainMessage(
+                                EVENT_PIN2_CHANGE_COMPLETE);
+                        if (!mIsPuk2Locked) {
+                            mPhone.getIccCard().changeIccFdnPassword(mOldPin,
+                                    mNewPin, onComplete);
+                        } else {
+                            mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin,
+                                    onComplete);
+                        }
+                    }
                 } else {
-                    // If the PIN is valid, then we submit the change PIN request.
-                    mButtonChangePin2.setText("");
-                    Message onComplete = mFDNHandler.obtainMessage(
-                            EVENT_PIN2_CHANGE_COMPLETE);
-                    mPhone.getIccCard().changeIccFdnPassword(
-                            mOldPin, mNewPin, onComplete);
+                    button.setText("");
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
-            case PIN_CHANGE_PUK: {
-                    // Doh! too many incorrect requests, PUK requested.
-                    mPuk2 = mButtonChangePin2.getText();
-                    mButtonChangePin2.setText("");
-                    // if the puk is not valid, display
-                    // a message and reset the state.
-                    if (validatePin (mPuk2, true)) {
-                        mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
-                        displayPinChangeDialog();
-                    } else {
-                        displayPinChangeDialog(R.string.invalidPuk2, true);
-                    }
+            case PIN_CHANGE_PUK:
+                // Doh! too many incorrect requests, PUK requested.
+                mPuk2 = button.getText();
+                button.setText("");
+                // if the puk is not valid, display
+                // a message and reset the state.
+                if (validatePin(mPuk2, true)) {
+                    mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
+                    displayPinChangeDialog(button);
+                } else {
+                    displayPinChangeDialog(button, R.string.invalidPuk2, true);
                 }
                 break;
             case PIN_CHANGE_NEW_PIN_FOR_PUK:
-                mNewPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mNewPin = button.getText();
+                button.setText("");
                 // if the new pin is not valid, display
                 // a message and reset the state.
                 if (validatePin (mNewPin, false)) {
                     mPinChangeState = PIN_CHANGE_REENTER_PIN_FOR_PUK;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_REENTER_PIN_FOR_PUK:
                 // if the re-entered pin is not valid, display
                 // a message and reset the state.
-                if (!mNewPin.equals(mButtonChangePin2.getText())) {
+                if (!mNewPin.equals(button.getText())) {
                     mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
-                    mButtonChangePin2.setText("");
-                    displayPinChangeDialog(R.string.mismatchPin2, true);
+                    button.setText("");
+                    displayPinChangeDialog(button, R.string.mismatchPin2, true);
                 } else {
                     // Both puk2 and new pin2 are ready to submit
-                    mButtonChangePin2.setText("");
-                    Message onComplete = mFDNHandler.obtainMessage(
-                            EVENT_PIN2_CHANGE_COMPLETE);
+                    Message onComplete = null;
+                    if (button == mButtonChangePin2) {
+                        button.setText("");
+                        onComplete = mFDNHandler.obtainMessage(EVENT_PIN2_CHANGE_COMPLETE);
+                    } else {
+                        onComplete = mFDNHandler.obtainMessage(
+                                EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN);
+                    }
                     mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin, onComplete);
                 }
                 break;
@@ -246,6 +270,7 @@
                 // when we are enabling FDN, either we are unsuccessful and display
                 // a toast, or just update the UI.
                 case EVENT_PIN2_ENTRY_COMPLETE: {
+                        if (DBG) log("Handle EVENT_PIN2_ENTRY_COMPLETE");
                         AsyncResult ar = (AsyncResult) msg.obj;
                         if (ar.exception != null) {
                             if (ar.exception instanceof CommandException) {
@@ -255,11 +280,8 @@
                                         ((CommandException) ar.exception).getCommandError();
                                 switch (e) {
                                     case SIM_PUK2:
-                                        // make sure we set the PUK2 state so that we can skip
-                                        // some redundant behaviour.
-                                        displayMessage(R.string.fdn_enable_puk2_requested,
-                                                attemptsRemaining);
-                                        resetPinChangeStateForPUK2();
+                                        showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                                .DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY);
                                         break;
                                     case PASSWORD_INCORRECT:
                                         displayMessage(R.string.pin2_invalid, attemptsRemaining);
@@ -279,7 +301,8 @@
                 // when changing the pin we need to pay attention to whether or not
                 // the error requests a PUK (usually after too many incorrect tries)
                 // Set the state accordingly.
-                case EVENT_PIN2_CHANGE_COMPLETE: {
+                case EVENT_PIN2_CHANGE_COMPLETE:
+                case EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN: {
                         if (DBG)
                             log("Handle EVENT_PIN2_CHANGE_COMPLETE");
                         AsyncResult ar = (AsyncResult) msg.obj;
@@ -291,34 +314,24 @@
                                 CommandException ce = (CommandException) ar.exception;
                                 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
                                     // throw an alert dialog on the screen, displaying the
-                                    // request for a PUK2.  set the cancel listener to
-                                    // FdnSetting.onCancel().
-                                    AlertDialog a = new AlertDialog.Builder(FdnSetting.this)
-                                        .setMessage(R.string.puk2_requested)
-                                        .setCancelable(true)
-                                        .setOnCancelListener(FdnSetting.this)
-                                        .setNeutralButton(android.R.string.ok,
-                                                new DialogInterface.OnClickListener() {
-                                                    @Override
-                                                    public void onClick(DialogInterface dialog,
-                                                            int which) {
-                                                        resetPinChangeStateForPUK2();
-                                                        displayPinChangeDialog(0,true);
-                                                    }
-                                                })
-                                        .create();
-                                    a.getWindow().addFlags(
-                                            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-                                    a.show();
+                                    // request for a PUK2.
+                                    showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                            .DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED);
                                 } else {
-                                    // set the correct error message depending upon the state.
-                                    // Reset the state depending upon or knowledge of the PUK state.
-                                    if (!mIsPuk2Locked) {
-                                        displayMessage(R.string.badPin2, attemptsRemaining);
-                                        resetPinChangeState();
+                                    if (mIsPuk2Locked && attemptsRemaining == 0) {
+                                        showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                                .DIALOG_ID_PUK2_LOCKED_OUT);
                                     } else {
-                                        displayMessage(R.string.badPuk2, attemptsRemaining);
-                                        resetPinChangeStateForPUK2();
+                                        // set the correct error message depending upon the state.
+                                        // Reset the state depending upon or knowledge of the PUK
+                                        // state.
+                                        if (!mIsPuk2Locked) {
+                                            displayMessage(R.string.badPin2, attemptsRemaining);
+                                            resetPinChangeState();
+                                        } else {
+                                            displayMessage(R.string.badPuk2, attemptsRemaining);
+                                            resetPinChangeStateForPUK2();
+                                        }
                                     }
                                 }
                             } else {
@@ -332,28 +345,25 @@
                             }
 
                             // reset to normal behaviour on successful change.
+                            if (msg.what == EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN) {
+                                log("Handle EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN");
+                                // activate/deactivate FDN
+                                toggleFDNEnable(true);
+                            }
                             resetPinChangeState();
                         }
                     }
+                    mButtonChangePin2.setText("");
+                    mButtonEnableFDN.setText("");
                     break;
             }
         }
     };
 
     /**
-     * Cancel listener for the PUK2 request alert dialog.
-     */
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        // set the state of the preference and then display the dialog.
-        resetPinChangeStateForPUK2();
-        displayPinChangeDialog(0, true);
-    }
-
-    /**
      * Display a toast for message, like the rest of the settings.
      */
-    private final void displayMessage(int strId, int attemptsRemaining) {
+    private void displayMessage(int strId, int attemptsRemaining) {
         String s = getString(strId);
         if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) ||
                 (strId == R.string.pin2_invalid)) {
@@ -367,22 +377,27 @@
         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
     }
 
-    private final void displayMessage(int strId) {
+    private void displayMessage(int strId) {
         displayMessage(strId, -1);
     }
 
     /**
      * The next two functions are for updating the message field on the dialog.
      */
-    private final void displayPinChangeDialog() {
-        displayPinChangeDialog(0, true);
+    private void displayPinChangeDialog(EditPinPreference button) {
+        displayPinChangeDialog(button, 0, true);
     }
 
-    private final void displayPinChangeDialog(int strId, boolean shouldDisplay) {
+    private void displayPinChangeDialog(EditPinPreference button,
+            int strId, boolean shouldDisplay) {
         int msgId;
         switch (mPinChangeState) {
             case PIN_CHANGE_OLD:
-                msgId = R.string.oldPin2Label;
+                if (button == mButtonEnableFDN) {
+                    msgId = R.string.enter_pin2_text;
+                } else {
+                    msgId = R.string.oldPin2Label;
+                }
                 break;
             case PIN_CHANGE_NEW:
             case PIN_CHANGE_NEW_PIN_FOR_PUK:
@@ -400,14 +415,14 @@
 
         // append the note / additional message, if needed.
         if (strId != 0) {
-            mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId));
+            button.setDialogMessage(getText(msgId) + "\n" + getText(strId));
         } else {
-            mButtonChangePin2.setDialogMessage(msgId);
+            button.setDialogMessage(msgId);
         }
 
         // only display if requested.
         if (shouldDisplay) {
-            mButtonChangePin2.showPinDialog();
+            button.showPinDialog();
         }
     }
 
@@ -417,7 +432,8 @@
     private final void resetPinChangeState() {
         if (DBG) log("resetPinChangeState");
         mPinChangeState = PIN_CHANGE_OLD;
-        displayPinChangeDialog(0, false);
+        displayPinChangeDialog(mButtonEnableFDN, 0, false);
+        displayPinChangeDialog(mButtonChangePin2, 0, false);
         mOldPin = mNewPin = "";
         mIsPuk2Locked = false;
     }
@@ -428,7 +444,8 @@
     private final void resetPinChangeStateForPUK2() {
         if (DBG) log("resetPinChangeStateForPUK2");
         mPinChangeState = PIN_CHANGE_PUK;
-        displayPinChangeDialog(0, false);
+        displayPinChangeDialog(mButtonEnableFDN, 0, false);
+        displayPinChangeDialog(mButtonChangePin2, 0, false);
         mOldPin = mNewPin = mPuk2 = "";
         mIsPuk2Locked = true;
     }
@@ -472,7 +489,10 @@
     * Reflect the updated change PIN2 state in the UI.
     */
     private void updateChangePIN2() {
-        if (mPhone.getIccCard().getIccPin2Blocked()) {
+        if (mPhone.getIccCard().getIccPuk2Blocked()) {
+            showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment.DIALOG_ID_PUK2_LOCKED_OUT);
+            resetPinChangeStateForPUK2();
+        } else if (mPhone.getIccCard().getIccPin2Blocked()) {
             // If the pin2 is blocked, the state of the change pin2 dialog
             // should be set for puk2 use (that is, the user should be prompted
             // to enter puk2 code instead of old pin2).
@@ -514,8 +534,14 @@
             mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY);
             mOldPin = icicle.getString(OLD_PIN_KEY);
             mNewPin = icicle.getString(NEW_PIN_KEY);
-            mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY));
-            mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY));
+            mPuk2 = icicle.getString(PUK_KEY);
+            mButtonChangePin2.setDialogMessage(
+                    icicle.getString(DIALOG_MESSAGE_KEY));
+            mButtonChangePin2.setText(
+                    icicle.getString(DIALOG_PIN_ENTRY_KEY));
+            mButtonEnableFDN.setDialogMessage(
+                    icicle.getString(FDN_DIALOG_MESSAGE_KEY));
+            mButtonEnableFDN.setText(icicle.getString(FDN_DIALOG_PIN_ENTRY_KEY));
         }
 
         ActionBar actionBar = getActionBar();
@@ -545,8 +571,19 @@
         out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState);
         out.putString(OLD_PIN_KEY, mOldPin);
         out.putString(NEW_PIN_KEY, mNewPin);
-        out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
-        out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
+        out.putString(PUK_KEY, mPuk2);
+        if (mButtonChangePin2.isEnabled()) {
+            out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
+            out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
+        }
+        if (mButtonEnableFDN.isEnabled()) {
+            CharSequence dialogMsg = mButtonEnableFDN.getDialogMessage();
+            if (dialogMsg != null) {
+                out.putString(FDN_DIALOG_MESSAGE_KEY,
+                        mButtonEnableFDN.getDialogMessage().toString());
+            }
+            out.putString(FDN_DIALOG_PIN_ENTRY_KEY, mButtonEnableFDN.getText());
+        }
     }
 
     @Override
@@ -562,5 +599,31 @@
     private void log(String msg) {
         Log.d(LOG_TAG, "FdnSetting: " + msg);
     }
+
+    @Override
+    public void onRequestPuk2(int id) {
+        resetPinChangeStateForPUK2();
+        final EditPinPreference button =
+                (id == Pin2LockedDialogFragment.DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED)
+                        ? mButtonChangePin2 : mButtonEnableFDN;
+        displayPinChangeDialog(button, 0, true);
+    }
+
+    private void showPin2OrPuk2LockedDialog(int id) {
+        final FragmentManager fragmentManager = getFragmentManager();
+        Pin2LockedDialogFragment dialogFragment = (Pin2LockedDialogFragment) fragmentManager
+                .findFragmentByTag(Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        if (dialogFragment == null) {
+            dialogFragment = new Pin2LockedDialogFragment();
+            Bundle args = new Bundle();
+            args.putInt(Pin2LockedDialogFragment.KEY_DIALOG_ID, id);
+            dialogFragment.setArguments(args);
+            dialogFragment.show(fragmentManager, Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        } else {
+            FragmentTransaction transaction = fragmentManager.beginTransaction();
+            transaction.show(dialogFragment);
+            transaction.commitNow();
+        }
+    }
 }
 
diff --git a/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java b/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java
new file mode 100644
index 0000000..ff16a7f
--- /dev/null
+++ b/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.phone.settings.fdn;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.phone.R;
+
+/**
+ * Dialog Fragment that displays dialogs indicating that PIN2/PUK2 has been locked out.
+ *
+ * 1. When user fails PIN2 authentication and PIN2 is locked, show the dialog indicating that PIN2
+ * is locked and PUK2 must be entered.
+ * 2. When user fails PUK2 authentication and PUK2 is locked, show the dialog indicating that PUK2
+ * is locked and user must contact service provider to unlock PUK2.
+ */
+public class Pin2LockedDialogFragment extends DialogFragment {
+
+    static final String TAG_PIN2_LOCKED_DIALOG = "tag_pin2_locked_dialog";
+    static final String KEY_DIALOG_ID = "key_dialog_id";
+
+    // AlertDialog IDs
+    static final int DIALOG_ID_PUK2_LOCKED_OUT = 10;
+    static final int DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY = 11;
+    static final int DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED = 12;
+
+    private Listener mListener;
+    private int mId;
+
+    interface Listener {
+        void onRequestPuk2(int id);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        super.onCreateDialog(savedInstanceState);
+        Activity activity = getActivity();
+        if (!(activity instanceof Listener)) {
+            return null;
+        }
+        mListener = (Listener) activity;
+        mId = getArguments().getInt(KEY_DIALOG_ID);
+
+        if (mId == DIALOG_ID_PUK2_LOCKED_OUT) {
+            AlertDialog alert = new AlertDialog.Builder(activity)
+                    .setMessage(R.string.puk2_locked)
+                    .setCancelable(true)
+                    .create();
+            alert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            alert.setButton(DialogInterface.BUTTON_NEUTRAL, getText(R.string.ok),
+                    (dialog, which) -> {
+                    });
+            return alert;
+        }
+
+        if (mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED
+                || mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY) {
+            AlertDialog alert = new AlertDialog.Builder(activity)
+                    .setMessage(R.string.puk2_requested)
+                    .setCancelable(true)
+                    .create();
+            alert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            alert.setButton(DialogInterface.BUTTON_NEUTRAL, getText(R.string.ok),
+                    (dialog, which) -> {
+                        mListener.onRequestPuk2(mId);
+                        dialog.dismiss();
+                    });
+            return alert;
+        }
+        return null;
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        if (mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED
+                || mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY) {
+            mListener.onRequestPuk2(mId);
+        }
+        dialog.dismiss();
+    }
+}
+
diff --git a/testapps/TestRcsApp/TestApp/Android.bp b/testapps/TestRcsApp/TestApp/Android.bp
index 4496a8e..cda7d17 100644
--- a/testapps/TestRcsApp/TestApp/Android.bp
+++ b/testapps/TestRcsApp/TestApp/Android.bp
@@ -21,6 +21,8 @@
         "libphonenumber-platform"
     ],
 
+    libs: ["org.apache.http.legacy"],
+
     certificate: "platform",
     privileged: true,
     product_specific: true,
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 460a1ce..7538df7 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -19,8 +19,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.sample.rcsclient"
-    android:versionCode="12"
-    android:versionName="1.0.11">
+    android:versionCode="13"
+    android:versionName="1.0.12">
 
     <uses-sdk
         android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java
index 3bc1c24..b9078f8 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java
@@ -156,6 +156,7 @@
                     Log.i(TAG, "FileTransferController null");
                     return;
                 }
+                mUploadResult.setText("");
                 Futures.addCallback(
                         fileTransferController.uploadFile(UUID.randomUUID().toString(),
                                 mFile),
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java
index d8e38e0..0d45828 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java
@@ -34,10 +34,11 @@
 import com.google.common.util.concurrent.MoreExecutors;
 
 import org.apache.http.Header;
-import org.apache.http.HeaderElement;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.params.AuthPolicy;
@@ -96,7 +97,7 @@
         ListenableFuture<HttpResponse> initialResponseFuture = sendEmptyPost(httpClient);
 
         BasicHttpContext httpContext = new BasicHttpContext();
-        ListenableFuture<Void> prepareAuthFuture =
+        ListenableFuture<AuthScheme> prepareAuthFuture =
                 Futures.transform(
                         initialResponseFuture,
                         initialResponse -> {
@@ -117,17 +118,16 @@
                             // Override nonce and realm in the HTTP context.
                             RFC2617Scheme authScheme = createAuthScheme(initialResponse);
                             httpContext.setAttribute(ATTRIBUTE_PREEMPTIVE_AUTH, authScheme);
-
-                            return null;
+                            return authScheme;
                         },
                         executor);
 
         // Executing the post with credentials.
         return Futures.transformAsync(
                 prepareAuthFuture,
-                unused ->
+                authScheme ->
                         executeAuthenticatedPost(
-                                httpClient, httpContext, transactionId, file),
+                                httpClient, httpContext, authScheme, transactionId, file),
                 executor);
     }
 
@@ -141,27 +141,12 @@
         String scheme = authHeader.getValue();
 
         if (scheme.contains(AuthPolicy.DIGEST)) {
-            HeaderElement[] elements = authHeader.getElements();
-
-            if (elements == null || elements.length == 0) {
-                throw new IllegalArgumentException(
-                        "Unable to find header elements. Cannot perform Digest authentication.");
-            }
-
             DigestScheme digestScheme = new DigestScheme();
-            for (HeaderElement element : elements) {
-                // TODO(b/180601658): Add checks for the realm, which should start with
-                //  3GPP-bootstrapping@.
-                if (element.getName().contains(PARAM_REALM)) {
-                    digestScheme.overrideParamter(PARAM_REALM, element.getValue());
-                    Log.i(TAG, "Realm: " + element.getValue());
-                }
-                if (element.getName().contains(PARAM_NONCE)) {
-                    digestScheme.overrideParamter(PARAM_NONCE, element.getValue());
-                    Log.i(TAG, "Nonce: " + element.getValue());
-                }
+            try {
+                digestScheme.processChallenge(authHeader);
+            } catch (MalformedChallengeException e) {
+                throw new IllegalArgumentException(e);
             }
-
             return digestScheme;
         } else {
             throw new IllegalArgumentException("Unable to create authentication scheme " + scheme);
@@ -195,13 +180,14 @@
     private ListenableFuture<String> executeAuthenticatedPost(
             DefaultHttpClient httpClient,
             HttpContext context,
+            AuthScheme authScheme,
             String transactionId,
             File file)
             throws IOException {
 
         Part[] parts = {
                 new StringPart(TRANSFER_ID_PART_NAME, transactionId),
-                new FilePart(file.getName(), file)
+                new FilePart(FILE_PART_NAME, file)
         };
         MultipartEntity entity = new MultipartEntity(parts);
 
@@ -211,7 +197,8 @@
         Log.i(TAG, "Created file upload POST:" + contentServerUri);
 
         ListenableFuture<HttpResponse> responseFuture =
-                requestExecutor.executeAuthenticatedRequest(httpClient, context, postRequest);
+                requestExecutor.executeAuthenticatedRequest(httpClient, context, postRequest,
+                        authScheme);
 
         Futures.addCallback(
                 responseFuture,
@@ -227,7 +214,7 @@
 
                     @Override
                     public void onFailure(Throwable t) {
-                        Log.i(TAG, "onFailure");
+                        Log.e(TAG, "onFailure", t);
                         throw new IllegalArgumentException(t);
                     }
                 },
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java
index 0bfa4cb..008fb76 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java
@@ -64,11 +64,12 @@
             Log.i(TAG, "organization:" + organization + ", protocol:" + protocol + ", cipherSuite:"
                     + cipherSuite + ", contentServerUrl:" + contentServerUrl);
 
-            builder.setOrg(UaSecurityProtocolIdentifier.ORG_3GPP)
-                    .setProtocol(
-                            UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+            builder.setOrg(organization)
+                    .setProtocol(protocol);
             if (cipherSuite == TlsParams.TLS_NULL_WITH_NULL_NULL) {
                 builder.setTlsCipherSuite(TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);
+            } else {
+                builder.setTlsCipherSuite(cipherSuite);
             }
         } catch (IllegalArgumentException e) {
             Log.e(TAG, e.getMessage());
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java
index 856fec1..83d5a8a 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java
@@ -24,9 +24,12 @@
 import com.google.common.util.concurrent.MoreExecutors;
 
 import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
 import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.protocol.ClientContext;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.protocol.HttpContext;
 
@@ -48,7 +51,8 @@
     @Override
     @SuppressWarnings("CheckReturnValue")
     public ListenableFuture<HttpResponse> executeAuthenticatedRequest(
-            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request) {
+            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request,
+            AuthScheme authScheme) {
 
         // Set authentication for the client.
         ListenableFuture<Credentials> credentialsFuture =
@@ -61,8 +65,13 @@
                             Log.i(TAG,
                                     "Obtained credentialsFuture, making the POST with credentials"
                                             + ".");
-                            httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY,
-                                    credentials);
+                            httpClient.addRequestInterceptor((req, ctx) -> {
+                                AuthState authState = (AuthState) context.getAttribute(
+                                        ClientContext.TARGET_AUTH_STATE);
+                                authState.setAuthScope(AuthScope.ANY);
+                                authState.setAuthScheme(authScheme);
+                                authState.setCredentials(credentials);
+                            }, /* index= */ 0);
 
                             // Make the first request.
                             return executor.submit(() -> httpClient.execute(request, context));
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java
index 59a3aa9..0026790 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java
@@ -19,6 +19,7 @@
 import com.google.common.util.concurrent.ListenableFuture;
 
 import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.protocol.HttpContext;
@@ -29,6 +30,7 @@
 public interface HttpRequestExecutor {
 
     ListenableFuture<HttpResponse> executeAuthenticatedRequest(
-            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request)
+            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request,
+            AuthScheme authScheme)
             throws IOException;
 }
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index c7d0c8f..57e01f0 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -85,28 +85,56 @@
  */
 public class RcsProvisioningMonitorTest {
     private static final String TAG = "RcsProvisioningMonitorTest";
-    private static final String CONFIG_DEFAULT = "<RCSConfig>\n"
-            + "\t<rcsVolteSingleRegistration>1</rcsVolteSingleRegistration>\n"
-            + "\t<SERVICES>\n"
-            + "\t\t<SupportedRCSProfileVersions>UP_2.0</SupportedRCSProfileVersions>\n"
-            + "\t\t<ChatAuth>1</ChatAuth>\n"
-            + "\t\t<GroupChatAuth>1</GroupChatAuth>\n"
-            + "\t\t<ftAuth>1</ftAuth>\n"
-            + "\t\t<standaloneMsgAuth>1</standaloneMsgAuth>\n"
-            + "\t\t<geolocPushAuth>1</geolocPushAuth>\n"
-            + "\t\t<Ext>\n"
-            + "\t\t\t<DataOff>\n"
-            + "\t\t\t\t<rcsMessagingDataOff>1</rcsMessagingDataOff>\n"
-            + "\t\t\t\t<fileTransferDataOff>1</fileTransferDataOff>\n"
-            + "\t\t\t\t<mmsDataOff>1</mmsDataOff>\n"
-            + "\t\t\t\t<syncDataOff>1</syncDataOff>\n"
-            + "\t\t\t</DataOff>\n"
-            + "\t\t</Ext>\n"
-            + "\t</SERVICES>\n"
-            + "</RCSConfig>";
-    private static final String CONFIG_SINGLE_REGISTRATION_DISABLED = "<RCSConfig>\n"
-            + "\t<rcsVolteSingleRegistration>0</rcsVolteSingleRegistration>\n"
-            + "</RCSConfig>";
+    private static final String CONFIG_DEFAULT = "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"1\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t\t<characteristic type=\"SERVICES\">\n"
+            + "\t\t\t<parm name=\"SupportedRCSProfileVersions\" value=\"UP2.3\"/>\n"
+            + "\t\t\t<parm name=\"ChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"GroupChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"ftAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"standaloneMsgAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"geolocPushAuth\" value=\"1\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"DataOff\">\n"
+            + "\t\t\t\t\t<parm name=\"rcsMessagingDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"fileTransferDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"mmsDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"syncDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<characteristic type=\"Ext\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
+
+    private static final String CONFIG_SINGLE_REGISTRATION_DISABLED = "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"0\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
     private static final int FAKE_SUB_ID_BASE = 0x0FFFFFF0;
     private static final String DEFAULT_MESSAGING_APP1 = "DMA1";
     private static final String DEFAULT_MESSAGING_APP2 = "DMA2";