Merge "Improve UI for PIN2/PUK2 authentication on FDN setting"
diff --git a/res/xml/carrier_ss_string.xml b/res/xml/carrier_ss_string.xml
index 29de13a..b7771cd 100644
--- a/res/xml/carrier_ss_string.xml
+++ b/res/xml/carrier_ss_string.xml
@@ -146,4 +146,32 @@
             <entry key="status_code" definition="ok">7</entry>
         </command_result>
     </feature>
+    <feature name="callwaiting">
+        <command name="query"><!--For example: *#102#-->
+            <service_code>102</service_code>
+            <action_code>*#</action_code>
+            <response_format number ="1"><!--For example: 120*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="activate"><!--For example: *102#-->
+            <service_code>102</service_code>
+            <action_code>*</action_code>
+            <response_format number="1"><!--For example: 102*5#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="deactivate"><!--For example: #102#-->
+            <service_code>102</service_code>
+            <action_code>#</action_code>
+            <response_format number="1"><!--For example: 148*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command_result number="3">
+            <entry key="status_code" definition="activate">5</entry>
+            <entry key="status_code" definition="deactivate">4</entry>
+            <entry key="status_code" definition="unregister">0</entry>
+        </command_result>
+    </feature>
 </resources>
diff --git a/res/xml/carrier_ss_string_850.xml b/res/xml/carrier_ss_string_850.xml
index 01eeee5..ed31fae 100644
--- a/res/xml/carrier_ss_string_850.xml
+++ b/res/xml/carrier_ss_string_850.xml
@@ -94,4 +94,32 @@
             <entry key="status_code" definition="ok">7</entry>
         </command_result>
     </feature>
+    <feature name="callwaiting">
+        <command name="query"><!--For example: *#102#-->
+            <service_code>102</service_code>
+            <action_code>*#</action_code>
+            <response_format number ="1"><!--For example: 120*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="activate"><!--For example: *102#-->
+            <service_code>102</service_code>
+            <action_code>*</action_code>
+            <response_format number="1"><!--For example: 102*5#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="deactivate"><!--For example: #102#-->
+            <service_code>102</service_code>
+            <action_code>#</action_code>
+            <response_format number="1"><!--For example: 148*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command_result number="3">
+            <entry key="status_code" definition="activate">5</entry>
+            <entry key="status_code" definition="deactivate">4</entry>
+            <entry key="status_code" definition="unregister">0</entry>
+        </command_result>
+    </feature>
 </resources>
diff --git a/src/com/android/phone/CallWaitingSwitchPreference.java b/src/com/android/phone/CallWaitingSwitchPreference.java
index 41442fe..01dd3b2 100644
--- a/src/com/android/phone/CallWaitingSwitchPreference.java
+++ b/src/com/android/phone/CallWaitingSwitchPreference.java
@@ -1,18 +1,21 @@
 package com.android.phone;
 
+import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
 
 import android.content.Context;
-import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.preference.SwitchPreference;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 public class CallWaitingSwitchPreference extends SwitchPreference {
     private static final String LOG_TAG = "CallWaitingSwitchPreference";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -20,6 +23,11 @@
     private final MyHandler mHandler = new MyHandler();
     private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
+    private Executor mExecutor;
+    private TelephonyManager mTelephonyManager;
+    private boolean mIsDuringUpdateProcess = false;
+    private int mUpdateStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+    private int mQueryStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
 
     public CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -37,101 +45,84 @@
             TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
         mPhone = phone;
         mTcpListener = listener;
+        mExecutor = Executors.newSingleThreadExecutor();
+        mTelephonyManager = getContext().getSystemService(
+                TelephonyManager.class).createForSubscriptionId(phone.getSubId());
 
         if (!skipReading) {
-            mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
-                    MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+            Log.d(LOG_TAG, "init getCallWaitingStatus");
+            mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
             if (mTcpListener != null) {
                 mTcpListener.onStarted(this, true);
             }
         }
     }
 
+    private void queryStatusCallBack(int result) {
+        Log.d(LOG_TAG, "queryStatusCallBack: CW state " + result);
+        mQueryStatus = result;
+        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MESSAGE_UPDATE_CALL_WAITING));
+    }
+
+    private void updateStatusCallBack(int result) {
+        Log.d(LOG_TAG, "updateStatusCallBack: CW state " + result + ", and re get");
+        mUpdateStatus = result;
+        mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
+    }
+
     @Override
     protected void onClick() {
         super.onClick();
-
-        mPhone.setCallWaiting(isChecked(),
-                mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+        mTelephonyManager.setCallWaitingEnabled(isChecked(), mExecutor, this::updateStatusCallBack);
         if (mTcpListener != null) {
+            mIsDuringUpdateProcess = true;
             mTcpListener.onStarted(this, false);
         }
     }
 
     private class MyHandler extends Handler {
-        static final int MESSAGE_GET_CALL_WAITING = 0;
-        static final int MESSAGE_SET_CALL_WAITING = 1;
+        static final int MESSAGE_UPDATE_CALL_WAITING = 0;
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MESSAGE_GET_CALL_WAITING:
-                    handleGetCallWaitingResponse(msg);
-                    break;
-                case MESSAGE_SET_CALL_WAITING:
-                    handleSetCallWaitingResponse(msg);
+                case MESSAGE_UPDATE_CALL_WAITING:
+                    updateUi();
                     break;
             }
         }
 
-        private void handleGetCallWaitingResponse(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
-
+        private void updateUi() {
             if (mTcpListener != null) {
-                if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+                if (mIsDuringUpdateProcess) {
                     mTcpListener.onFinished(CallWaitingSwitchPreference.this, false);
                 } else {
                     mTcpListener.onFinished(CallWaitingSwitchPreference.this, true);
                 }
             }
 
-            if (ar.exception instanceof CommandException) {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
-                            ar.exception);
-                }
+            if (mIsDuringUpdateProcess && (
+                    mUpdateStatus == TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED
+                            || mUpdateStatus
+                            == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR)) {
+                Log.d(LOG_TAG, "handleSetCallWaitingResponse: Exception");
                 if (mTcpListener != null) {
-                    mTcpListener.onException(CallWaitingSwitchPreference.this,
-                            (CommandException)ar.exception);
+                    mTcpListener.onError(CallWaitingSwitchPreference.this, EXCEPTION_ERROR);
                 }
-            } else if (ar.userObj instanceof Throwable || ar.exception != null) {
-                // Still an error case but just not a CommandException.
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
-                }
+            } else if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED
+                    || mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR) {
+                Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception");
                 if (mTcpListener != null) {
                     mTcpListener.onError(CallWaitingSwitchPreference.this, RESPONSE_ERROR);
                 }
             } else {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
-                }
-                int[] cwArray = (int[])ar.result;
-                // If cwArray[0] is = 1, then cwArray[1] must follow,
-                // with the TS 27.007 service class bit vector of services
-                // for which call waiting is enabled.
-                try {
-                    setChecked(((cwArray[0] == 1) && ((cwArray[1] & 0x01) == 0x01)));
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
-                            + e.getMessage());
+                if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_ENABLED) {
+                    setChecked(true);
+                } else {
+                    setChecked(false);
                 }
             }
-        }
-
-        private void handleSetCallWaitingResponse(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
-
-            if (ar.exception != null) {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleSetCallWaitingResponse: ar.exception=" + ar.exception);
-                }
-                //setEnabled(false);
-            }
-            if (DBG) Log.d(LOG_TAG, "handleSetCallWaitingResponse: re get");
-
-            mPhone.getCallWaiting(obtainMessage(MESSAGE_GET_CALL_WAITING,
-                    MESSAGE_SET_CALL_WAITING, MESSAGE_SET_CALL_WAITING, ar.exception));
+            mIsDuringUpdateProcess = false;
         }
     }
 }
diff --git a/src/com/android/phone/CallWaitingUssdResultReceiver.java b/src/com/android/phone/CallWaitingUssdResultReceiver.java
new file mode 100644
index 0000000..b9049e9
--- /dev/null
+++ b/src/com/android/phone/CallWaitingUssdResultReceiver.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 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;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.telephony.TelephonyManager;
+import android.telephony.UssdResponse;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.HashMap;
+
+/**
+ * Handling the call waiting USSD result.
+ */
+public class CallWaitingUssdResultReceiver extends ResultReceiver {
+    private static final String LOG_TAG = "CwUssdResultReceiver";
+
+    private IIntegerConsumer mCallback;
+    private CarrierXmlParser mCarrierXmlParser;
+    private CarrierXmlParser.SsEntry.SSAction mSsAction;
+
+    CallWaitingUssdResultReceiver(Handler handler, IIntegerConsumer callback,
+            CarrierXmlParser carrierXmlParser, CarrierXmlParser.SsEntry.SSAction action) {
+        super(handler);
+        mCallback = callback;
+        mCarrierXmlParser = carrierXmlParser;
+        mSsAction = action;
+    }
+
+    @Override
+    protected void onReceiveResult(int resultCode, Bundle ussdResponse) {
+        log("USSD:" + resultCode);
+        checkNotNull(ussdResponse, "ussdResponse cannot be null.");
+        UssdResponse response = ussdResponse.getParcelable(
+                TelephonyManager.USSD_RESPONSE);
+
+        if (resultCode == TelephonyManager.USSD_RETURN_SUCCESS) {
+            int callWaitingStatus = getStatusFromResponse(response);
+            try {
+                mCallback.accept(callWaitingStatus);
+            } catch (RemoteException e) {
+                log("Fail to notify getCallWaitingStatus due to " + e);
+            }
+        } else {
+            try {
+                mCallback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+            } catch (RemoteException e) {
+                log("Fail to notify getCallWaitingStatus due to " + e);
+            }
+        }
+    }
+
+    private int getStatusFromResponse(UssdResponse response) {
+        int callWaitingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+
+        CarrierXmlParser.SsFeature callWaitingFeature = mCarrierXmlParser.getFeature(
+                CarrierXmlParser.FEATURE_CALL_WAITING);
+        if (callWaitingFeature == null) {
+            return callWaitingStatus;
+        }
+
+        HashMap<String, String> analysisResult = callWaitingFeature
+                .getResponseSet(mSsAction, response.getReturnMessage().toString());
+        if (analysisResult.get(CarrierXmlParser.TAG_RESPONSE_STATUS_ERROR) != null) {
+            return callWaitingStatus;
+        }
+
+        if (analysisResult != null && analysisResult.size() != 0) {
+            String tmpStatusStr = analysisResult.get(
+                    CarrierXmlParser.TAG_RESPONSE_STATUS);
+
+            if (!TextUtils.isEmpty(tmpStatusStr)) {
+                if (tmpStatusStr.equals(
+                        CarrierXmlParser.TAG_COMMAND_RESULT_DEFINITION_ACTIVATE)) {
+                    callWaitingStatus =
+                            TelephonyManager.CALL_WAITING_STATUS_ENABLED;
+                } else if (tmpStatusStr.equals(
+                        CarrierXmlParser.TAG_COMMAND_RESULT_DEFINITION_DEACTIVATE)) {
+                    callWaitingStatus =
+                            TelephonyManager.CALL_WAITING_STATUS_DISABLED;
+                }
+            }
+        }
+        return callWaitingStatus;
+    }
+
+    private static void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+}
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 4ff7316..d5b697a 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -55,6 +56,7 @@
 import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.Phone;
@@ -166,6 +168,7 @@
     private static final int EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT = 21;
     // Fetching config timed out from the default app for no SIM config.
     private static final int EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT = 22;
+    // NOTE: any new EVENT_* values must be added to method eventToString().
 
     private static final int BIND_TIMEOUT_MILLIS = 30000;
 
@@ -201,10 +204,14 @@
     // 3. clearing config (e.g. due to sim removal)
     // 4. encountering bind or IPC error
     private class ConfigHandler extends Handler {
+        ConfigHandler(@NonNull Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             final int phoneId = msg.arg1;
-            logdWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+            logdWithLocalLog("mHandler: " + eventToString(msg.what) + " phoneId: " + phoneId);
             if (!SubscriptionManager.isValidPhoneId(phoneId)
                     && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
                 return;
@@ -314,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");
@@ -444,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: "
@@ -666,11 +673,13 @@
      * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
      * receiver for relevant events.
      */
-    private CarrierConfigLoader(Context context) {
+    @VisibleForTesting
+    /* package */ CarrierConfigLoader(Context context,
+            SubscriptionInfoUpdater subscriptionInfoUpdater, @NonNull Looper looper) {
         mContext = context;
         mPlatformCarrierConfigPackage =
                 mContext.getString(R.string.platform_carrier_config_package);
-        mHandler = new ConfigHandler();
+        mHandler = new ConfigHandler(looper);
 
         IntentFilter bootFilter = new IntentFilter();
         bootFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
@@ -701,7 +710,7 @@
         TelephonyFrameworkInitializer
                 .getTelephonyServiceManager().getCarrierConfigServiceRegisterer().register(this);
         logd("CarrierConfigLoader has started");
-        mSubscriptionInfoUpdater = PhoneFactory.getSubscriptionInfoUpdater();
+        mSubscriptionInfoUpdater = subscriptionInfoUpdater;
         mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
     }
 
@@ -710,11 +719,11 @@
      *
      * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
      */
-    /* package */
-    static CarrierConfigLoader init(Context context) {
+    /* package */ static CarrierConfigLoader init(Context context) {
         synchronized (CarrierConfigLoader.class) {
             if (sInstance == null) {
-                sInstance = new CarrierConfigLoader(context);
+                sInstance = new CarrierConfigLoader(context,
+                        PhoneFactory.getSubscriptionInfoUpdater(), Looper.myLooper());
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -722,7 +731,8 @@
         }
     }
 
-    private void clearConfigForPhone(int phoneId, boolean fetchNoSimConfig) {
+    @VisibleForTesting
+    /* package */ void clearConfigForPhone(int phoneId, boolean fetchNoSimConfig) {
         /* Ignore clear configuration request if device is being shutdown. */
         Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone != null) {
@@ -844,7 +854,8 @@
         }
     }
 
-    private CarrierIdentifier getCarrierIdentifierForPhoneId(int phoneId) {
+    @VisibleForTesting
+    /* package */ CarrierIdentifier getCarrierIdentifierForPhoneId(int phoneId) {
         String mcc = "";
         String mnc = "";
         String imsi = "";
@@ -1000,12 +1011,14 @@
         }
     }
 
-    private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
+    @VisibleForTesting
+    /* package */ void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
             CarrierIdentifier carrierId, PersistableBundle config) {
         saveConfigToXml(packageName, extraString, phoneId, carrierId, config, false);
     }
 
-    private void saveNoSimConfigToXml(String packageName, PersistableBundle config) {
+    @VisibleForTesting
+    /* package */ void saveNoSimConfigToXml(String packageName, PersistableBundle config) {
         saveConfigToXml(packageName, "", -1, null, config, true);
     }
 
@@ -1166,20 +1179,20 @@
 
     @Override
     @NonNull
-    public PersistableBundle getConfigForSubId(int subId, String callingPackage) {
-        return getConfigForSubIdWithFeature(subId, callingPackage, null);
+    public PersistableBundle getConfigForSubId(int subscriptionId, String callingPackage) {
+        return getConfigForSubIdWithFeature(subscriptionId, callingPackage, null);
     }
 
     @Override
     @NonNull
-    public PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+    public PersistableBundle getConfigForSubIdWithFeature(int subscriptionId, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
-                callingFeatureId, "getCarrierConfig")) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subscriptionId,
+                callingPackage, callingFeatureId, "getCarrierConfig")) {
             return new PersistableBundle();
         }
 
-        int phoneId = SubscriptionManager.getPhoneId(subId);
+        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             PersistableBundle config = mConfigFromDefaultApp[phoneId];
@@ -1223,7 +1236,8 @@
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
-            return;
+            throw new IllegalArgumentException(
+                    "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
         }
         // Post to run on handler thread on which all states should be confined.
         mHandler.post(() -> {
@@ -1262,17 +1276,18 @@
     }
 
     @Override
-    public void notifyConfigChangedForSubId(int subId) {
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
-            return;
-        }
-
+    public void notifyConfigChangedForSubId(int subscriptionId) {
         // Requires the calling app to be either a carrier privileged app for this subId or
         // system privileged app with MODIFY_PHONE_STATE permission.
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext, subId,
-                "Require carrier privileges or MODIFY_PHONE_STATE permission.");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext,
+                subscriptionId, "Require carrier privileges or MODIFY_PHONE_STATE permission.");
+
+        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
+            throw new IllegalArgumentException(
+                    "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
+        }
 
         // This method should block until deleting has completed, so that an error which prevents us
         // from clearing the cache is passed back to the carrier app. With the files successfully
@@ -1289,7 +1304,7 @@
                 android.Manifest.permission.MODIFY_PHONE_STATE, null);
         logdWithLocalLog("Update config for phoneId: " + phoneId + " simState: " + simState);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            return;
+            throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
         }
         // requires Java 7 for switch on string.
         switch (simState) {
@@ -1315,6 +1330,31 @@
         return mPlatformCarrierConfigPackage;
     }
 
+    @VisibleForTesting
+    /* package */ Handler getHandler() {
+        return mHandler;
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getConfigFromDefaultApp(int phoneId) {
+        return mConfigFromDefaultApp[phoneId];
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getConfigFromCarrierApp(int phoneId) {
+        return mConfigFromCarrierApp[phoneId];
+    }
+
+    @VisibleForTesting
+     /* package */ PersistableBundle getNoSimConfig() {
+        return mNoSimConfig;
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getOverrideConfig(int phoneId) {
+        return mOverrideConfigs[phoneId];
+    }
+
     private void unbindIfBound(Context context, CarrierServiceConnection conn,
             int phoneId) {
         if (mServiceBound[phoneId]) {
@@ -1614,6 +1654,56 @@
         }
     }
 
+    // Get readable string for the message code supported in this class.
+    private static String eventToString(int code) {
+        switch (code) {
+            case EVENT_CLEAR_CONFIG:
+                return "EVENT_CLEAR_CONFIG";
+            case EVENT_CONNECTED_TO_DEFAULT:
+                return "EVENT_CONNECTED_TO_DEFAULT";
+            case EVENT_CONNECTED_TO_CARRIER:
+                return "EVENT_CONNECTED_TO_CARRIER";
+            case EVENT_FETCH_DEFAULT_DONE:
+                return "EVENT_FETCH_DEFAULT_DONE";
+            case EVENT_FETCH_CARRIER_DONE:
+                return "EVENT_FETCH_CARRIER_DONE";
+            case EVENT_DO_FETCH_DEFAULT:
+                return "EVENT_DO_FETCH_DEFAULT";
+            case EVENT_DO_FETCH_CARRIER:
+                return "EVENT_DO_FETCH_CARRIER";
+            case EVENT_PACKAGE_CHANGED:
+                return "EVENT_PACKAGE_CHANGED";
+            case EVENT_BIND_DEFAULT_TIMEOUT:
+                return "EVENT_BIND_DEFAULT_TIMEOUT";
+            case EVENT_BIND_CARRIER_TIMEOUT:
+                return "EVENT_BIND_CARRIER_TIMEOUT";
+            case EVENT_CHECK_SYSTEM_UPDATE:
+                return "EVENT_CHECK_SYSTEM_UPDATE";
+            case EVENT_SYSTEM_UNLOCKED:
+                return "EVENT_SYSTEM_UNLOCKED";
+            case EVENT_FETCH_DEFAULT_TIMEOUT:
+                return "EVENT_FETCH_DEFAULT_TIMEOUT";
+            case EVENT_FETCH_CARRIER_TIMEOUT:
+                return "EVENT_FETCH_CARRIER_TIMEOUT";
+            case EVENT_SUBSCRIPTION_INFO_UPDATED:
+                return "EVENT_SUBSCRIPTION_INFO_UPDATED";
+            case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                return "EVENT_MULTI_SIM_CONFIG_CHANGED";
+            case EVENT_DO_FETCH_DEFAULT_FOR_NO_SIM_CONFIG:
+                return "EVENT_DO_FETCH_DEFAULT_FOR_NO_SIM_CONFIG";
+            case EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_DONE:
+                return "EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_DONE";
+            case EVENT_CONNECTED_TO_DEFAULT_FOR_NO_SIM_CONFIG:
+                return "EVENT_CONNECTED_TO_DEFAULT_FOR_NO_SIM_CONFIG";
+            case EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT:
+                return "EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT";
+            case EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT:
+                return "EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT";
+            default:
+                return "UNKNOWN(" + code + ")";
+        }
+    }
+
     private void logd(String msg) {
         Log.d(LOG_TAG, msg);
     }
diff --git a/src/com/android/phone/CarrierXmlParser.java b/src/com/android/phone/CarrierXmlParser.java
index 18602c9..6e01b43 100644
--- a/src/com/android/phone/CarrierXmlParser.java
+++ b/src/com/android/phone/CarrierXmlParser.java
@@ -67,6 +67,7 @@
 
     // To define feature's item name in xml
     public static final String FEATURE_CALL_FORWARDING = "callforwarding";
+    public static final String FEATURE_CALL_WAITING = "callwaiting";
     public static final String FEATURE_CALLER_ID = "callerid";
 
     // COMMAND_NAME is xml's command name.
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index ad33302..bd6ba6b 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -104,7 +104,7 @@
      */
     @Override
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
@@ -122,7 +122,7 @@
      */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
@@ -139,7 +139,7 @@
      */
     @Override
     public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationState");
         final long token = Binder.clearCallingIdentity();
         try {
@@ -161,7 +161,7 @@
      */
     @Override
     public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationTransportType");
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9a1e275..98b540c 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -209,6 +209,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
@@ -1152,7 +1153,6 @@
                     }
                     break;
                 }
-
                 case EVENT_PERFORM_NETWORK_SCAN_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -3678,7 +3678,7 @@
     @Override
     public int getNetworkSelectionMode(int subId) {
         TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "getNetworkSelectionMode");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -3715,7 +3715,7 @@
     @Override
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
             throws RemoteException {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
 
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
@@ -3741,7 +3741,7 @@
      */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterImsRegistrationCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -3798,7 +3798,7 @@
      */
     @Override
     public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsMmTelRegistrationTransportType");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -3838,7 +3838,7 @@
     @Override
     public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
             throws RemoteException {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerMmTelCapabilityCallback");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -3863,7 +3863,7 @@
      */
     @Override
     public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterMmTelCapabilityCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -3963,7 +3963,7 @@
      */
     @Override
     public boolean isAdvancedCallingSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
         // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -4000,7 +4000,7 @@
      */
     @Override
     public boolean isVtSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVtSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4034,7 +4034,7 @@
      */
     @Override
     public boolean isVoWiFiSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4069,7 +4069,7 @@
      */
     @Override
     public boolean isVoWiFiRoamingSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiRoamingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4121,7 +4121,7 @@
      */
     @Override
     public int getVoWiFiModeSetting(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getVoWiFiModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4203,7 +4203,7 @@
      */
     @Override
     public boolean isTtyOverVolteEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isTtyOverVolteEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -5595,7 +5595,7 @@
             @ImsFeature.FeatureType int featureType) {
         int[] subIds = SubscriptionManager.getSubId(slotId);
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
                 "getBoundImsServicePackage");
 
@@ -5732,7 +5732,7 @@
     @Override
     public String getManualNetworkSelectionPlmn(int subId) {
         TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "getManualNetworkSelectionPlmn");
 
         final long identity = Binder.clearCallingIdentity();
@@ -5879,10 +5879,9 @@
      */
     @Override
     public void getCallWaitingStatus(int subId, IIntegerConsumer callback) {
-        enforceReadPrivilegedPermission("getCallForwarding");
+        enforceReadPrivilegedPermission("getCallWaitingStatus");
         long identity = Binder.clearCallingIdentity();
         try {
-
             Phone phone = getPhone(subId);
             if (phone == null) {
                 try {
@@ -5892,11 +5891,35 @@
                 }
                 return;
             }
-
-            Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
+            CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext());
+            PersistableBundle c = configManager.getConfigForSubId(subId);
+            boolean requireUssd = c.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false);
 
             if (DBG) log("getCallWaitingStatus: subId " + subId);
-            sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null);
+            if (requireUssd) {
+                CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(),
+                        getSubscriptionCarrierId(subId));
+                String newUssdCommand = "";
+                try {
+                    newUssdCommand = carrierXmlParser.getFeature(
+                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                            .makeCommand(CarrierXmlParser.SsEntry.SSAction.QUERY, null);
+                } catch (NullPointerException e) {
+                    loge("Failed to generate USSD number" + e);
+                }
+                ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver(
+                        mMainThreadHandler, callback, carrierXmlParser,
+                        CarrierXmlParser.SsEntry.SSAction.QUERY);
+                final String ussdCommand = newUssdCommand;
+                Executors.newSingleThreadExecutor().execute(() -> {
+                    handleUssdRequest(subId, ussdCommand, wrappedCallback);
+                });
+            } else {
+                Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(
+                        callback::accept);
+                sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5922,10 +5945,38 @@
                 return;
             }
 
-            Pair<Boolean, Consumer<Integer>> arguments = Pair.create(enable,
-                    FunctionalUtils.ignoreRemoteException(callback::accept));
+            CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext());
+            PersistableBundle c = configManager.getConfigForSubId(subId);
+            boolean requireUssd = c.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false);
 
-            sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null);
+            if (DBG) log("getCallWaitingStatus: subId " + subId);
+            if (requireUssd) {
+                CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(),
+                        getSubscriptionCarrierId(subId));
+                CarrierXmlParser.SsEntry.SSAction ssAction =
+                        enable ? CarrierXmlParser.SsEntry.SSAction.UPDATE_ACTIVATE
+                                : CarrierXmlParser.SsEntry.SSAction.UPDATE_DEACTIVATE;
+                String newUssdCommand = "";
+                try {
+                    newUssdCommand = carrierXmlParser.getFeature(
+                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                            .makeCommand(ssAction, null);
+                } catch (NullPointerException e) {
+                    loge("Failed to generate USSD number" + e);
+                }
+                ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver(
+                        mMainThreadHandler, callback, carrierXmlParser, ssAction);
+                final String ussdCommand = newUssdCommand;
+                Executors.newSingleThreadExecutor().execute(() -> {
+                    handleUssdRequest(subId, ussdCommand, wrappedCallback);
+                });
+            } else {
+                Pair<Boolean, Consumer<Integer>> arguments = Pair.create(enable,
+                        FunctionalUtils.ignoreRemoteException(callback::accept));
+
+                sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6032,7 +6083,7 @@
     @Override
     public int getAllowedNetworkTypesBitmask(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getAllowedNetworkTypesBitmask");
 
         final long identity = Binder.clearCallingIdentity();
@@ -6057,7 +6108,7 @@
     @Override
     public long getAllowedNetworkTypesForReason(int subId,
             @TelephonyManager.AllowedNetworkTypesReason int reason) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getAllowedNetworkTypesForReason");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6110,7 +6161,7 @@
     @Override
     public boolean isNrDualConnectivityEnabled(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isNRDualConnectivityEnabled");
         if (!isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
@@ -6918,7 +6969,7 @@
         Phone phone = PhoneFactory.getPhone(phoneId);
         try {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, phone.getSubId(), "getRadioAccessFamily");
         } catch (SecurityException e) {
             EventLog.writeEvent(0x534e4554, "150857259", -1, "Missing Permission");
@@ -6931,7 +6982,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, phone.getSubId(), "getRadioAccessFamily");
             raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
         } finally {
@@ -8090,7 +8141,7 @@
             mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
                     null);
         } catch (Exception e) {
-            TelephonyPermissions.enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+            TelephonyPermissions.enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "isDataRoamingEnabled");
         }
 
@@ -8135,7 +8186,7 @@
     @Override
     public boolean isManualNetworkSelectionAllowed(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isManualNetworkSelectionAllowed");
 
         boolean isAllowed = true;
@@ -8421,7 +8472,7 @@
     @Override
     public int getCdmaRoamingMode(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaRoamingMode");
 
         final long identity = Binder.clearCallingIdentity();
@@ -8448,7 +8499,7 @@
     @Override
     public int getCdmaSubscriptionMode(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaSubscriptionMode");
 
         final long identity = Binder.clearCallingIdentity();
@@ -8502,7 +8553,7 @@
         final Phone defaultPhone = getDefaultPhone();
         if (!exactMatch) {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, defaultPhone.getSubId(), "isEmergencyNumber(Potential)");
         }
         final long identity = Binder.clearCallingIdentity();
@@ -8968,7 +9019,7 @@
     @Override
     public List<RadioAccessSpecifier> getSystemSelectionChannels(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getSystemSelectionChannels");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index 6fdde78..18c8c0b 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -539,8 +539,15 @@
      */
     public boolean isRcsVolteSingleRegistrationEnabled(int subId) {
         if (mRcsProvisioningInfos.containsKey(subId)) {
-            return mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
-                    == ProvisioningManager.STATUS_CAPABLE;
+            if (mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
+                    == ProvisioningManager.STATUS_CAPABLE) {
+                try {
+                    RcsConfig rcsConfig = new RcsConfig(getConfig(subId));
+                    return rcsConfig.isRcsVolteSingleRegistrationSupported();
+                } catch (IllegalArgumentException e) {
+                    logd("fail to get rcs config for sub:" + subId);
+                }
+            }
         }
         return false;
     }
@@ -838,10 +845,9 @@
     }
 
     void unregisterRcsFeatureListener(RcsProvisioningInfo info) {
-        int slotId = SubscriptionManager.getSlotIndex(info.getSubId());
-        RcsFeatureListener cb = mRcsFeatureListeners.get(slotId);
-        if (cb != null) {
-            cb.removeRcsProvisioningInfo(info);
+        // make sure the info to be removed in any case, even the slotId changed or invalid.
+        for (int i  = 0; i < mRcsFeatureListeners.size(); i++) {
+            mRcsFeatureListeners.valueAt(i).removeRcsProvisioningInfo(info);
         }
     }
 
diff --git a/src/com/android/phone/SimPhonebookProvider.java b/src/com/android/phone/SimPhonebookProvider.java
index 6a27130..4a15950 100644
--- a/src/com/android/phone/SimPhonebookProvider.java
+++ b/src/com/android/phone/SimPhonebookProvider.java
@@ -818,15 +818,15 @@
             int efid;
             if (efName != null) {
                 switch (efName) {
-                    case ElementaryFiles.EF_ADN_PATH_SEGMENT:
+                    case ElementaryFiles.PATH_SEGMENT_EF_ADN:
                         efType = ElementaryFiles.EF_ADN;
                         efid = IccConstants.EF_ADN;
                         break;
-                    case ElementaryFiles.EF_FDN_PATH_SEGMENT:
+                    case ElementaryFiles.PATH_SEGMENT_EF_FDN:
                         efType = ElementaryFiles.EF_FDN;
                         efid = IccConstants.EF_FDN;
                         break;
-                    case ElementaryFiles.EF_SDN_PATH_SEGMENT:
+                    case ElementaryFiles.PATH_SEGMENT_EF_SDN:
                         efType = ElementaryFiles.EF_SDN;
                         efid = IccConstants.EF_SDN;
                         break;
diff --git a/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java b/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
index c42472d..daafeb2 100644
--- a/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
+++ b/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
@@ -190,7 +190,7 @@
          * dialog be released as the SIP dialog is now closed.
          */
         @Override
-        public void closeDialog(String callId) {
+        public void cleanupSession(String callId) {
             long token = Binder.clearCallingIdentity();
             try {
                 mExecutor.execute(() -> {
@@ -202,7 +202,7 @@
                     try {
                         // TODO track the SIP Dialogs created/destroyed on the associated
                         // SipDelegate.
-                        mSipDelegate.closeDialog(callId);
+                        mSipDelegate.cleanupSession(callId);
                     } catch (RemoteException e) {
                         logw("SipDelegate not available when closeDialog was called "
                                 + "for call id: " + callId);
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 52dd427..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="11"
-    android:versionName="1.0.10">
+    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 55608e0..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
@@ -25,6 +25,7 @@
 import android.util.Log;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.io.BaseEncoding;
 import com.google.common.util.concurrent.SettableFuture;
 
 import org.apache.http.auth.Credentials;
@@ -61,13 +62,14 @@
             int cipherSuite = carrierConfig.getInt(
                     CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT);
             Log.i(TAG, "organization:" + organization + ", protocol:" + protocol + ", cipherSuite:"
-                    + 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());
@@ -79,7 +81,8 @@
                 new TelephonyManager.BootstrapAuthenticationCallback() {
                     @Override
                     public void onKeysAvailable(byte[] gbaKey, String btId) {
-                        Log.i(TAG, "onKeysAvailable: key:[" + new String(gbaKey) + "] btid:[" + btId
+                        Log.i(TAG, "onKeysAvailable: String key:[" + new String(gbaKey) + "] btid:["
+                                + btId + "]" + "Base64 key:[" + BaseEncoding.base64().encode(gbaKey)
                                 + "]");
                         credentialsFuture.set(GbaCredentials.create(btId, gbaKey));
                     }
@@ -108,7 +111,7 @@
 
         public static GbaCredentials create(String btId, byte[] gbaKey) {
             return new AutoValue_GbaAuthenticationProvider_GbaCredentials(
-                    GbaPrincipal.create(btId), new String(gbaKey));
+                    GbaPrincipal.create(btId), BaseEncoding.base64().encode(gbaKey));
         }
 
         @Override
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/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java
index 75eb48d..e3a091d 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java
@@ -88,7 +88,7 @@
             return (Message)
                     method.invoke(
                             new StringMsgParser(),
-                            message.getEncodedMessage(),
+                            message.toEncodedMessage(),
                             true,
                             false,
                             (ParseExceptionListener)
@@ -98,7 +98,7 @@
         } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
             try {
                 method = StringMsgParser.class.getDeclaredMethod("parseSIPMessage", byte[].class);
-                return (Message) method.invoke(new StringMsgParser(), message.getEncodedMessage());
+                return (Message) method.invoke(new StringMsgParser(), message.toEncodedMessage());
             } catch (IllegalAccessException | InvocationTargetException
                     | NoSuchMethodException ex) {
                 ex.printStackTrace();
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 9d712d3..fc5ee4c 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.PersistableBundle;
 import android.telecom.TelecomManager;
@@ -32,16 +34,22 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsManager;
 import android.test.mock.MockContext;
+import android.util.Log;
 import android.util.SparseArray;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
 
+import java.util.HashSet;
 import java.util.concurrent.Executor;
 
 public class TestContext extends MockContext {
 
+    private static final String TAG = "TestContext";
+    // Stub used to grant all permissions
+    public static final String STUB_PERMISSION_ENABLE_ALL = "stub_permission_enable_all";
+
     @Mock CarrierConfigManager mMockCarrierConfigManager;
     @Mock TelecomManager mMockTelecomManager;
     @Mock TelephonyManager mMockTelephonyManager;
@@ -50,6 +58,8 @@
 
     private SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
 
+    private final HashSet<String> mPermissionTable = new HashSet<>();
+
     public TestContext() {
         MockitoAnnotations.initMocks(this);
         doAnswer((Answer<PersistableBundle>) invocation -> {
@@ -161,4 +171,67 @@
         }
         return b;
     }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(permission + " denied: " + message);
+        }
+    }
+
+    @Override
+    public void enforcePermission(String permission, int pid, int uid, String message) {
+        enforceCallingOrSelfPermission(permission, message);
+    }
+
+    @Override
+    public void enforceCallingPermission(String permission, String message) {
+        enforceCallingOrSelfPermission(permission, message);
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        return checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid());
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable.contains(permission)
+                    || mPermissionTable.contains(STUB_PERMISSION_ENABLE_ALL)) {
+                logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+                return PackageManager.PERMISSION_GRANTED;
+            } else {
+                logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+                return PackageManager.PERMISSION_DENIED;
+            }
+        }
+    }
+
+    public void grantPermission(String permission) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable != null && permission != null) {
+                mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
+                mPermissionTable.add(permission);
+            }
+        }
+    }
+
+    public void revokePermission(String permission) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable != null && permission != null) {
+                mPermissionTable.remove(permission);
+            }
+        }
+    }
+
+    public void revokeAllPermissions() {
+        synchronized (mPermissionTable) {
+            mPermissionTable.clear();
+        }
+    }
+
+    private static void logd(String s) {
+        Log.d(TAG, s);
+    }
 }
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
new file mode 100644
index 0000000..f58e6cc
--- /dev/null
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2021 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;
+
+import static com.android.TestContext.STUB_PERMISSION_ENABLE_ALL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.SubscriptionInfoUpdater;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+/**
+ * Unit Test for CarrierConfigLoader.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CarrierConfigLoaderTest extends TelephonyTestBase {
+
+    private static final int DEFAULT_PHONE_ID = 0;
+    private static final int DEFAULT_SUB_ID = SubscriptionManager.getDefaultSubscriptionId();
+    private static final String PLATFORM_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
+    private static final long PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE = 1;
+    private static final String CARRIER_CONFIG_EXAMPLE_KEY =
+            CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT;
+    private static final int CARRIER_CONFIG_EXAMPLE_VALUE =
+            CarrierConfigManager.USSD_OVER_CS_PREFERRED;
+
+    @Mock Resources mResources;
+    @Mock PackageManager mPackageManager;
+    @Mock PackageInfo mPackageInfo;
+    @Mock SubscriptionInfoUpdater mSubscriptionInfoUpdater;
+    @Mock SharedPreferences mSharedPreferences;
+
+    private TelephonyManager mTelephonyManager;
+    private CarrierConfigLoader mCarrierConfigLoader;
+    private Handler mHandler;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
+        doReturn(Build.FINGERPRINT).when(mSharedPreferences).getString(eq("build_fingerprint"),
+                any());
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
+                mContext).getFilesDir();
+        doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE).when(mResources).getString(
+                eq(R.string.platform_carrier_config_package));
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        doReturn(1).when(mTelephonyManager).getSupportedModemCount();
+        doReturn(1).when(mTelephonyManager).getActiveModemCount();
+        doReturn("spn").when(mTelephonyManager).getSimOperatorNameForPhone(anyInt());
+        doReturn("310260").when(mTelephonyManager).getSimOperatorNumericForPhone(anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(
+                eq(PLATFORM_CARRIER_CONFIG_PACKAGE), eq(0) /*flags*/);
+        doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE).when(
+                mPackageInfo).getLongVersionCode();
+
+        mHandlerThread = new HandlerThread("CarrierConfigLoaderTest");
+        mHandlerThread.start();
+
+        mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
+        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mSubscriptionInfoUpdater,
+                mTestableLooper.getLooper());
+        mHandler = mCarrierConfigLoader.getHandler();
+
+        // Clear all configs to have the same starting point.
+        mCarrierConfigLoader.clearConfigForPhone(DEFAULT_PHONE_ID, false);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mContext.revokeAllPermissions();
+        mTestableLooper.destroy();
+        super.tearDown();
+    }
+
+    /**
+     * Verifies that SecurityException should throw when call #updateConfigForPhoneId() without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_noPermission() throws Exception {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                        IccCardConstants.INTENT_VALUE_ICC_ABSENT));
+    }
+
+    /**
+     * Verifies that IllegalArgumentException should throw when call #updateConfigForPhoneId() with
+     * invalid phoneId.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_invalidPhoneId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarrierConfigLoader.updateConfigForPhoneId(
+                        SubscriptionManager.INVALID_PHONE_INDEX,
+                        IccCardConstants.INTENT_VALUE_ICC_ABSENT));
+    }
+
+    /**
+     * Verifies that when call #updateConfigForPhoneId() with SIM absence, both carrier config from
+     * default app and carrier should be cleared but no-sim config should be loaded.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_simAbsent() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+        doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+
+        // Prepare a cached config to fetch from xml
+        PersistableBundle config = getTestConfig();
+        mCarrierConfigLoader.saveNoSimConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, config);
+        mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID)).isNull();
+        assertThat(mCarrierConfigLoader.getConfigFromCarrierApp(DEFAULT_PHONE_ID)).isNull();
+        assertThat(mCarrierConfigLoader.getNoSimConfig().getInt(CARRIER_CONFIG_EXAMPLE_KEY))
+                .isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+        verify(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+    }
+
+    /**
+     * Verifies that with cached config in XML, calling #updateConfigForPhoneId() with SIM loaded
+     * will return the right config in the XML.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_simLoaded_withCachedConfigInXml() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        // Prepare to make sure we can save the config into the XML file which used as cache
+        List<String> carrierPackages = List.of(PLATFORM_CARRIER_CONFIG_PACKAGE);
+        doReturn(carrierPackages).when(mTelephonyManager).getCarrierPackageNamesForIntentAndPhone(
+                nullable(Intent.class), anyInt());
+
+        // Save the sample config into the XML file
+        PersistableBundle config = getTestConfig();
+        CarrierIdentifier carrierId = mCarrierConfigLoader.getCarrierIdentifierForPhoneId(
+                DEFAULT_PHONE_ID);
+        mCarrierConfigLoader.saveConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, "",
+                DEFAULT_PHONE_ID, carrierId, config);
+        mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID).getInt(
+                CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+
+    }
+
+    /**
+     * Verifies that SecurityException should throw if call #overrideConfig() without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Test
+    public void testOverrideConfig_noPermission() throws Exception {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, PersistableBundle.EMPTY,
+                        false));
+    }
+
+    /**
+     * Verifies IllegalArgumentException should throw if call #overrideConfig() with invalid subId.
+     */
+    @Test
+    public void testOverrideConfig_invalidSubId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class, () -> mCarrierConfigLoader.overrideConfig(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, new PersistableBundle(), false));
+    }
+
+    /**
+     * Verifies that override config is not null when calling #overrideConfig with null bundle.
+     */
+    @Test
+    public void testOverrideConfig_withNullBundle() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, null /*overrides*/,
+                false/*persistent*/);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).isEmpty()).isTrue();
+        verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+                eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
+                any(PersistableBundle.class), any(Message.class));
+    }
+
+    /**
+     * Verifies that override config is not null when calling #overrideConfig with non-null bundle.
+     */
+    @Test
+    public void testOverrideConfig_withNonNullBundle() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        PersistableBundle config = getTestConfig();
+        mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, config /*overrides*/,
+                false/*persistent*/);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).getInt(
+                CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+        verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+                eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
+                any(PersistableBundle.class), any(Message.class));
+    }
+
+    /**
+     * Verifies that IllegalArgumentException should throw when calling
+     * #notifyConfigChangedForSubId() with invalid subId.
+     */
+    @Test
+    public void testNotifyConfigChangedForSubId_invalidSubId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarrierConfigLoader.notifyConfigChangedForSubId(
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+    }
+
+    // TODO(b/184040111): Enable test case when support disabling carrier privilege
+    // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
+    // when running the test here.
+    /**
+     * Verifies that SecurityException should throw when calling notifyConfigChangedForSubId without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Ignore
+    public void testNotifyConfigChangedForSubId_noPermission() throws Exception {
+        setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
+
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.notifyConfigChangedForSubId(DEFAULT_SUB_ID));
+    }
+
+    /**
+     * Verifies that SecurityException should throw when calling getDefaultCarrierServicePackageName
+     * without READ_PRIVILEGED_PHONE_STATE permission.
+     */
+    @Test
+    public void testGetDefaultCarrierServicePackageName_noPermission() {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.getDefaultCarrierServicePackageName());
+    }
+
+    /**
+     * Verifies that the right default carrier service package name is return when calling
+     * getDefaultCarrierServicePackageName with permission.
+     */
+    @Test
+    public void testGetDefaultCarrierServicePackageName_withPermission() {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThat(mCarrierConfigLoader.getDefaultCarrierServicePackageName())
+                .isEqualTo(PLATFORM_CARRIER_CONFIG_PACKAGE);
+    }
+
+    // TODO(b/184040111): Enable test case when support disabling carrier privilege
+    // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
+    // when running the test here.
+    /**
+     * Verifies that without permission, #getConfigForSubId will return an empty PersistableBundle.
+     */
+    @Ignore
+    public void testGetConfigForSubId_noPermission() {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
+
+        assertThat(mCarrierConfigLoader.getConfigForSubId(DEFAULT_SUB_ID,
+                PLATFORM_CARRIER_CONFIG_PACKAGE)).isEqualTo(PersistableBundle.EMPTY);
+    }
+
+    /**
+     * Verifies that when have no DUMP permission, the #dump() method shows permission denial.
+     */
+    @Test
+    public void testDump_noPermission() {
+        StringWriter stringWriter = new StringWriter();
+        mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+                new String[0]);
+        stringWriter.flush();
+
+        assertThat(stringWriter.toString()).contains("Permission Denial:");
+    }
+
+    /**
+     * Verifies that when have DUMP permission, the #dump() method can dump the CarrierConfigLoader.
+     */
+    @Test
+    public void testDump_withPermission() {
+        mContext.grantPermission(android.Manifest.permission.DUMP);
+
+        StringWriter stringWriter = new StringWriter();
+        mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+                new String[0]);
+        stringWriter.flush();
+
+        String dumpContent = stringWriter.toString();
+        assertThat(dumpContent).contains("CarrierConfigLoader:");
+        assertThat(dumpContent).doesNotContain("Permission Denial:");
+    }
+
+    private static PersistableBundle getTestConfig() {
+        PersistableBundle config = new PersistableBundle();
+        config.putInt(CARRIER_CONFIG_EXAMPLE_KEY, CARRIER_CONFIG_EXAMPLE_VALUE);
+        return config;
+    }
+
+    private void setCarrierPrivilegesForSubId(boolean hasCarrierPrivileges, int subId) {
+        TelephonyManager mockTelephonyManager = Mockito.mock(TelephonyManager.class);
+        doReturn(mockTelephonyManager).when(mTelephonyManager).createForSubscriptionId(subId);
+        doReturn(hasCarrierPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+                : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS).when(
+                mockTelephonyManager).getCarrierPrivilegeStatus(anyInt());
+    }
+}
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index 54333bb..c7d0c8f 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -85,7 +85,7 @@
  */
 public class RcsProvisioningMonitorTest {
     private static final String TAG = "RcsProvisioningMonitorTest";
-    private static final String SAMPLE_CONFIG = "<RCSConfig>\n"
+    private static final String CONFIG_DEFAULT = "<RCSConfig>\n"
             + "\t<rcsVolteSingleRegistration>1</rcsVolteSingleRegistration>\n"
             + "\t<SERVICES>\n"
             + "\t\t<SupportedRCSProfileVersions>UP_2.0</SupportedRCSProfileVersions>\n"
@@ -104,6 +104,9 @@
             + "\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 int FAKE_SUB_ID_BASE = 0x0FFFFFF0;
     private static final String DEFAULT_MESSAGING_APP1 = "DMA1";
     private static final String DEFAULT_MESSAGING_APP2 = "DMA2";
@@ -252,7 +255,7 @@
         when(mCursor.moveToFirst()).thenReturn(true);
         when(mCursor.getColumnIndexOrThrow(any())).thenReturn(1);
         when(mCursor.getBlob(anyInt())).thenReturn(
-                RcsConfig.compressGzip(SAMPLE_CONFIG.getBytes()));
+                RcsConfig.compressGzip(CONFIG_DEFAULT.getBytes()));
 
         mHandlerThread = new HandlerThread("RcsProvisioningMonitorTest");
         mHandlerThread.start();
@@ -278,7 +281,7 @@
         createMonitor(3);
 
         for (int i = 0; i < 3; i++) {
-            assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+            assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                     mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
         }
 
@@ -312,7 +315,7 @@
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
 
         for (int i = 0; i < 3; i++) {
-            assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+            assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                     mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
         }
         verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
@@ -358,7 +361,7 @@
         processAllMessages();
         byte[] configCached = mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE);
 
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(), configCached));
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(), configCached));
         verify(mIImsConfig, times(1)).notifyRcsAutoConfigurationRemoved();
         // The api should be called 2 times, one happens when monitor is initilized,
         // Another happens when DMS is changed.
@@ -421,12 +424,12 @@
         createMonitor(1);
         final ArgumentCaptor<byte[]> argumentBytes = ArgumentCaptor.forClass(byte[].class);
 
-        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, SAMPLE_CONFIG.getBytes(), false);
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
         processAllMessages();
 
         verify(mIImsConfig, atLeastOnce()).notifyRcsAutoConfigurationReceived(
                 argumentBytes.capture(), eq(false));
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(), argumentBytes.getValue()));
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(), argumentBytes.getValue()));
     }
 
     @Test
@@ -447,13 +450,15 @@
         createMonitor(1);
 
         when(mPackageManager.hasSystemFeature(
-                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
         mBundle.putBoolean(
-                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
-        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
 
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
         mBundle.putBoolean(
                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
@@ -468,6 +473,27 @@
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
         assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, null, false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE,
+                CONFIG_SINGLE_REGISTRATION_DISABLED.getBytes(), false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
     }
 
     @Test
@@ -594,12 +620,12 @@
         verify(mCursor, times(1)).getBlob(anyInt());
         assertNull(mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE));
 
-        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, SAMPLE_CONFIG.getBytes(), false);
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
         processAllMessages();
 
         //config cahced in monitor should be updated, but db should not
         assertNull(mProvider.getContentValues());
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                 mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE)));
 
         //verify if monitor goes back to normal mode
@@ -609,12 +635,12 @@
         verify(mCursor, times(2)).getBlob(anyInt());
         assertNull(mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE));
 
-        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, SAMPLE_CONFIG.getBytes(), false);
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
         processAllMessages();
 
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                 mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE)));
-        assertTrue(Arrays.equals(RcsConfig.compressGzip(SAMPLE_CONFIG.getBytes()),
+        assertTrue(Arrays.equals(RcsConfig.compressGzip(CONFIG_DEFAULT.getBytes()),
                 (byte[]) mProvider.getContentValues().get(SimInfo.COLUMN_RCS_CONFIG)));
     }
 
diff --git a/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
index 5e05085..f69b9a8 100644
--- a/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
@@ -173,12 +173,12 @@
 
     @SmallTest
     @Test
-    public void testDelegateConnectionCloseDialog() throws Exception {
+    public void testDelegateConnectionCloseSession() throws Exception {
         MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
                 Runnable::run, mDelegateMessageCallback);
         tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().closeDialog("testCallId");
-        verify(mISipDelegate).closeDialog("testCallId");
+        tracker.getDelegateConnection().cleanupSession("testCallId");
+        verify(mISipDelegate).cleanupSession("testCallId");
     }
 
     @SmallTest