Merge changes from topics "MEP_Integrate_AIDL_SlotStatus", "telephonyApiForMep"

* changes:
  [MEP] Refactor PhoneInterfaceManager to integrate with latest MEP platform changes.
  [MEP] Adding getSimSlotMapping Telephony API
diff --git a/Android.bp b/Android.bp
index 0649958..742de0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,26 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 // Build the Phone app which includes the emergency dialer. See Contacts
 // for the 'other' dialer.
-
-package {
-    default_applicable_licenses: ["packages_services_Telephony_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
-    name: "packages_services_Telephony_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-    ],
-    license_text: [
-        "NOTICE",
-    ],
-}
-
 android_app {
     name: "TeleService",
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5050ea3..a4fa767 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -709,7 +709,7 @@
                 <action android:name="android.telephony.NetworkService" />
             </intent-filter>
         </service>
-        <service android:name="com.android.internal.telephony.dataconnection.CellularDataService"
+        <service android:name="com.android.internal.telephony.data.CellularDataService"
             android:exported="true"
             android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
             <intent-filter>
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2008, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/OWNERS b/OWNERS
index c394f15..b4ef543 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
+amitmahajan@google.com
 breadley@google.com
 fionaxu@google.com
 jackyu@google.com
diff --git a/apex/Android.bp b/apex/Android.bp
index a0e5713..fbb70db 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 apex_defaults {
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 1138b5e..1c6f4a3 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -14,11 +14,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 apex {
diff --git a/ecc/conversion_toolset_v1/proto/Android.bp b/ecc/conversion_toolset_v1/proto/Android.bp
index 632ab40..cefd789 100644
--- a/ecc/conversion_toolset_v1/proto/Android.bp
+++ b/ecc/conversion_toolset_v1/proto/Android.bp
@@ -14,11 +14,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 java_library_static {
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6fda189..1684e89 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1446,19 +1446,19 @@
     <!-- ECM: Displays the time when ECM will end, Example: "No Data Connection until 10:45 AM" -->
     <string name="phone_in_ecm_notification_complete_time">No data connection until <xliff:g id="completeTime">%s</xliff:g></string>
     <!-- ECM: Dialog box message for exiting from the notifications screen -->
-    <plurals name="alert_dialog_exit_ecm">
+    <string name="alert_dialog_exit_ecm"> {count, plural,
         <!-- number of minutes is one -->
-        <item quantity="one">The phone will be in Emergency Callback mode for <xliff:g id="count">%s</xliff:g> minute. While in this mode no apps using a data connection can be used. Do you want to exit now?</item>
+        =1    {The phone will be in Emergency Callback mode for one minute. While in this mode no apps using a data connection can be used. Do you want to exit now?}
         <!-- number of minutes is not equal to one -->
-        <item quantity="other">The phone will be in Emergency Callback mode for <xliff:g id="count">%s</xliff:g> minutes. While in this mode no applications using a data connection can be used. Do you want to exit now?</item>
-    </plurals>
+        other {The phone will be in Emergency Callback mode for %s minutes. While in this mode no applications using a data connection can be used. Do you want to exit now?}
+    }</string>
     <!-- ECM: Dialog box message for exiting from any other app -->
-    <plurals name="alert_dialog_not_avaialble_in_ecm">
+    <string name="alert_dialog_not_avaialble_in_ecm"> {count, plural,
         <!-- number of minutes is one -->
-        <item quantity="one">The selected action isn\'t available while in the Emergency Callback mode. The phone will be in this mode for <xliff:g id="count">%s</xliff:g> minute. Do you want to exit now?</item>
+        =1    {The selected action isn\'t available while in the Emergency Callback mode. The phone will be in this mode for one minute. Do you want to exit now?}
         <!-- number of minutes is not equal to one -->
-        <item quantity="other">The selected action isn\'t available while in the Emergency Callback mode. The phone will be in this mode for <xliff:g id="count">%s</xliff:g> minutes. Do you want to exit now?</item>
-    </plurals>
+        other {The selected action isn\'t available while in the Emergency Callback mode. The phone will be in this mode for %s minutes. Do you want to exit now?}
+    }</string>
     <!-- ECM: Dialog box message while in emergency call -->
     <string name="alert_dialog_in_ecm_call">The selected action isn\'t available while in an emergency call.</string>
     <!-- ECM: Progress text -->
@@ -1474,12 +1474,12 @@
     <!-- ECM: Displays the time when ECM will end without data restriction hint, Example: "Until 10:45 AM" -->
     <string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint">Until <xliff:g id="completeTime">%s</xliff:g></string>
     <!-- ECM: Dialog box message without data restriction hint for exiting from the notifications screen -->
-    <plurals name="alert_dialog_exit_ecm_without_data_restriction_hint">
+    <string name="alert_dialog_exit_ecm_without_data_restriction_hint"> {count, plural,
         <!-- number of minutes is one -->
-        <item quantity="one">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minute.\nDo you want to exit now?</item>
+        =1    {The phone will be in emergency callback mode for one minute.\nDo you want to exit now?}
         <!-- number of minutes is not equal to one -->
-        <item quantity="other">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minutes.\nDo you want to exit now?</item>
-    </plurals>
+        other {The phone will be in emergency callback mode for %s minutes.\nDo you want to exit now?}
+    }</string>
 
     <!-- For incoming calls, this is a string we can get from a CDMA network instead of
          the actual phone number, to indicate there's no number present.  DO NOT TRANSLATE. -->
diff --git a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
index 2845dac..6b34f00 100644
--- a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
+++ b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
@@ -236,7 +236,8 @@
 
         Intent intent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
+                PendingIntent.FLAG_IMMUTABLE);
 
         Notification.Action action = new Notification.Action.Builder(R.drawable.ic_sim_card,
                 context.getString(R.string.sip_accounts_removed_notification_action),
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index d249fae..e64c81c 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -455,10 +455,16 @@
         boolean useWfcHomeModeForRoaming = carrierConfig.getBoolean(
                     CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL,
                     false);
+        boolean isDataEnabled;
+        if (mPhone.isUsingNewDataStack()) {
+            isDataEnabled = mPhone.getDataSettingsManager().isDataEnabled();
+        } else {
+            isDataEnabled = mPhone.getDataEnabledSettings().isDataEnabled();
+        }
         if (mImsMgr.isVtEnabledByPlatform() && mImsMgr.isVtProvisionedOnDevice()
                 && (carrierConfig.getBoolean(
                         CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
-                || mPhone.getDataEnabledSettings().isDataEnabled())) {
+                || isDataEnabled)) {
             boolean currentValue =
                     mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()
                     ? mImsMgr.isVtEnabledByUser() : false;
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index db1c5b4..2cbb7c5 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -58,6 +58,7 @@
             CarrierXmlParser.SsEntry.SSAction.UNKNOWN;
     private int mAction;
     private HashMap<String, String> mCfInfo;
+    private long mDelayMillisAfterUssdSet = 1000;
 
     public CallForwardEditPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -490,8 +491,9 @@
                 mPhone.getCallForwardingOption(reason, mServiceClass,
                         obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
             } else {
-                mHandler.sendMessage(mHandler.obtainMessage(mHandler.MESSAGE_GET_CF_USSD,
-                        msg.arg1, MyHandler.MESSAGE_SET_CF, ar.exception));
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(mHandler.MESSAGE_GET_CF_USSD,
+                        msg.arg1, MyHandler.MESSAGE_SET_CF, ar.exception),
+                        mDelayMillisAfterUssdSet);
             }
         }
 
diff --git a/src/com/android/phone/CallWaitingSwitchPreference.java b/src/com/android/phone/CallWaitingSwitchPreference.java
index 01dd3b2..609488c 100644
--- a/src/com/android/phone/CallWaitingSwitchPreference.java
+++ b/src/com/android/phone/CallWaitingSwitchPreference.java
@@ -6,28 +6,34 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.preference.SwitchPreference;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
 
-import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 public class CallWaitingSwitchPreference extends SwitchPreference {
     private static final String LOG_TAG = "CallWaitingSwitchPreference";
+    private static final int DELAY_MILLIS_FOR_USSD = 1000;
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private final MyHandler mHandler = new MyHandler();
     private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
-    private Executor mExecutor;
+    private ScheduledExecutorService 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;
+    private boolean mUssdMode = false;
 
     public CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -45,9 +51,14 @@
             TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
         mPhone = phone;
         mTcpListener = listener;
-        mExecutor = Executors.newSingleThreadExecutor();
+        mExecutor = Executors.newSingleThreadScheduledExecutor();
         mTelephonyManager = getContext().getSystemService(
                 TelephonyManager.class).createForSubscriptionId(phone.getSubId());
+        CarrierConfigManager configManager = getContext().getSystemService(
+                CarrierConfigManager.class);
+        PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId());
+        mUssdMode = (bundle != null) ? bundle.getBoolean(
+                CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false) : false;
 
         if (!skipReading) {
             Log.d(LOG_TAG, "init getCallWaitingStatus");
@@ -67,7 +78,23 @@
     private void updateStatusCallBack(int result) {
         Log.d(LOG_TAG, "updateStatusCallBack: CW state " + result + ", and re get");
         mUpdateStatus = result;
-        mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
+        if (mUssdMode) {
+            Log.d(LOG_TAG, "updateStatusCallBack: USSD mode needs to wait 1s since Framework"
+                    + " has the limitation");
+            Consumer<Integer> resultListener = this::queryStatusCallBack;
+            try {
+                mExecutor.schedule(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTelephonyManager.getCallWaitingStatus(mExecutor, resultListener);
+                    }
+                }, DELAY_MILLIS_FOR_USSD, TimeUnit.MILLISECONDS);
+            } catch (Exception e) {
+                Log.d(LOG_TAG, "Exception while waiting: " + e);
+            }
+        } else {
+            mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
+        }
     }
 
     @Override
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index e276082..8a007b6 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -65,6 +65,7 @@
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -1064,6 +1065,7 @@
         }
 
         String fileName;
+        String iccid = null;
         if (isNoSimConfig) {
             fileName = getFilenameForNoSimConfig(packageName);
         } else {
@@ -1073,7 +1075,7 @@
                 return null;
             }
 
-            final String iccid = getIccIdForPhoneId(phoneId);
+            iccid = getIccIdForPhoneId(phoneId);
             final int cid = getSpecificCarrierIdForPhoneId(phoneId);
             if (iccid == null) {
                 loge("Cannot restore config with null iccid.");
@@ -1102,7 +1104,15 @@
         } catch (FileNotFoundException e) {
             // Missing file is normal occurrence that might occur with a new sim or when restoring
             // an override file during boot and should not be treated as an error.
-            if (file != null) logd("File not found: " + file.getPath());
+            if (file != null) {
+                if (isNoSimConfig) {
+                    logd("File not found: " + file.getPath());
+                } else {
+                    String filePath = file.getPath();
+                    filePath = getFilePathForLogging(filePath, iccid);
+                    logd("File not found : " + filePath);
+                }
+            }
         } catch (IOException e) {
             loge(e.toString());
         }
@@ -1110,6 +1120,22 @@
         return restoredBundle;
     }
 
+    /**
+     * This method will mask most part of iccid in the filepath for logging on userbuild
+     */
+    private String getFilePathForLogging(String filePath, String iccid) {
+        // If loggable then return with actual file path
+        if (Rlog.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            return filePath;
+        }
+        String path = filePath;
+        int length = (iccid != null) ? iccid.length() : 0;
+        if (length > 5 && filePath != null) {
+            path = filePath.replace(iccid.substring(5), "***************");
+        }
+        return path;
+    }
+
     private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
             int phoneId) {
         return restoreConfigFromXml(packageName, extraString, phoneId, false);
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index adba850..fc0e513 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -30,6 +30,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.icu.text.MessageFormat;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.CountDownTimer;
@@ -43,6 +44,9 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Displays dialog that enables users to exit Emergency Callback Mode
  *
@@ -297,20 +301,22 @@
         int minutes = (int)(millisUntilFinished / 60000);
         String time = String.format("%d:%02d", minutes,
                 (millisUntilFinished % 60000) / 1000);
+        Map<String, Object> msgArgs = new HashMap<>();
+        msgArgs.put("count", minutes);
 
         switch (mDialogType) {
         case EXIT_ECM_BLOCK_OTHERS:
-            return String.format(getResources().getQuantityText(
-                    R.plurals.alert_dialog_not_avaialble_in_ecm, minutes).toString(), time);
+            return MessageFormat.format(getResources().getString(
+                    R.string.alert_dialog_not_avaialble_in_ecm, time), msgArgs);
         case EXIT_ECM_DIALOG:
                 boolean shouldRestrictData = mPhone.getImsPhone() != null
                         && mPhone.getImsPhone().isInImsEcm();
-                return String.format(getResources().getQuantityText(
+                return MessageFormat.format(getResources().getString(
                         // During IMS ECM, data restriction hint should be removed.
                         shouldRestrictData
-                        ? R.plurals.alert_dialog_exit_ecm_without_data_restriction_hint
-                        : R.plurals.alert_dialog_exit_ecm,
-                        minutes).toString(), time);
+                        ? R.string.alert_dialog_exit_ecm_without_data_restriction_hint
+                        : R.string.alert_dialog_exit_ecm,
+                        time), msgArgs);
         }
         return null;
     }
diff --git a/src/com/android/phone/ImsProvisioningController.java b/src/com/android/phone/ImsProvisioningController.java
new file mode 100644
index 0000000..d78e565
--- /dev/null
+++ b/src/com/android/phone/ImsProvisioningController.java
@@ -0,0 +1,1489 @@
+/*
+ * 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 android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_DISABLED;
+import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
+import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_MAX;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NR;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CarrierConfigManager.Ims;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyRegistryManager;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.SparseArray;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.util.HandlerExecutor;
+import com.android.telephony.Rlog;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Provides APIs for MMTEL and RCS provisioning status. This class handles provisioning status and
+ * notifies the status changing for each capability
+ * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
+ * {{@link RcsImsCapabilities.RcsImsCapabilityFlag} for RCS services}
+ */
+public class ImsProvisioningController {
+    private static final String TAG = "ImsProvisioningController";
+    private static final int INVALID_VALUE = -1;
+
+    private static final int EVENT_SUB_CHANGED = 1;
+    private static final int EVENT_PROVISIONING_CAPABILITY_CHANGED = 2;
+    @VisibleForTesting
+    protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
+
+    // Provisioning Keys that are handled via AOSP cache and not sent to the ImsService
+    private static final int[] LOCAL_IMS_CONFIG_KEYS = {
+            KEY_VOLTE_PROVISIONING_STATUS,
+            KEY_VT_PROVISIONING_STATUS,
+            KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
+            KEY_EAB_PROVISIONING_STATUS
+    };
+    private static final int[] LOCAL_RADIO_TECHS = {
+            REGISTRATION_TECH_LTE,
+            REGISTRATION_TECH_IWLAN,
+            REGISTRATION_TECH_CROSS_SIM,
+            REGISTRATION_TECH_NR
+    };
+
+    private static final int MMTEL_CAPABILITY_MIN = MmTelCapabilities.CAPABILITY_TYPE_NONE;
+    private static final int MMTEL_CAPABILITY_MAX = MmTelCapabilities.CAPABILITY_TYPE_MAX;
+
+    private static final int RCS_CAPABILITY_MIN = RcsImsCapabilities.CAPABILITY_TYPE_NONE;
+    private static final int RCS_CAPABILITY_MAX = RcsImsCapabilities.CAPABILITY_TYPE_MAX;
+
+    private static final int[] LOCAL_MMTEL_CAPABILITY = {
+            CAPABILITY_TYPE_VOICE,
+            CAPABILITY_TYPE_VIDEO,
+            CAPABILITY_TYPE_UT,
+            CAPABILITY_TYPE_SMS,
+            CAPABILITY_TYPE_CALL_COMPOSER
+    };
+
+    /**
+     * map the MmTelCapabilities.MmTelCapability and
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_UT_INT
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_SMS_INT
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT
+     */
+    private static final Map<Integer, String> KEYS_MMTEL_CAPABILITY = Map.of(
+            CAPABILITY_TYPE_VOICE, Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
+            CAPABILITY_TYPE_VIDEO, Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
+            CAPABILITY_TYPE_UT, Ims.KEY_CAPABILITY_TYPE_UT_INT_ARRAY,
+            CAPABILITY_TYPE_SMS, Ims.KEY_CAPABILITY_TYPE_SMS_INT_ARRAY,
+            CAPABILITY_TYPE_CALL_COMPOSER, Ims.KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY
+    );
+
+    /**
+     * map the RcsImsCapabilities.RcsImsCapabilityFlag and
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE
+     * CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE
+     */
+    private static final Map<Integer, String> KEYS_RCS_CAPABILITY = Map.of(
+            CAPABILITY_TYPE_OPTIONS_UCE, Ims.KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY,
+            CAPABILITY_TYPE_PRESENCE_UCE, Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY
+    );
+
+    /**
+     * Create a FeatureConnector for this class to use to connect to an ImsManager.
+     */
+    @VisibleForTesting
+    public interface MmTelFeatureConnector {
+        /**
+         * Create a FeatureConnector for this class to use to connect to an ImsManager.
+         * @param listener will receive ImsManager instance.
+         * @param executor that the Listener callbacks will be called on.
+         * @return A FeatureConnector
+         */
+        FeatureConnector<ImsManager> create(Context context, int slotId,
+                String logPrefix, FeatureConnector.Listener<ImsManager> listener,
+                Executor executor);
+    }
+
+    /**
+     * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
+     */
+    @VisibleForTesting
+    public interface RcsFeatureConnector {
+        /**
+         * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
+         * @param listener will receive RcsFeatureManager instance.
+         * @param executor that the Listener callbacks will be called on.
+         * @return A FeatureConnector
+         */
+        FeatureConnector<RcsFeatureManager> create(Context context, int slotId,
+                FeatureConnector.Listener<RcsFeatureManager> listener,
+                Executor executor, String logPrefix);
+    }
+
+    private static ImsProvisioningController sInstance;
+
+    private final PhoneGlobals mApp;
+    private final Handler mHandler;
+    private final CarrierConfigManager mCarrierConfigManager;
+    private final SubscriptionManager mSubscriptionManager;
+    private final TelephonyRegistryManager mTelephonyRegistryManager;
+    private final MmTelFeatureConnector mMmTelFeatureConnector;
+    private final RcsFeatureConnector mRcsFeatureConnector;
+
+    // maps a slotId to a list of MmTelFeatureListeners
+    private final SparseArray<MmTelFeatureListener> mMmTelFeatureListenersSlotMap =
+            new SparseArray<>();
+    // maps a slotId to a list of RcsFeatureListeners
+    private final SparseArray<RcsFeatureListener> mRcsFeatureListenersSlotMap =
+            new SparseArray<>();
+    // map a slotId to a list of ProvisioningCallbackManager
+    private final SparseArray<ProvisioningCallbackManager> mProvisioningCallbackManagersSlotMap =
+            new SparseArray<>();
+    private final ImsProvisioningLoader mImsProvisioningLoader;
+
+    private int mNumSlot;
+
+    /**
+     * This class contains the provisioning status to notify changes.
+     * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
+     * {{@link RcsImsCapabilities.RcsImsCapabilityFlag} for RCS services}
+     * {{@link ImsRegistrationImplBase.ImsRegistrationTech} for Registration tech}
+     */
+    private static final class FeatureProvisioningData {
+        public final int mCapability;
+        public final int mTech;
+        public final boolean mProvisioned;
+        public final boolean mIsMmTel;
+
+        FeatureProvisioningData(int capability, int tech, boolean provisioned, boolean isMmTel) {
+            mCapability = capability;
+            mTech = tech;
+            mProvisioned = provisioned;
+            mIsMmTel = isMmTel;
+        }
+    }
+
+    private final class MessageHandler extends Handler {
+        private static final String LOG_PREFIX = "Handler";
+        MessageHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_SUB_CHANGED:
+                    onSubscriptionsChanged();
+                    break;
+                case EVENT_PROVISIONING_CAPABILITY_CHANGED:
+                    try {
+                        mProvisioningCallbackManagersSlotMap.get(msg.arg1)
+                                .notifyProvisioningCapabilityChanged(
+                                        (FeatureProvisioningData) msg.obj);
+                    } catch (NullPointerException e) {
+                        logw(LOG_PREFIX, msg.arg1,
+                                "can not find callback manager message" + msg.what);
+                    }
+                    break;
+                case EVENT_MULTI_SIM_CONFIGURATION_CHANGE:
+                    int activeModemCount = (int) ((AsyncResult) msg.obj).result;
+                    onMultiSimConfigChanged(activeModemCount);
+                    break;
+                default:
+                    log("unknown message " + msg);
+                    break;
+            }
+        }
+    }
+
+    private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
+            new SubscriptionManager.OnSubscriptionsChangedListener() {
+                @Override
+                public void onSubscriptionsChanged() {
+                    if (!mHandler.hasMessages(EVENT_SUB_CHANGED)) {
+                        mHandler.sendEmptyMessage(EVENT_SUB_CHANGED);
+                    }
+                }
+            };
+
+    private final class ProvisioningCallbackManager {
+        private static final String LOG_PREFIX = "ProvisioningCallbackManager";
+        private RemoteCallbackList<IFeatureProvisioningCallback> mIFeatureProvisioningCallbackList;
+        private int mSubId;
+        private int mSlotId;
+
+        ProvisioningCallbackManager(int slotId) {
+            mIFeatureProvisioningCallbackList =
+                    new RemoteCallbackList<IFeatureProvisioningCallback>();
+            mSlotId = slotId;
+            mSubId = getSubId(slotId);
+            log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager create");
+        }
+
+        public void clear() {
+            log(LOG_PREFIX, mSlotId, "ProvisioningCallbackManager clear ");
+
+            mIFeatureProvisioningCallbackList.kill();
+
+            // All registered callbacks are unregistered, and the list is disabled
+            // need to create again
+            mIFeatureProvisioningCallbackList =
+                    new RemoteCallbackList<IFeatureProvisioningCallback>();
+        }
+
+        public void registerCallback(IFeatureProvisioningCallback localCallback) {
+            if (!mIFeatureProvisioningCallbackList.register(localCallback, (Object) mSubId)) {
+                log(LOG_PREFIX, mSlotId, "registration callback fail");
+            }
+        }
+
+        public void unregisterCallback(IFeatureProvisioningCallback localCallback) {
+            mIFeatureProvisioningCallbackList.unregister(localCallback);
+        }
+
+        public void setSubId(int subId) {
+            if (mSubId == subId) {
+                log(LOG_PREFIX, mSlotId, "subId is not changed ");
+                return;
+            }
+
+            mSubId = subId;
+            mSlotId = getSlotId(subId);
+
+            // subId changed means the registered callbacks are not available.
+            clear();
+        }
+
+        public boolean hasCallblacks() {
+            int size = mIFeatureProvisioningCallbackList.beginBroadcast();
+            mIFeatureProvisioningCallbackList.finishBroadcast();
+
+            return (size > 0);
+        }
+
+        public void notifyProvisioningCapabilityChanged(FeatureProvisioningData data) {
+            int size = mIFeatureProvisioningCallbackList.beginBroadcast();
+            for (int index = 0; index < size; index++) {
+                try {
+                    IFeatureProvisioningCallback imsFeatureProvisioningCallback =
+                            mIFeatureProvisioningCallbackList.getBroadcastItem(index);
+
+                    // MMTEL
+                    if (data.mIsMmTel
+                            && Arrays.stream(LOCAL_MMTEL_CAPABILITY)
+                            .anyMatch(value -> value == data.mCapability)) {
+                        imsFeatureProvisioningCallback.onFeatureProvisioningChanged(
+                                data.mCapability, data.mTech, data.mProvisioned);
+                        logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
+                                + "onFeatureProvisioningChanged"
+                                + " capability " + data.mCapability
+                                + " tech "  + data.mTech
+                                + " isProvisioned " + data.mProvisioned);
+                    } else if (data.mCapability == CAPABILITY_TYPE_PRESENCE_UCE) {
+                        imsFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
+                                data.mCapability, data.mTech, data.mProvisioned);
+                        logi(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
+                                + "onRcsFeatureProvisioningChanged"
+                                + " capability " + data.mCapability
+                                + " tech "  + data.mTech
+                                + " isProvisioned " + data.mProvisioned);
+                    } else {
+                        loge(LOG_PREFIX, mSlotId, "notifyProvisioningCapabilityChanged : "
+                                + "unknown capability "
+                                + data.mCapability);
+                    }
+                } catch (RemoteException e) {
+                    loge(LOG_PREFIX, mSlotId,
+                            "notifyProvisioningChanged: callback #" + index + " failed");
+                }
+            }
+            mIFeatureProvisioningCallbackList.finishBroadcast();
+        }
+    }
+
+    private final class MmTelFeatureListener implements FeatureConnector.Listener<ImsManager> {
+        private static final String LOG_PREFIX = "MmTelFeatureListener";
+        private FeatureConnector<ImsManager> mConnector;
+        private ImsManager mImsManager;
+        private boolean mReady = false;
+        // stores whether the initial provisioning key value should be notified to ImsService
+        private boolean mRequiredNotify = false;
+        private int mSubId;
+        private int mSlotId;
+
+        MmTelFeatureListener(int slotId) {
+            log(LOG_PREFIX, slotId, "created");
+
+            mSlotId = slotId;
+            mSubId = getSubId(slotId);
+            mConnector = mMmTelFeatureConnector.create(
+                    mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
+            mConnector.connect();
+        }
+
+        public void setSubId(int subId) {
+            if (mRequiredNotify && mReady) {
+                mRequiredNotify = false;
+                setInitialProvisioningKeys(subId);
+            }
+            if (mSubId == subId) {
+                log(LOG_PREFIX, mSlotId, "subId is not changed");
+                return;
+            }
+
+            mSubId = subId;
+            mSlotId = getSlotId(subId);
+        }
+
+        public void destroy() {
+            log("destroy");
+            mConnector.disconnect();
+            mConnector = null;
+            mReady = false;
+            mImsManager = null;
+        }
+
+        public @Nullable ImsManager getImsManager() {
+            return mImsManager;
+        }
+
+        @Override
+        public void connectionReady(ImsManager manager, int subId) {
+            log(LOG_PREFIX, mSlotId, "connection ready");
+            mReady = true;
+            mImsManager = manager;
+
+            onMmTelAvailable();
+        }
+
+        @Override
+        public void connectionUnavailable(int reason) {
+            log(LOG_PREFIX, mSlotId, "connection unavailable " + reason);
+
+            mReady = false;
+            mImsManager = null;
+
+            // keep the callback for other reason
+            if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
+                onMmTelUnavailable();
+            }
+        }
+
+        public int setProvisioningValue(int key, int value) {
+            int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
+
+            if (!mReady) {
+                loge(LOG_PREFIX, mSlotId, "service is Unavailable");
+                return retVal;
+            }
+            try {
+                // getConfigInterface() will return not null or throw the ImsException
+                // need not null checking
+                ImsConfig imsConfig = getImsConfig(mImsManager);
+                retVal = imsConfig.setConfig(key, value);
+                log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
+            } catch (ImsException e) {
+                logw(LOG_PREFIX, mSlotId,
+                        "setConfig operation failed for key =" + key
+                        + ", value =" + value + ". Exception:" + e.getMessage());
+            }
+            return retVal;
+        }
+
+        public int getProvisioningValue(int key) {
+            if (!mReady) {
+                loge(LOG_PREFIX, mSlotId, "service is Unavailable");
+                return INVALID_VALUE;
+            }
+
+            int retValue = INVALID_VALUE;
+            try {
+                // getConfigInterface() will return not null or throw the ImsException
+                // need not null checking
+                ImsConfig imsConfig = getImsConfig(mImsManager);
+                retValue = imsConfig.getConfigInt(key);
+            } catch (ImsException e) {
+                logw(LOG_PREFIX, mSlotId,
+                        "getConfig operation failed for key =" + key
+                        + ", value =" + retValue + ". Exception:" + e.getMessage());
+            }
+            return retValue;
+        }
+
+        public void onMmTelAvailable() {
+            log(LOG_PREFIX, mSlotId, "onMmTelAvailable");
+
+            if (isValidSubId(mSubId)) {
+                mRequiredNotify = false;
+
+                // notify provisioning key value to ImsService
+                setInitialProvisioningKeys(mSubId);
+            } else {
+                // wait until subId is valid
+                mRequiredNotify = true;
+            }
+        }
+
+        public void onMmTelUnavailable() {
+            log(LOG_PREFIX, mSlotId, "onMmTelUnavailable");
+
+            try {
+                // delete all callbacks reference from ProvisioningManager
+                mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
+            } catch (NullPointerException e) {
+                logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
+            }
+        }
+
+        private void setInitialProvisioningKeys(int subId) {
+            boolean required;
+            int value = ImsProvisioningLoader.STATUS_NOT_SET;
+
+            // updating KEY_VOLTE_PROVISIONING_STATUS
+            required = isProvisioningRequired(subId, CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE,
+                    /*isMmTel*/true);
+            log(LOG_PREFIX, mSlotId,
+                    "setInitialProvisioningKeys provisioning required(voice, lte) " + required);
+            if (required) {
+                value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
+                        CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
+                if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
+                    value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
+                            ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
+                    setProvisioningValue(KEY_VOLTE_PROVISIONING_STATUS, value);
+                }
+            }
+
+            // updating KEY_VT_PROVISIONING_STATUS
+            required = isProvisioningRequired(subId, CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE,
+                    /*isMmTel*/true);
+            log(LOG_PREFIX, mSlotId,
+                    "setInitialProvisioningKeys provisioning required(video, lte) " + required);
+            if (required) {
+                value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
+                        CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
+                if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
+                    value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
+                            ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
+                    setProvisioningValue(KEY_VT_PROVISIONING_STATUS, value);
+                }
+            }
+
+            // updating KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
+            required = isProvisioningRequired(subId, CAPABILITY_TYPE_VOICE,
+                    REGISTRATION_TECH_IWLAN, /*isMmTel*/true);
+            log(LOG_PREFIX, mSlotId,
+                    "setInitialProvisioningKeys provisioning required(voice, iwlan) " + required);
+            if (required) {
+                value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
+                        CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
+                if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
+                    value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
+                            ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
+                    setProvisioningValue(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, value);
+                }
+            }
+        }
+    }
+
+    private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
+        private static final String LOG_PREFIX = "RcsFeatureListener";
+        private FeatureConnector<RcsFeatureManager> mConnector;
+        private RcsFeatureManager mRcsFeatureManager;
+        private boolean mReady = false;
+        // stores whether the initial provisioning key value should be notified to ImsService
+        private boolean mRequiredNotify = false;
+        private int mSubId;
+        private int mSlotId;
+
+        RcsFeatureListener(int slotId) {
+            log(LOG_PREFIX, slotId, "created");
+
+            mSlotId = slotId;
+            mSubId = getSubId(slotId);
+            mConnector = mRcsFeatureConnector.create(
+                    mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
+            mConnector.connect();
+        }
+
+        public void setSubId(int subId) {
+            if (mRequiredNotify && mReady) {
+                mRequiredNotify = false;
+                setInitialProvisioningKeys(subId);
+            }
+            if (mSubId == subId) {
+                log(LOG_PREFIX, mSlotId, "subId is not changed");
+                return;
+            }
+
+            mSubId = subId;
+            mSlotId = getSlotId(subId);
+        }
+
+        public void destroy() {
+            log(LOG_PREFIX, mSlotId, "destroy");
+            mConnector.disconnect();
+            mConnector = null;
+            mReady = false;
+            mRcsFeatureManager = null;
+        }
+
+        @Override
+        public void connectionReady(RcsFeatureManager manager, int subId) {
+            log(LOG_PREFIX, mSlotId, "connection ready");
+            mReady = true;
+            mRcsFeatureManager = manager;
+
+            onRcsAvailable();
+        }
+
+        @Override
+        public void connectionUnavailable(int reason) {
+            log(LOG_PREFIX, mSlotId, "connection unavailable");
+            mReady = false;
+            mRcsFeatureManager = null;
+
+            // keep the callback for other reason
+            if (reason == FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED) {
+                onRcsUnavailable();
+            }
+        }
+
+        public int setProvisioningValue(int key, int value) {
+            int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
+
+            if (!mReady) {
+                loge(LOG_PREFIX, mSlotId, "service is Unavailable");
+                return retVal;
+            }
+
+            try {
+                // getConfigInterface() will return not null or throw the ImsException
+                // need not null checking
+                ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+                retVal = imsConfig.setConfig(key, value);
+                log(LOG_PREFIX, mSlotId, "setConfig called with key " + key + " value " + value);
+            } catch (ImsException e) {
+                logw(LOG_PREFIX, mSlotId,
+                        "setConfig operation failed for key =" + key
+                        + ", value =" + value + ". Exception:" + e.getMessage());
+            }
+            return retVal;
+        }
+
+        public int getProvisioningValue(int key) {
+            if (!mReady) {
+                loge(LOG_PREFIX, mSlotId, "service is Unavailable");
+                return INVALID_VALUE;
+            }
+
+            int retValue = INVALID_VALUE;
+            try {
+                // getConfigInterface() will return not null or throw the ImsException
+                // need not null checking
+                ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+                retValue = imsConfig.getConfigInt(key);
+            } catch (ImsException e) {
+                logw(LOG_PREFIX, mSlotId,
+                        "getConfig operation failed for key =" + key
+                        + ", value =" + retValue + ". Exception:" + e.getMessage());
+            }
+            return retValue;
+        }
+
+        public void onRcsAvailable() {
+            log(LOG_PREFIX, mSlotId, "onRcsAvailable");
+
+            if (isValidSubId(mSubId)) {
+                mRequiredNotify = false;
+
+                // notify provisioning key value to ImsService
+                setInitialProvisioningKeys(mSubId);
+            } else {
+                // wait until subId is valid
+                mRequiredNotify = true;
+            }
+        }
+
+        public void onRcsUnavailable() {
+            log(LOG_PREFIX, mSlotId, "onRcsUnavailable");
+
+            try {
+                // delete all callbacks reference from ProvisioningManager
+                mProvisioningCallbackManagersSlotMap.get(getSlotId(mSubId)).clear();
+            } catch (NullPointerException e) {
+                logw(LOG_PREFIX, getSlotId(mSubId), "can not find callback manager to clear");
+            }
+        }
+
+        private void setInitialProvisioningKeys(int subId) {
+            boolean required;
+            int value = ImsProvisioningLoader.STATUS_NOT_SET;
+
+            // KEY_EAB_PROVISIONING_STATUS
+            int capability = CAPABILITY_TYPE_PRESENCE_UCE;
+            // Assume that all radio techs have the same provisioning value
+            int tech = REGISTRATION_TECH_LTE;
+
+            required = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
+            if (required) {
+                value = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
+                        capability, tech);
+                if (value != ImsProvisioningLoader.STATUS_NOT_SET) {
+                    value = (value == ImsProvisioningLoader.STATUS_PROVISIONED)
+                            ? PROVISIONING_VALUE_ENABLED : PROVISIONING_VALUE_DISABLED;
+                    setProvisioningValue(KEY_EAB_PROVISIONING_STATUS, value);
+                }
+            }
+        }
+    }
+
+    /**
+     * Do NOT use this directly, instead use {@link #getInstance()}.
+     */
+    @VisibleForTesting
+    public ImsProvisioningController(PhoneGlobals app, int numSlot, Looper looper,
+            MmTelFeatureConnector mmTelFeatureConnector, RcsFeatureConnector rcsFeatureConnector,
+            ImsProvisioningLoader imsProvisioningLoader) {
+        log("ImsProvisioningController");
+        mApp = app;
+        mNumSlot = numSlot;
+        mHandler = new MessageHandler(looper);
+        mMmTelFeatureConnector = mmTelFeatureConnector;
+        mRcsFeatureConnector = rcsFeatureConnector;
+        mCarrierConfigManager = mApp.getSystemService(CarrierConfigManager.class);
+        mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
+        mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
+        mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
+                mSubChangedListener, mSubChangedListener.getHandlerExecutor());
+        mImsProvisioningLoader = imsProvisioningLoader;
+
+        PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
+                EVENT_MULTI_SIM_CONFIGURATION_CHANGE, null);
+
+        initialize(numSlot);
+    }
+
+    private void initialize(int numSlot) {
+        for (int i = 0; i < numSlot; i++) {
+            MmTelFeatureListener m = new MmTelFeatureListener(i);
+            mMmTelFeatureListenersSlotMap.put(i, m);
+
+            RcsFeatureListener r = new RcsFeatureListener(i);
+            mRcsFeatureListenersSlotMap.put(i, r);
+
+            ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
+            mProvisioningCallbackManagersSlotMap.put(i, p);
+        }
+    }
+
+    private void onMultiSimConfigChanged(int newNumSlot) {
+        log("onMultiSimConfigChanged: NumSlot " + mNumSlot + " newNumSlot " + newNumSlot);
+
+        if (mNumSlot < newNumSlot) {
+            for (int i = mNumSlot; i < newNumSlot; i++) {
+                MmTelFeatureListener m = new MmTelFeatureListener(i);
+                mMmTelFeatureListenersSlotMap.put(i, m);
+
+                RcsFeatureListener r = new RcsFeatureListener(i);
+                mRcsFeatureListenersSlotMap.put(i, r);
+
+                ProvisioningCallbackManager p = new ProvisioningCallbackManager(i);
+                mProvisioningCallbackManagersSlotMap.put(i, p);
+            }
+        } else if (mNumSlot > newNumSlot) {
+            for (int i = (mNumSlot - 1); i > (newNumSlot - 1); i--) {
+                MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(i);
+                mMmTelFeatureListenersSlotMap.remove(i);
+                m.destroy();
+
+                RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(i);
+                mRcsFeatureListenersSlotMap.remove(i);
+                r.destroy();
+
+                ProvisioningCallbackManager p = mProvisioningCallbackManagersSlotMap.get(i);
+                mProvisioningCallbackManagersSlotMap.remove(i);
+                p.clear();
+            }
+        }
+
+        mNumSlot = newNumSlot;
+    }
+
+    /**
+     * destroy the instance
+     */
+    @VisibleForTesting
+    public void destroy() {
+        log("destroy");
+
+        mHandler.getLooper().quit();
+
+        mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
+
+        for (int i = 0; i < mMmTelFeatureListenersSlotMap.size(); i++) {
+            mMmTelFeatureListenersSlotMap.get(i).destroy();
+        }
+        mMmTelFeatureListenersSlotMap.clear();
+
+        for (int i = 0; i < mRcsFeatureListenersSlotMap.size(); i++) {
+            mRcsFeatureListenersSlotMap.get(i).destroy();
+        }
+        mRcsFeatureListenersSlotMap.clear();
+
+        for (int i = 0; i < mProvisioningCallbackManagersSlotMap.size(); i++) {
+            mProvisioningCallbackManagersSlotMap.get(i).clear();
+        }
+    }
+
+    /**
+     * create an instance
+     */
+    @VisibleForTesting
+    public static ImsProvisioningController make(PhoneGlobals app, int numSlot) {
+        synchronized (ImsProvisioningController.class) {
+            if (sInstance == null) {
+                Rlog.i(TAG, "ImsProvisioningController created");
+                HandlerThread handlerThread = new HandlerThread(TAG);
+                handlerThread.start();
+                sInstance = new ImsProvisioningController(app, numSlot, handlerThread.getLooper(),
+                        ImsManager::getConnector, RcsFeatureManager::getConnector,
+                        new ImsProvisioningLoader(app));
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Gets a ImsProvisioningController instance
+     */
+    @VisibleForTesting
+    public static ImsProvisioningController getInstance() {
+        synchronized (ImsProvisioningController.class) {
+            return sInstance;
+        }
+    }
+
+    /**
+     * Register IFeatureProvisioningCallback from ProvisioningManager
+     */
+
+    @VisibleForTesting
+    public void addFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("provisioning callback can't be null");
+        }
+        int slotId = getSlotId(subId);
+        if (slotId < 0 || slotId >= mNumSlot) {
+            throw new IllegalArgumentException("subscription id is not available");
+        }
+
+        try {
+            mProvisioningCallbackManagersSlotMap.get(slotId).registerCallback(callback);
+            log("Feature Provisioning Callback registered.");
+        } catch (NullPointerException e) {
+            logw("can not access callback manager to add callback");
+        }
+    }
+
+    /**
+     * Remove IFeatureProvisioningCallback
+     */
+    @VisibleForTesting
+    public void removeFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("provisioning callback can't be null");
+        }
+
+        int slotId = getSlotId(subId);
+        if (slotId < 0 || slotId >= mNumSlot) {
+            throw new IllegalArgumentException("subscription id is not available");
+        }
+
+        try {
+            mProvisioningCallbackManagersSlotMap.get(slotId).unregisterCallback(callback);
+            log("Feature Provisioning Callback removed.");
+        } catch (NullPointerException e) {
+            logw("can not access callback manager to remove callback");
+        }
+    }
+
+    /**
+     * return the boolean whether MmTel capability is required provisioning or not
+     */
+    @VisibleForTesting
+    public boolean isImsProvisioningRequiredForCapability(int subId, int capability, int tech) {
+        // check subId
+        int slotId = getSlotId(subId);
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
+            loge("Fail to retrieve slotId from subId");
+            throw new IllegalArgumentException("subscribe id is invalid");
+        }
+
+        // check valid capability
+        if (!(MMTEL_CAPABILITY_MIN < capability && capability < MMTEL_CAPABILITY_MAX)) {
+            throw new IllegalArgumentException("MmTel capability '" + capability + "' is invalid");
+        }
+
+        // check valid radio tech
+        if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
+            log("Ims not matched radio tech " + tech);
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+
+        boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/true);
+
+        log("isImsProvisioningRequiredForCapability capability " + capability
+                + " tech " + tech + " return value " + retVal);
+
+        return retVal;
+    }
+
+    /**
+     * return the boolean whether RCS capability is required provisioning or not
+     */
+    @VisibleForTesting
+    public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
+        // check slotId and Phone object
+        int slotId = getSlotId(subId);
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
+            loge("Fail to retrieve slotId from subId");
+            throw new IllegalArgumentException("subscribe id is invalid");
+        }
+
+        // check valid capability
+        if (!(RCS_CAPABILITY_MIN < capability && capability < RCS_CAPABILITY_MAX)) {
+            throw new IllegalArgumentException("Rcs capability '" + capability + "' is invalid");
+        }
+
+        // check valid radio tech
+        if (!(REGISTRATION_TECH_NONE < tech && tech < REGISTRATION_TECH_MAX)) {
+            log("Rcs not matched radio tech " + tech);
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+
+        boolean retVal = isProvisioningRequired(subId, capability, tech, /*isMmTel*/false);
+
+        log("isRcsProvisioningRequiredForCapability capability " + capability
+                + " tech " + tech + " return value " + retVal);
+
+        return retVal;
+    }
+
+    /**
+     * return the provisioning status for MmTel capability in specific radio tech
+     */
+    @VisibleForTesting
+    public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
+        boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
+        if (!mmTelProvisioned) { // provisioning not required
+            log("getImsProvisioningStatusForCapability : not required "
+                    + " capability " + capability + " tech " + tech);
+            return true;
+        }
+
+        // read value from ImsProvisioningLoader
+        int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
+                capability, tech);
+        if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
+            // not set means initial value
+            // read data from vendor ImsService and store that in ImsProvisioningLoader
+            result = getValueFromImsService(subId, capability, tech);
+            mmTelProvisioned = getBoolValue(result);
+            if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
+                setAndNotifyMmTelProvisioningValue(subId, capability, tech, mmTelProvisioned);
+            }
+        } else {
+            mmTelProvisioned = getBoolValue(result);
+        }
+
+        log("getImsProvisioningStatusForCapability : "
+                + " capability " + capability
+                + " tech " + tech
+                + " result " + mmTelProvisioned);
+        return mmTelProvisioned;
+    }
+
+    /**
+     * set MmTel provisioning status in specific tech
+     */
+    @VisibleForTesting
+    public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        boolean mmTelProvisioned = isImsProvisioningRequiredForCapability(subId, capability, tech);
+        if (!mmTelProvisioned) { // provisioning not required
+            log("setImsProvisioningStatusForCapability : not required "
+                    + " capability " + capability + " tech " + tech);
+            return;
+        }
+
+        // write value to ImsProvisioningLoader
+        boolean isChanged = setAndNotifyMmTelProvisioningValue(subId, capability, tech,
+                isProvisioned);
+        if (!isChanged) {
+            log("status not changed mmtel capability " + capability + " tech " + tech);
+            return;
+        }
+
+        int slotId = getSlotId(subId);
+        // find matched key from capability and tech
+        int value = getIntValue(isProvisioned);
+        int key = getKeyFromCapability(capability, tech);
+        if (key != INVALID_VALUE) {
+            log("setImsProvisioningStatusForCapability : matched key " + key);
+            try {
+                // set key and value to vendor ImsService for MmTel
+                mMmTelFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
+            } catch (NullPointerException e) {
+                loge("can not access MmTelFeatureListener with capability " + capability);
+            }
+        }
+    }
+
+    /**
+     * return the provisioning status for RCS capability in specific radio tech
+     */
+    @VisibleForTesting
+    public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
+        boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
+        if (!rcsProvisioned) { // provisioning not required
+            log("getRcsProvisioningStatusForCapability : not required"
+                    + " capability " + capability + " tech " + tech);
+            return true;
+        }
+
+        // read data from ImsProvisioningLoader
+        int result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
+                capability, tech);
+        if (result == ImsProvisioningLoader.STATUS_NOT_SET) {
+            // not set means initial value
+            // read data from vendor ImsService and store that in ImsProvisioningLoader
+            result = getRcsValueFromImsService(subId, capability);
+            rcsProvisioned = getBoolValue(result);
+            if (result != ProvisioningManager.PROVISIONING_RESULT_UNKNOWN) {
+                setAndNotifyRcsProvisioningValueForAllTech(subId, capability, rcsProvisioned);
+            }
+        } else {
+            rcsProvisioned = getBoolValue(result);
+        }
+
+        log("getRcsProvisioningStatusForCapability : "
+                + " capability " + capability
+                + " tech " + tech
+                + " result " + rcsProvisioned);
+        return rcsProvisioned;
+    }
+
+    /**
+     * set RCS provisioning status in specific tech
+     */
+    @VisibleForTesting
+    public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        boolean rcsProvisioned = isRcsProvisioningRequiredForCapability(subId, capability, tech);
+        if (!rcsProvisioned) { // provisioning not required
+            log("set rcs provisioning status but not required");
+            return;
+        }
+
+        // write status using ImsProvisioningLoader
+        boolean isChanged = setAndNotifyRcsProvisioningValue(subId, capability, tech,
+                isProvisioned);
+        if (!isChanged) {
+            log("status not changed rcs capability " + capability + " tech " + tech);
+            return;
+        }
+
+        int slotId = getSlotId(subId);
+        int key =  ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+        int value = getIntValue(isProvisioned);
+        try {
+            // set key and value to vendor ImsService for Rcs
+            mRcsFeatureListenersSlotMap.get(slotId).setProvisioningValue(key, value);
+        } catch (NullPointerException e) {
+            loge("can not access RcsFeatureListener with capability " + capability);
+        }
+    }
+
+    /**
+     * set RCS provisioning status in specific key and value
+     * @param key integer key, defined as one of
+     * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
+     * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
+     * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
+     * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
+     * @param value in Integer format.
+     * @return the result of setting the configuration value, defined as one of
+     * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
+     * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
+     */
+    @VisibleForTesting
+    public int setProvisioningValue(int subId, int key, int value) {
+        log("setProvisioningValue");
+
+        int retVal = ImsConfigImplBase.CONFIG_RESULT_FAILED;
+        // check key value
+        if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
+            log("not matched key " + key);
+            return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+        }
+
+        // check subId
+        int slotId = getSlotId(subId);
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
+            loge("Fail to retrieve slotId from subId");
+            return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+        }
+
+        try {
+            if (key == KEY_EAB_PROVISIONING_STATUS) {
+                // set key and value to vendor ImsService for Rcs
+                retVal = mRcsFeatureListenersSlotMap.get(slotId)
+                        .setProvisioningValue(key, value);
+            } else {
+                // set key and value to vendor ImsService for MmTel
+                retVal = mMmTelFeatureListenersSlotMap.get(slotId)
+                        .setProvisioningValue(key, value);
+            }
+        } catch (NullPointerException e) {
+            loge("can not access FeatureListener to set provisioning value");
+            return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+        }
+
+        // update and notify provisioning status changed capability and tech from key
+        updateCapabilityTechFromKey(subId, key, value);
+
+        return retVal;
+    }
+
+    /**
+     * get RCS provisioning status in specific key and value
+     * @param key integer key, defined as one of
+     * {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS}
+     * {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS}
+     * {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}
+     * {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}
+     * @return the result of setting the configuration value, defined as one of
+     * {@link ImsConfigImplBase#CONFIG_RESULT_FAILED} or
+     * {@link ImsConfigImplBase#CONFIG_RESULT_SUCCESS} or
+     * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN}
+     */
+    @VisibleForTesting
+    public int getProvisioningValue(int subId, int key) {
+        // check key value
+        if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == key)) {
+            log("not matched key " + key);
+            return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+        }
+
+        // check subId
+        int slotId = getSlotId(subId);
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
+            loge("Fail to retrieve slotId from subId");
+            return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+        }
+
+        // check data from ImsProvisioningLoader
+        int capability = getCapabilityFromKey(key);
+        int tech = getTechFromKey(key);
+        int result;
+        if (capability != INVALID_VALUE && tech != INVALID_VALUE) {
+            if (key == KEY_EAB_PROVISIONING_STATUS) {
+                result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_RCS,
+                        capability, tech);
+            } else {
+                result = mImsProvisioningLoader.getProvisioningStatus(subId, FEATURE_MMTEL,
+                        capability, tech);
+            }
+            if (result != ImsProvisioningLoader.STATUS_NOT_SET) {
+                log("getProvisioningValue from loader : key " + key + " result " + result);
+                return result;
+            }
+        }
+
+        // get data from ImsService, update it in ImsProvisioningLoader
+        if (key == KEY_EAB_PROVISIONING_STATUS) {
+            result = getRcsValueFromImsService(subId, capability);
+            if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
+                logw("getProvisioningValue : fail to get data from ImsService capability"
+                        + capability);
+                return result;
+            }
+            log("getProvisioningValue from vendor : key " + key + " result " + result);
+
+            setAndNotifyRcsProvisioningValueForAllTech(subId, capability, getBoolValue(result));
+            return result;
+        } else {
+            result = getValueFromImsService(subId, capability, tech);
+            if (result == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
+                logw("getProvisioningValue : fail to get data from ImsService capability"
+                        + capability);
+                return result;
+            }
+            log("getProvisioningValue from vendor : key " + key + " result " + result);
+
+            setAndNotifyMmTelProvisioningValue(subId, capability, tech, getBoolValue(result));
+            return result;
+        }
+    }
+
+    /**
+     * get the handler
+     */
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    private boolean isProvisioningRequired(int subId, int capability, int tech, boolean isMmTel) {
+        int[] techArray;
+        techArray = getTechsFromCarrierConfig(subId, capability, isMmTel);
+        if (techArray == null) {
+            logw("isProvisioningRequired : getTechsFromCarrierConfig failed");
+            // not exist in CarrierConfig that means provisioning is not required
+            return false;
+        }
+
+        // compare with carrier config
+        if (Arrays.stream(techArray).anyMatch(keyValue -> keyValue == tech)) {
+            // existing same tech means provisioning required
+            return true;
+        }
+
+        log("isProvisioningRequired : not matched capability " + capability + " tech " + tech);
+        return false;
+    }
+
+    @VisibleForTesting
+    protected int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmTel) {
+        String featureKey;
+        String capabilityKey;
+        if (isMmTel) {
+            featureKey = CarrierConfigManager.Ims.KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE;
+            capabilityKey = KEYS_MMTEL_CAPABILITY.get(capability);
+        } else {
+            featureKey = CarrierConfigManager.Ims.KEY_RCS_REQUIRES_PROVISIONING_BUNDLE;
+            capabilityKey = KEYS_RCS_CAPABILITY.get(capability);
+        }
+
+        if (capabilityKey != null) {
+            PersistableBundle imsCarrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+            if (imsCarrierConfigs == null) {
+                log("getTechsFromCarrierConfig : imsCarrierConfigs null");
+                return null;
+            }
+
+            PersistableBundle provisioningBundle =
+                    imsCarrierConfigs.getPersistableBundle(featureKey);
+            if (provisioningBundle == null) {
+                log("getTechsFromCarrierConfig : provisioningBundle null");
+                return null;
+            }
+
+            return provisioningBundle.getIntArray(capabilityKey);
+        }
+
+        return null;
+    }
+
+    private int getValueFromImsService(int subId, int capability, int tech) {
+        int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+
+        // operation is based on capability
+        switch (capability) {
+            case CAPABILITY_TYPE_VOICE:
+                int item = (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
+                        ? ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
+                        : ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
+                // read data from vendor ImsService
+                config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
+                        .getProvisioningValue(item);
+                break;
+            case CAPABILITY_TYPE_VIDEO:
+                // read data from vendor ImsService
+                config = mMmTelFeatureListenersSlotMap.get(getSlotId(subId))
+                        .getProvisioningValue(ProvisioningManager.KEY_VT_PROVISIONING_STATUS);
+                break;
+            default:
+                log("Capability " + capability + " has been provisioning");
+                break;
+        }
+
+        return config;
+    }
+
+    private int getRcsValueFromImsService(int subId, int capability) {
+        int config = ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+
+        if (capability == CAPABILITY_TYPE_PRESENCE_UCE) {
+            try {
+                config = mRcsFeatureListenersSlotMap.get(getSlotId(subId))
+                        .getProvisioningValue(ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
+            } catch (NullPointerException e) {
+                logw("can not access RcsFeatureListener");
+            }
+        } else {
+            log("Capability " + capability + " has been provisioning");
+        }
+
+        return config;
+    }
+
+    private void onSubscriptionsChanged() {
+        for (int index = 0; index < mMmTelFeatureListenersSlotMap.size(); index++) {
+            MmTelFeatureListener m = mMmTelFeatureListenersSlotMap.get(index);
+            m.setSubId(getSubId(index));
+        }
+        for (int index = 0; index < mRcsFeatureListenersSlotMap.size(); index++) {
+            RcsFeatureListener r = mRcsFeatureListenersSlotMap.get(index);
+            r.setSubId(getSubId(index));
+        }
+        for (int index = 0; index < mProvisioningCallbackManagersSlotMap.size(); index++) {
+            ProvisioningCallbackManager m = mProvisioningCallbackManagersSlotMap.get(index);
+            m.setSubId(getSubId(index));
+        }
+    }
+
+    private void  updateCapabilityTechFromKey(int subId, int key, int value) {
+        boolean isProvisioned = getBoolValue(value);
+        int capability = getCapabilityFromKey(key);
+        int tech = getTechFromKey(key);
+
+        if (capability == INVALID_VALUE || tech == INVALID_VALUE) {
+            logw("updateCapabilityTechFromKey : unknown key " + key);
+            return;
+        }
+
+        if (key == KEY_VOLTE_PROVISIONING_STATUS
+                || key == KEY_VT_PROVISIONING_STATUS
+                || key == KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE) {
+            setAndNotifyMmTelProvisioningValue(subId, capability, tech, isProvisioned);
+        }
+        if (key == KEY_EAB_PROVISIONING_STATUS) {
+            setAndNotifyRcsProvisioningValueForAllTech(subId, capability, isProvisioned);
+        }
+    }
+
+    private int getCapabilityFromKey(int key) {
+        int capability;
+        switch (key) {
+            case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
+                // intentional fallthrough
+            case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
+                capability = CAPABILITY_TYPE_VOICE;
+                break;
+            case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
+                capability = CAPABILITY_TYPE_VIDEO;
+                break;
+            case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
+                // default CAPABILITY_TYPE_PRESENCE_UCE used for KEY_EAB_PROVISIONING_STATUS
+                capability = CAPABILITY_TYPE_PRESENCE_UCE;
+                break;
+            default:
+                capability = INVALID_VALUE;
+                break;
+        }
+        return capability;
+    }
+
+    private int getTechFromKey(int key) {
+        int tech;
+        switch (key) {
+            case ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE:
+                tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+                break;
+            case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
+                // intentional fallthrough
+            case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
+                // intentional fallthrough
+            case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
+                tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+                break;
+            default:
+                tech = INVALID_VALUE;
+                break;
+        }
+        return tech;
+    }
+
+    private int getKeyFromCapability(int capability, int tech) {
+        int key = INVALID_VALUE;
+        if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_IWLAN) {
+            key = ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+        } else if (capability == CAPABILITY_TYPE_VOICE && tech == REGISTRATION_TECH_LTE) {
+            key = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
+        } else if (capability == CAPABILITY_TYPE_VIDEO && tech == REGISTRATION_TECH_LTE) {
+            key = ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+        }
+
+        return key;
+    }
+
+    protected int getSubId(int slotId) {
+        final int[] subIds = mSubscriptionManager.getSubscriptionIds(slotId);
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        if (subIds != null && subIds.length >= 1) {
+            subId = subIds[0];
+        }
+
+        return subId;
+    }
+
+    protected int getSlotId(int subId) {
+        return mSubscriptionManager.getPhoneId(subId);
+    }
+
+    protected ImsConfig getImsConfig(ImsManager imsManager) throws ImsException {
+        return imsManager.getConfigInterface();
+    }
+
+    protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
+        return new ImsConfig(iImsConfig);
+    }
+
+    private int getIntValue(boolean isProvisioned) {
+        return isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
+                : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
+    }
+
+    private boolean getBoolValue(int value) {
+        return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED ? true : false;
+    }
+
+    private boolean setAndNotifyMmTelProvisioningValue(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        boolean changed = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_MMTEL,
+                capability, tech, isProvisioned);
+        // notify MmTel capability changed
+        if (changed) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
+                    getSlotId(subId), 0, (Object) new FeatureProvisioningData(
+                            capability, tech, isProvisioned, /*isMmTel*/true)));
+        }
+
+        return changed;
+    }
+
+    private boolean setAndNotifyRcsProvisioningValue(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        boolean isChanged = mImsProvisioningLoader.setProvisioningStatus(subId, FEATURE_RCS,
+                capability, tech, isProvisioned);
+
+        if (isChanged) {
+            int slotId = getSlotId(subId);
+
+            // notify RCS capability changed
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_PROVISIONING_CAPABILITY_CHANGED,
+                    slotId, 0, (Object) new FeatureProvisioningData(
+                            capability, tech, isProvisioned, /*isMmtel*/false)));
+        }
+
+        return isChanged;
+    }
+
+    private boolean setAndNotifyRcsProvisioningValueForAllTech(int subId, int capability,
+            boolean isProvisioned) {
+        boolean isChanged = false;
+
+        for (int tech : LOCAL_RADIO_TECHS) {
+            isChanged |= setAndNotifyRcsProvisioningValue(subId, capability, tech, isProvisioned);
+        }
+
+        return isChanged;
+    }
+
+    protected boolean isValidSubId(int subId) {
+        int slotId = getSlotId(subId);
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlot) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void log(String prefix, int slotId, String s) {
+        Rlog.d(TAG, prefix + "[" + slotId + "] " + s);
+    }
+
+    private void logi(String prefix, int slotId, String s) {
+        Rlog.i(TAG, prefix + "[" + slotId + "] " + s);
+    }
+
+    private void logw(String s) {
+        Rlog.w(TAG, s);
+    }
+
+    private void logw(String prefix, int slotId, String s) {
+        Rlog.w(TAG, prefix + "[" + slotId + "] " + s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+
+    private void loge(String prefix, int slotId, String s) {
+        Rlog.e(TAG, prefix + "[" + slotId + "] " + s);
+    }
+}
diff --git a/src/com/android/phone/ImsProvisioningLoader.java b/src/com/android/phone/ImsProvisioningLoader.java
new file mode 100644
index 0000000..1238b9a
--- /dev/null
+++ b/src/com/android/phone/ImsProvisioningLoader.java
@@ -0,0 +1,311 @@
+/*
+ * 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 android.content.Context;
+import android.content.SharedPreferences;
+import android.os.PersistableBundle;
+import android.preference.PreferenceManager;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Provides a function to set/get Ims feature provisioning status in storage.
+ */
+public class ImsProvisioningLoader {
+    private static final String LOG_TAG = ImsProvisioningLoader.class.getSimpleName();
+
+    public static final int STATUS_NOT_SET = -1;
+    public static final int STATUS_NOT_PROVISIONED =
+            ProvisioningManager.PROVISIONING_VALUE_DISABLED;
+    public static final int STATUS_PROVISIONED =
+            ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+
+    public static final int IMS_FEATURE_MMTEL = ImsFeature.FEATURE_MMTEL;
+    public static final int IMS_FEATURE_RCS = ImsFeature.FEATURE_RCS;
+
+    private static final String PROVISIONING_FILE_NAME_PREF = "imsprovisioningstatus_";
+    private static final String PREF_PROVISION_IMS_MMTEL_PREFIX = "provision_ims_mmtel_";
+
+    private Context mContext;
+    private SharedPreferences mTelephonySharedPreferences;
+    // key : sub Id, value : read from sub Id's xml and it's in-memory cache
+    private SparseArray<PersistableBundle> mSubIdBundleArray = new SparseArray<>();
+    private final Object mLock = new Object();
+
+    public ImsProvisioningLoader(Context context) {
+        mContext = context;
+        mTelephonySharedPreferences =
+                PreferenceManager.getDefaultSharedPreferences(context);
+    }
+
+    /**
+     * Get Ims feature provisioned status in storage
+     */
+    public int getProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature,
+            int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        initCache(subId);
+        return getImsProvisioningStatus(subId, imsFeature, tech,
+                capability);
+    }
+
+    /**
+     * Set Ims feature provisioned status in storage
+     */
+    public boolean setProvisioningStatus(int subId, @ImsFeature.FeatureType int imsFeature,
+            int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+            boolean isProvisioned) {
+        initCache(subId);
+        return setImsFeatureProvisioning(subId, imsFeature, tech, capability,
+                isProvisioned);
+    }
+
+    private boolean isFileExist(int subId) {
+        File file = new File(mContext.getFilesDir(), getFileName(subId));
+        return file.exists();
+    }
+
+    private void initCache(int subId) {
+        synchronized (mLock) {
+            PersistableBundle subIdBundle = mSubIdBundleArray.get(subId, null);
+            if (subIdBundle != null) {
+                // initCache() has already been called for the subId
+                return;
+            }
+            if (isFileExist(subId)) {
+                subIdBundle = readSubIdBundleFromXml(subId);
+            } else {
+                // It should read the MMTEL capability cache as part of shared prefs and migrate
+                // over any configs for UT.
+                final int[] regTech = {ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_NR};
+                subIdBundle = new PersistableBundle();
+                for (int tech : regTech) {
+                    int UtProvisioningStatus = getUTProvisioningStatus(subId, tech);
+                    logd("check UT provisioning status " + UtProvisioningStatus);
+
+                    if (STATUS_PROVISIONED == UtProvisioningStatus) {
+                        setProvisioningStatusToSubIdBundle(ImsFeature.FEATURE_MMTEL, tech,
+                                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, subIdBundle,
+                                UtProvisioningStatus);
+                    }
+                }
+                saveSubIdBundleToXml(subId, subIdBundle);
+            }
+            mSubIdBundleArray.put(subId, subIdBundle);
+        }
+    }
+
+    private int getImsProvisioningStatus(int subId, int imsFeature, int tech, int capability) {
+        PersistableBundle subIdBundle = null;
+        synchronized (mLock) {
+            subIdBundle = mSubIdBundleArray.get(subId, null);
+        }
+
+        return getProvisioningStatusFromSubIdBundle(imsFeature, tech,
+                capability, subIdBundle);
+    }
+
+    private boolean setImsFeatureProvisioning(int subId, int imsFeature, int tech, int capability,
+            boolean isProvisioned) {
+        synchronized (mLock) {
+            int preValue = getImsProvisioningStatus(subId, imsFeature, tech, capability);
+            int newValue = isProvisioned ? STATUS_PROVISIONED : STATUS_NOT_PROVISIONED;
+            if (preValue == newValue) {
+                logd("already stored provisioning status " + isProvisioned + " ImsFeature "
+                        + imsFeature + " tech " + tech + " capa " + capability);
+                return false;
+            }
+
+            PersistableBundle subIdBundle = mSubIdBundleArray.get(subId, null);
+            setProvisioningStatusToSubIdBundle(imsFeature, tech, capability, subIdBundle,
+                    newValue);
+            saveSubIdBundleToXml(subId, subIdBundle);
+        }
+        return true;
+    }
+
+    private int getProvisioningStatusFromSubIdBundle(int imsFeature, int tech,
+            int capability, PersistableBundle subIdBundle) {
+        // If it doesn't exist in xml, return STATUS_NOT_SET
+        if (subIdBundle == null || subIdBundle.isEmpty()) {
+            logd("xml is empty");
+            return STATUS_NOT_SET;
+        }
+
+        PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
+                String.valueOf(imsFeature));
+        if (regTechBundle == null) {
+            logd("ImsFeature " + imsFeature + " is not exist in xml");
+            return STATUS_NOT_SET;
+        }
+
+        PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
+                String.valueOf(tech));
+        if (capabilityBundle == null) {
+            logd("RegistrationTech " + tech + " is not exist in xml");
+            return STATUS_NOT_SET;
+        }
+
+        return getIntValueFromBundle(String.valueOf(capability), capabilityBundle);
+    }
+
+    private void setProvisioningStatusToSubIdBundle(int imsFeature, int tech,
+            int capability, PersistableBundle subIdBundle, int newStatus) {
+        logd("set provisioning status " + newStatus + " ImsFeature "
+                + imsFeature + " tech " + tech + " capa " + capability);
+
+        PersistableBundle regTechBundle = subIdBundle.getPersistableBundle(
+                String.valueOf(imsFeature));
+        if (regTechBundle == null) {
+            regTechBundle = new PersistableBundle();
+            subIdBundle.putPersistableBundle(String.valueOf(imsFeature), regTechBundle);
+        }
+
+        PersistableBundle capabilityBundle = regTechBundle.getPersistableBundle(
+                String.valueOf(tech));
+        if (capabilityBundle == null) {
+            capabilityBundle = new PersistableBundle();
+            regTechBundle.putPersistableBundle(String.valueOf(tech), capabilityBundle);
+        }
+
+        capabilityBundle.putInt(String.valueOf(capability), newStatus);
+    }
+
+    // Default value is STATUS_NOT_SET
+    private int getIntValueFromBundle(String key, PersistableBundle bundle) {
+        int value = bundle.getInt(key, STATUS_NOT_SET);
+        logd("get value " + value);
+        return value;
+    }
+
+    // Return subIdBundle from imsprovisioningstatus_{subId}.xml
+    private PersistableBundle readSubIdBundleFromXml(int subId) {
+        String fileName = getFileName(subId);
+
+        PersistableBundle subIdBundles = new PersistableBundle();
+        File file = null;
+        FileInputStream inFile = null;
+        synchronized (mLock) {
+            try {
+                file = new File(mContext.getFilesDir(), fileName);
+                inFile = new FileInputStream(file);
+                subIdBundles = PersistableBundle.readFromStream(inFile);
+                inFile.close();
+            } catch (FileNotFoundException e) {
+                logd(e.toString());
+            } catch (IOException e) {
+                loge(e.toString());
+            } catch (RuntimeException e) {
+                loge(e.toString());
+            }
+        }
+
+        return subIdBundles;
+    }
+
+    private void saveSubIdBundleToXml(int subId, PersistableBundle subIdBundle) {
+        String fileName = getFileName(subId);
+
+        if (subIdBundle == null || subIdBundle.isEmpty()) {
+            logd("subIdBundle is empty");
+            return;
+        }
+
+        FileOutputStream outFile = null;
+        synchronized (mLock) {
+            try {
+                outFile = new FileOutputStream(new File(mContext.getFilesDir(), fileName));
+                subIdBundle.writeToStream(outFile);
+                outFile.flush();
+                outFile.close();
+            } catch (IOException e) {
+                loge(e.toString());
+            } catch (RuntimeException e) {
+                loge(e.toString());
+            }
+        }
+    }
+
+    private int getUTProvisioningStatus(int subId, int tech) {
+        return getMmTelCapabilityProvisioningBitfield(subId, tech) > 0 ? STATUS_PROVISIONED
+                : STATUS_NOT_SET;
+    }
+
+    /**
+     * @return the bitfield containing the MmTel provisioning for the provided subscription and
+     * technology. The bitfield should mirror the bitfield defined by
+     * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     */
+    private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) {
+        String key = getMmTelProvisioningKey(subId, tech);
+        // Default is no capabilities are provisioned.
+        return mTelephonySharedPreferences.getInt(key, 0 /*default*/);
+    }
+
+    private String getMmTelProvisioningKey(int subId, int tech) {
+        // Resulting key is provision_ims_mmtel_{subId}_{tech}
+        return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech;
+    }
+
+    private String getFileName(int subId) {
+        // Resulting name is imsprovisioningstatus_{subId}.xml
+        return PROVISIONING_FILE_NAME_PREF + subId + ".xml";
+    }
+
+    @VisibleForTesting
+    void clear() {
+        synchronized (mLock) {
+            mSubIdBundleArray.clear();
+        }
+    }
+
+    @VisibleForTesting
+    void setProvisioningToXml(int subId, PersistableBundle subIdBundle,
+            String[] infoArray) {
+        for (String info : infoArray) {
+            String[] paramArray = info.split(",");
+            setProvisioningStatusToSubIdBundle(Integer.valueOf(paramArray[0]),
+                    Integer.valueOf(paramArray[1]), Integer.valueOf(paramArray[2]),
+                    subIdBundle, Integer.valueOf(paramArray[3]));
+        }
+        saveSubIdBundleToXml(subId, subIdBundle);
+    }
+
+    private void loge(String contents) {
+        Log.e(LOG_TAG, contents);
+    }
+
+    private void logd(String contents) {
+        Log.d(LOG_TAG, contents);
+    }
+
+}
diff --git a/src/com/android/phone/ImsStateCallbackController.java b/src/com/android/phone/ImsStateCallbackController.java
index 28fca59..4e2407c 100644
--- a/src/com/android/phone/ImsStateCallbackController.java
+++ b/src/com/android/phone/ImsStateCallbackController.java
@@ -292,7 +292,7 @@
         }
 
         @Override
-        public void connectionReady(ImsManager manager) {
+        public void connectionReady(ImsManager manager, int subId) {
             logd(mLogPrefix + "connectionReady");
 
             mState = STATE_READY;
@@ -434,7 +434,7 @@
         }
 
         @Override
-        public void connectionReady(RcsFeatureManager manager) {
+        public void connectionReady(RcsFeatureManager manager, int subId) {
             logd(mLogPrefix + "connectionReady");
 
             mState = STATE_READY;
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index e7cb28c..a4f405b 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -73,7 +73,6 @@
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
-import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccPort;
 import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.util.IndentingPrintWriter;
@@ -161,6 +160,7 @@
     public PhoneInterfaceManager phoneMgr;
     public ImsRcsController imsRcsController;
     public ImsStateCallbackController mImsStateCallbackController;
+    public ImsProvisioningController mImsProvisioningController;
     CarrierConfigLoader configLoader;
 
     private Phone phoneInEcm;
@@ -470,6 +470,8 @@
                         PhoneFactory.getPhones().length);
                 mTelephonyRcsService.initialize();
                 imsRcsController.setRcsService(mTelephonyRcsService);
+                mImsProvisioningController =
+                        ImsProvisioningController.make(this, PhoneFactory.getPhones().length);
             }
 
             configLoader = CarrierConfigLoader.init(this);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index a807b11..402e7f8 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -22,6 +22,7 @@
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_IMS;
 import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -122,6 +123,7 @@
 import android.telephony.ims.RcsClientConfiguration;
 import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -186,6 +188,7 @@
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccIoResult;
@@ -2799,8 +2802,13 @@
             int subId = mSubscriptionController.getDefaultDataSubId();
             final Phone phone = getPhone(subId);
             if (phone != null) {
-                phone.getDataEnabledSettings().setDataEnabled(
-                        TelephonyManager.DATA_ENABLED_REASON_USER, true);
+                if (phone.isUsingNewDataStack()) {
+                    phone.getDataSettingsManager().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, true);
+                } else {
+                    phone.getDataEnabledSettings().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, true);
+                }
                 return true;
             } else {
                 return false;
@@ -2820,8 +2828,13 @@
             int subId = mSubscriptionController.getDefaultDataSubId();
             final Phone phone = getPhone(subId);
             if (phone != null) {
-                phone.getDataEnabledSettings().setDataEnabled(
-                        TelephonyManager.DATA_ENABLED_REASON_USER, false);
+                if (phone.isUsingNewDataStack()) {
+                    phone.getDataSettingsManager().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, false);
+                } else {
+                    phone.getDataEnabledSettings().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, false);
+                }
                 return true;
             } else {
                 return false;
@@ -2937,6 +2950,9 @@
         try {
             final Phone phone = getPhone(subId);
             if (phone != null) {
+                if (phone.isUsingNewDataStack()) {
+                    return phone.getDataNetworkController().getInternetDataNetworkState();
+                }
                 return PhoneConstantConversions.convertDataState(phone.getDataConnectionState());
             } else {
                 return PhoneConstantConversions.convertDataState(
@@ -4523,6 +4539,7 @@
     @Override
     public void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) {
         enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isImsAvailableOnDevice()) {
@@ -4543,6 +4560,7 @@
     @Override
     public void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) {
         enforceReadPrivilegedPermission("unregisterImsProvisioningChangedCallback");
+
         final long identity = Binder.clearCallingIdentity();
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -4560,6 +4578,39 @@
         }
     }
 
+    @Override
+    public void registerFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "registerFeatureProvisioningChangedCallback");
+
+        final long identity = Binder.clearCallingIdentity();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
+        }
+
+        ImsProvisioningController.getInstance()
+                .addFeatureProvisioningChangedCallback(subId, callback);
+
+        Binder.restoreCallingIdentity(identity);
+    }
+
+    @Override
+    public void unregisterFeatureProvisioningChangedCallback(int subId,
+            IFeatureProvisioningCallback callback) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "unregisterFeatureProvisioningChangedCallback");
+
+        final long identity = Binder.clearCallingIdentity();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
+        }
+
+        ImsProvisioningController.getInstance()
+                .removeFeatureProvisioningChangedCallback(subId, callback);
+
+        Binder.restoreCallingIdentity(identity);
+    }
 
     private void checkModifyPhoneStatePermission(int subId, String message) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
@@ -4586,60 +4637,30 @@
     }
 
     @Override
-    public void setRcsProvisioningStatusForCapability(int subId, int capability,
+    public void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            if (!isImsProvisioningRequired(subId, capability, false)) {
-                return;
-            }
-
-            // this capability requires provisioning, route to the correct API.
-            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
-            switch (capability) {
-                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
-                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
-                    ims.setEabProvisioned(isProvisioned);
-                    break;
-                default: {
-                    throw new IllegalArgumentException("Tried to set provisioning for "
-                            + "rcs capability '" + capability + "', which does not require "
-                            + "provisioning.");
-                }
-            }
+            ImsProvisioningController.getInstance()
+                    .setRcsProvisioningStatusForCapability(subId, capability, tech, isProvisioned);
+            return;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-
     }
 
 
     @Override
-    public boolean getRcsProvisioningStatusForCapability(int subId, int capability) {
-        enforceReadPrivilegedPermission("getRcsProvisioningStatusForCapability");
+    public boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            if (!isImsProvisioningRequired(subId, capability, false)) {
-                return true;
-            }
-
-            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
-            switch (capability) {
-                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
-                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
-                    return ims.isEabProvisionedOnDevice();
-
-                default: {
-                    throw new IllegalArgumentException("Tried to get rcs provisioning for "
-                            + "capability '" + capability + "', which does not require "
-                            + "provisioning.");
-                }
-            }
-
+            return ImsProvisioningController.getInstance()
+                    .getRcsProvisioningStatusForCapability(subId, capability, tech);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4648,66 +4669,12 @@
     @Override
     public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
             boolean isProvisioned) {
-        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
-        }
         checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            if (!isImsProvisioningRequired(subId, capability, true)) {
-                return;
-            }
-            if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR
-                    || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-                loge("setImsProvisioningStatusForCapability: called for technology that does "
-                        + "not support provisioning - " + tech);
-                return;
-            }
-
-            // this capability requires provisioning, route to the correct API.
-            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
-            switch (capability) {
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: {
-                    if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
-                        ims.setVolteProvisioned(isProvisioned);
-                    } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
-                        ims.setWfcProvisioned(isProvisioned);
-                    }
-                    break;
-                }
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
-                    // There is currently no difference in VT provisioning type.
-                    ims.setVtProvisioned(isProvisioned);
-                    break;
-                }
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
-                    // There is no "deprecated" UT provisioning mechanism through ImsConfig, so
-                    // change the capability of the feature instead if needed.
-                    if (isMmTelCapabilityProvisionedInCache(subId, capability, tech)
-                            == isProvisioned) {
-                        // No change in provisioning.
-                        return;
-                    }
-                    cacheMmTelCapabilityProvisioning(subId, capability, tech, isProvisioned);
-                    try {
-                        ims.changeMmTelCapability(isProvisioned, capability, tech);
-                    } catch (com.android.ims.ImsException e) {
-                        loge("setImsProvisioningStatusForCapability: couldn't change UT capability"
-                                + ", Exception" + e.getMessage());
-                    }
-                    break;
-                }
-                default: {
-                    throw new IllegalArgumentException("Tried to set provisioning for "
-                            + "MmTel capability '" + capability + "', which does not require "
-                            + "provisioning. ");
-                }
-            }
-
+            ImsProvisioningController.getInstance()
+                    .setImsProvisioningStatusForCapability(subId, capability, tech, isProvisioned);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4715,54 +4682,13 @@
 
     @Override
     public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
-        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
-        }
-        enforceReadPrivilegedPermission("getProvisioningStatusForCapability");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            if (!isImsProvisioningRequired(subId, capability, true)) {
-                return true;
-            }
-
-            if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR
-                    || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-                loge("getImsProvisioningStatusForCapability: called for technology that does "
-                        + "not support provisioning - " + tech);
-                return true;
-            }
-
-            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
-            switch (capability) {
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: {
-                    if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
-                        return ims.isVolteProvisionedOnDevice();
-                    } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
-                        return ims.isWfcProvisionedOnDevice();
-                    }
-                    // This should never happen, since we are checking tech above to make sure it
-                    // is either LTE or IWLAN.
-                    throw new IllegalArgumentException("Invalid radio technology for voice "
-                            + "capability.");
-                }
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
-                    // There is currently no difference in VT provisioning type.
-                    return ims.isVtProvisionedOnDevice();
-                }
-                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
-                    // There is no "deprecated" UT provisioning mechanism, so get from shared prefs.
-                    return isMmTelCapabilityProvisionedInCache(subId, capability, tech);
-                }
-                default: {
-                    throw new IllegalArgumentException(
-                            "Tried to get provisioning for MmTel capability '" + capability
-                                    + "', which does not require provisioning.");
-                }
-            }
+            return ImsProvisioningController.getInstance()
+                    .getImsProvisioningStatusForCapability(subId, capability, tech);
 
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -4770,61 +4696,31 @@
     }
 
     @Override
-    public boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech) {
-        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
-            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+    public boolean isProvisioningRequiredForCapability(int subId, int capability, int tech) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isProvisioningRequiredForCapability");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return ImsProvisioningController.getInstance()
+                    .isImsProvisioningRequiredForCapability(subId, capability, tech);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        enforceReadPrivilegedPermission("isMmTelCapabilityProvisionedInCache");
-        int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech);
-        return (provisionedBits & capability) > 0;
     }
 
     @Override
-    public void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
-            boolean isProvisioned) {
-        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
-            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
-        }
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
-                "setProvisioningStatusForCapability");
-        int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech);
-        // If the current provisioning status for capability already matches isProvisioned,
-        // do nothing.
-        if (((provisionedBits & capability) > 0) == isProvisioned) {
-            return;
-        }
-        if (isProvisioned) {
-            setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits | capability));
-        } else {
-            setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits & ~capability));
-        }
-    }
+    public boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isProvisioningRequiredForCapability");
 
-    /**
-     * @return the bitfield containing the MmTel provisioning for the provided subscription and
-     * technology. The bitfield should mirror the bitfield defined by
-     * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
-     */
-    private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) {
-        String key = getMmTelProvisioningKey(subId, tech);
-        // Default is no capabilities are provisioned.
-        return mTelephonySharedPreferences.getInt(key, 0 /*default*/);
-    }
-
-    /**
-     * Sets the MmTel capability provisioning bitfield (defined by
-     *     {@link MmTelFeature.MmTelCapabilities.MmTelCapability}) for the subscription and
-     *     technology specified.
-     *
-     * Note: This is a synchronous command and should not be called on UI thread.
-     */
-    private void setMmTelCapabilityProvisioningBitfield(int subId, int tech, int newField) {
-        final SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
-        String key = getMmTelProvisioningKey(subId, tech);
-        editor.putInt(key, newField);
-        editor.commit();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return ImsProvisioningController.getInstance()
+                    .isRcsProvisioningRequiredForCapability(subId, capability, tech);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private static String getMmTelProvisioningKey(int subId, int tech) {
@@ -4897,7 +4793,9 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
         }
-        enforceReadPrivilegedPermission("getImsProvisioningInt");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -4907,6 +4805,12 @@
                         + subId + "' for key:" + key);
                 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
             }
+
+            int retVal = ImsProvisioningController.getInstance().getProvisioningValue(subId, key);
+            if (retVal != ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
+                return retVal;
+            }
+
             return ImsManager.getInstance(mApp, slotId).getConfigInt(key);
         } catch (com.android.ims.ImsException e) {
             Log.w(LOG_TAG, "getImsProvisioningInt: ImsService is not available for subscription '"
@@ -4922,7 +4826,9 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
         }
-        enforceReadPrivilegedPermission("getImsProvisioningString");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getImsProvisioningString");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -4949,6 +4855,7 @@
         }
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -4958,6 +4865,13 @@
                         + subId + "' for key:" + key);
                 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
             }
+
+            int retVal = ImsProvisioningController.getInstance()
+                    .setProvisioningValue(subId, key, value);
+            if (retVal != ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
+                return retVal;
+            }
+
             return ImsManager.getInstance(mApp, slotId).setConfig(key, value);
         } catch (com.android.ims.ImsException | RemoteException e) {
             Log.w(LOG_TAG, "setImsProvisioningInt: ImsService unavailable for sub '" + subId
@@ -5538,11 +5452,9 @@
      */
     public int setForbiddenPlmns(int subId, int appType, List<String> fplmns, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
-                callingFeatureId, "setForbiddenPlmns")) {
-            if (DBG) logv("no permissions for setForbiddenplmns");
-            throw new IllegalStateException("No Permissions for setForbiddenPlmns");
-        }
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "setForbiddenPlmns");
+
         if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
             loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
             throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
@@ -6701,11 +6613,15 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             int phoneId = mSubscriptionController.getPhoneId(subId);
-            if (DBG) log("isDataEnabled: subId=" + subId + " phoneId=" + phoneId);
             Phone phone = PhoneFactory.getPhone(phoneId);
             if (phone != null) {
-                boolean retVal = phone.getDataEnabledSettings().isDataEnabled();
-                if (DBG) log("isDataEnabled: subId=" + subId + " retVal=" + retVal);
+                boolean retVal;
+                if (phone.isUsingNewDataStack()) {
+                    retVal = phone.getDataSettingsManager().isDataEnabled();
+                } else {
+                    retVal = phone.getDataEnabledSettings().isDataEnabled();
+                }
+                if (DBG) log("isDataEnabled: " + retVal + ", subId=" + subId);
                 return retVal;
             } else {
                 if (DBG) loge("isDataEnabled: no phone subId=" + subId + " retVal=false");
@@ -6756,10 +6672,14 @@
             Phone phone = PhoneFactory.getPhone(phoneId);
             if (phone != null) {
                 boolean retVal;
-                if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
-                    retVal = phone.isUserDataEnabled();
+                if (phone.isUsingNewDataStack()) {
+                    retVal = phone.getDataSettingsManager().isDataEnabledForReason(reason);
                 } else {
-                    retVal = phone.getDataEnabledSettings().isDataEnabledForReason(reason);
+                    if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
+                        retVal = phone.isUserDataEnabled();
+                    } else {
+                        retVal = phone.getDataEnabledSettings().isDataEnabledForReason(reason);
+                    }
                 }
                 if (DBG) log("isDataEnabledForReason: retVal=" + retVal);
                 return retVal;
@@ -8360,6 +8280,16 @@
             int result = (int) sendRequest(CMD_ENABLE_VONR, enabled, subId,
                     workSource);
             if (DBG) log("setVoNrEnabled result: " + result);
+
+            if (result == TelephonyManager.ENABLE_VONR_SUCCESS) {
+                if (DBG) {
+                    log("Set VoNR settings in siminfo db; subId=" + subId + ", value:" + enabled);
+                }
+                SubscriptionManager.setSubscriptionProperty(
+                        subId, SubscriptionManager.NR_ADVANCED_CALLING_ENABLED,
+                        (enabled ? "1" : "0"));
+            }
+
             return result;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -8489,7 +8419,11 @@
                 if (reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) {
                     phone.carrierActionSetMeteredApnsEnabled(enabled);
                 } else {
-                    phone.getDataEnabledSettings().setDataEnabled(reason, enabled);
+                    if (phone.isUsingNewDataStack()) {
+                        phone.getDataSettingsManager().setDataEnabled(reason, enabled);
+                    } else {
+                        phone.getDataEnabledSettings().setDataEnabled(reason, enabled);
+                    }
                 }
             }
         } finally {
@@ -9696,7 +9630,13 @@
             if (phone == null) return false;
 
             boolean isMetered = ApnSettingUtils.isMeteredApnType(apnType, phone);
-            return !isMetered || phone.getDataEnabledSettings().isDataEnabled(apnType);
+            boolean isDataEnabled;
+            if (phone.isUsingNewDataStack()) {
+                isDataEnabled = phone.getDataSettingsManager().isDataEnabled(apnType);
+            } else {
+                isDataEnabled = phone.getDataEnabledSettings().isDataEnabled(apnType);
+            }
+            return !isMetered || isDataEnabled;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -9850,9 +9790,17 @@
 
             switch (policy) {
                 case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL:
-                    return phone.getDataEnabledSettings().isDataAllowedInVoiceCall();
+                    if (phone.isUsingNewDataStack()) {
+                        return phone.getDataSettingsManager().isDataAllowedInVoiceCall();
+                    } else {
+                        return phone.getDataEnabledSettings().isDataAllowedInVoiceCall();
+                    }
                 case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED:
-                    return phone.getDataEnabledSettings().isMmsAlwaysAllowed();
+                    if (phone.isUsingNewDataStack()) {
+                        return phone.getDataSettingsManager().isMmsAlwaysAllowed();
+                    } else {
+                        return phone.getDataEnabledSettings().isMmsAlwaysAllowed();
+                    }
                 default:
                     throw new IllegalArgumentException(policy + " is not a valid policy");
             }
@@ -9873,10 +9821,18 @@
 
             switch (policy) {
                 case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL:
-                    phone.getDataEnabledSettings().setAllowDataDuringVoiceCall(enabled);
+                    if (phone.isUsingNewDataStack()) {
+                        phone.getDataSettingsManager().setAllowDataDuringVoiceCall(enabled);
+                    } else {
+                        phone.getDataEnabledSettings().setAllowDataDuringVoiceCall(enabled);
+                    }
                     break;
                 case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED:
-                    phone.getDataEnabledSettings().setAlwaysAllowMmsData(enabled);
+                    if (phone.isUsingNewDataStack()) {
+                        phone.getDataSettingsManager().setAlwaysAllowMmsData(enabled);
+                    } else {
+                        phone.getDataEnabledSettings().setAlwaysAllowMmsData(enabled);
+                    }
                     break;
                 default:
                     throw new IllegalArgumentException(policy + " is not a valid policy");
@@ -10592,6 +10548,9 @@
             } else {
                 configBinder.setRcsClientConfiguration(rcc);
             }
+
+            RcsStats.getInstance().onRcsClientProvisioningStats(subId,
+                    RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT);
         } catch (RemoteException e) {
             Rlog.e(LOG_TAG, "fail to setRcsClientConfiguration " + e.getMessage());
             throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
@@ -11115,7 +11074,9 @@
      */
     @Override
     public void getSlicingConfig(ResultReceiver callback) {
-        enforceReadPrivilegedPermission("getSlicingConfig");
+        TelephonyPermissions
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                        mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID, "getSlicingConfig");
 
         final long identity = Binder.clearCallingIdentity();
         try {
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index 6d2bd6f..1ed0d72 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -16,6 +16,10 @@
 
 package com.android.phone;
 
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
+
 import android.Manifest;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
@@ -47,6 +51,8 @@
 import com.android.ims.FeatureUpdates;
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.RcsStats;
+import com.android.internal.telephony.metrics.RcsStats.RcsProvisioningCallback;
 import com.android.internal.telephony.util.HandlerExecutor;
 import com.android.internal.util.CollectionUtils;
 import com.android.telephony.Rlog;
@@ -100,6 +106,8 @@
     private final RoleManagerAdapter mRoleManager;
     private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
 
+    private RcsStats mRcsStats;
+
     private static RcsProvisioningMonitor sInstance;
 
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
@@ -227,6 +235,10 @@
             if (mSingleRegistrationCapability != singleRegistrationCapability) {
                 mSingleRegistrationCapability = singleRegistrationCapability;
                 notifyDma();
+
+                // update whether single registration supported.
+                mRcsStats.setEnableSingleRegistration(mSubId,
+                        mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
             }
         }
 
@@ -338,6 +350,9 @@
                     } else {
                         notifyRcsAutoConfigurationReceived();
                     }
+
+                    // check callback for metrics if not registered, register callback
+                    registerMetricsCallback();
                 } else {
                     // clear callbacks if rcs disconnected
                     clearCallbacks();
@@ -397,6 +412,18 @@
                 }
             }
         }
+
+        private void registerMetricsCallback() {
+            RcsProvisioningCallback rcsProvisioningCallback = mRcsStats.getRcsProvisioningCallback(
+                    mSubId, mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
+
+            // if not yet registered, register callback and set registered value
+            if (rcsProvisioningCallback != null && !rcsProvisioningCallback.getRegistered()) {
+                if (addRcsConfigCallback(rcsProvisioningCallback)) {
+                    rcsProvisioningCallback.setRegistered(true);
+                }
+            }
+        }
     }
 
     @VisibleForTesting
@@ -440,7 +467,7 @@
         }
 
         @Override
-        public void connectionReady(RcsFeatureManager manager) {
+        public void connectionReady(RcsFeatureManager manager, int subId) {
             mRcsFeatureManager = manager;
             mRcsProvisioningInfos.forEach(v -> v.onRcsStatusChanged(manager.getConfig()));
         }
@@ -454,7 +481,7 @@
 
     @VisibleForTesting
     public RcsProvisioningMonitor(PhoneGlobals app, Looper looper, RoleManagerAdapter roleManager,
-            FeatureConnectorFactory<RcsFeatureManager> factory) {
+            FeatureConnectorFactory<RcsFeatureManager> factory, RcsStats rcsStats) {
         mPhone = app;
         mHandler = new MyHandler(looper);
         mCarrierConfigManager = mPhone.getSystemService(CarrierConfigManager.class);
@@ -465,6 +492,7 @@
         logv("DMA is " + mDmaPackageName);
         mDmaChangedListener = new DmaChangedListener();
         mFeatureFactory = factory;
+        mRcsStats = rcsStats;
         init();
     }
 
@@ -477,7 +505,8 @@
             HandlerThread handlerThread = new HandlerThread(TAG);
             handlerThread.start();
             sInstance = new RcsProvisioningMonitor(app, handlerThread.getLooper(),
-                    new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector);
+                    new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector,
+                    RcsStats.getInstance());
         }
         return sInstance;
     }
@@ -704,6 +733,10 @@
                     logv("acs not used, set cached config and notify.");
                     v.setConfig(cachedConfig);
                 }
+
+                // store RCS metrics - DMA changed event
+                mRcsStats.onRcsClientProvisioningStats(k,
+                        RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED);
             });
         }
     }
@@ -803,6 +836,14 @@
         }
         info.setConfig(isCompressed ? RcsConfig.decompressGzip(config) : config);
         updateConfigForSub(subId, config, isCompressed);
+
+        // Supporting ACS means config data comes from ACS
+        // store RCS metrics - received provisioning event
+        if (isAcsUsed(subId)) {
+            mRcsStats.onRcsAcsProvisioningStats(subId, 200,
+                    RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML,
+                    isRcsVolteSingleRegistrationEnabled(subId));
+        }
     }
 
     private void onReconfigRequest(int subId) {
@@ -814,6 +855,10 @@
             updateConfigForSub(subId, null, true);
             info.triggerRcsReconfiguration();
         }
+
+        // store RCS metrics - reconfig event
+        mRcsStats.onRcsClientProvisioningStats(subId,
+                RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION);
     }
 
     private void notifyDmaForSub(int subId, int capability) {
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
index 6f5fcf5..3fa1e58 100644
--- a/src/com/android/phone/ServiceStateProvider.java
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -426,30 +426,44 @@
                 ss = unredactedServiceState;
             } else {
                 availableColumns = ALL_COLUMNS;
-
-                final boolean hasLocationPermission = hasLocationPermission();
-                if (!enforceLocationPermission || hasLocationPermission) {
-                    // No matter the targetSdkVersion, return unredacted ServiceState if caller does
-                    // have location permission or location permission enforcement is not introduced
+                if (!enforceLocationPermission) {
+                    // No matter the targetSdkVersion, return unredacted ServiceState if location
+                    // permission enforcement is not introduced
                     ss = unredactedServiceState;
                 } else {
-                    // The caller has targetSdkVersion S+ but no location permission. It explicitly
-                    // requires location protected columns. Throw SecurityException to fail loudly.
-                    if (targetingAtLeastS && projection != null) {
+                    boolean implicitlyQueryLocation = projection == null;
+                    boolean explicitlyQueryLocation = false;
+                    if (projection != null) {
                         for (String requiredColumn : projection) {
                             if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) {
-                                throw new SecurityException("Column " + requiredColumn
-                                        + "requires location permissions to access.");
+                                explicitlyQueryLocation = true;
+                                break;
                             }
                         }
                     }
 
-                    // In all other cases, return the redacted ServiceState.
-                    // The caller has no location permission but only requires columns without
-                    // location sensitive info or "all" columns, return result that scrub out all
-                    // sensitive info. In later case, we will not know which columns will be fetched
-                    // from the returned cursor until the result has been returned.
-                    ss = getLocationRedactedServiceState(unredactedServiceState);
+                    // Check location permission only when location sensitive info are queried
+                    // (either explicitly or implicitly) to avoid caller get blamed with location
+                    // permission when query non sensitive info.
+                    if (implicitlyQueryLocation || explicitlyQueryLocation) {
+                        if (hasLocationPermission()) {
+                            ss = unredactedServiceState;
+                        } else {
+                            if (targetingAtLeastS) {
+                                // Throw SecurityException to fail loudly if caller is targetSDK S+
+                                throw new SecurityException(
+                                        "Querying location sensitive info requires location "
+                                                + "permissions");
+                            } else {
+                                // For backward compatibility, return redacted value for old SDK
+                                ss = getLocationRedactedServiceState(unredactedServiceState);
+                            }
+                        }
+                    } else {
+                        // The caller is not interested in location sensitive info, return result
+                        // that scrub out all sensitive info. And no permission check is needed.
+                        ss = getLocationRedactedServiceState(unredactedServiceState);
+                    }
                 }
             }
 
@@ -700,8 +714,6 @@
     /* package */ static ServiceState getLocationRedactedServiceState(ServiceState serviceState) {
         ServiceState ss =
                 serviceState.createLocationInfoSanitizedCopy(true /*removeCoarseLocation*/);
-        // TODO(b/188061647): remove the additional redaction once it is fixed in SS
-        ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID, ServiceState.UNKNOWN_ID);
         return ss;
     }
 }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index c6ee74e..eaf4371 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -250,13 +250,13 @@
                 FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
         map.put("chatbot_v2", new ArraySet<>(Arrays.asList(
                 FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
-                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_V2_SUPPORTED)));
         map.put("chatbot_sa", new ArraySet<>(Arrays.asList(
                 FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
                 FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
         map.put("chatbot_sa_v2", new ArraySet<>(Arrays.asList(
                 FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
-                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_V2_SUPPORTED)));
         map.put("chatbot_role", Collections.singleton(FeatureTags.FEATURE_TAG_CHATBOT_ROLE));
         TEST_FEATURE_TAG_MAP = Collections.unmodifiableMap(map);
     }
@@ -323,8 +323,6 @@
             case GET_ALLOWED_NETWORK_TYPES_FOR_USER:
             case SET_ALLOWED_NETWORK_TYPES_FOR_USER:
                 return handleAllowedNetworkTypesCommand(cmd);
-            case RADIO_SUBCOMMAND:
-                return handleRadioCommand();
             default: {
                 return handleDefaultCommands(cmd);
             }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 7bf4697..f2a3ff7 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -1049,6 +1049,8 @@
                 display = r.getString(R.string.radioInfo_service_in);
                 break;
             case ServiceState.STATE_OUT_OF_SERVICE:
+                display = r.getString(R.string.radioInfo_service_out);
+                break;
             case ServiceState.STATE_EMERGENCY_ONLY:
                 display = r.getString(R.string.radioInfo_service_emergency);
                 break;
@@ -1175,9 +1177,16 @@
         if (s == null) s = r.getString(R.string.radioInfo_unknown);
         mSubscriberId.setText(s);
 
-        //FIXME: Replace with a TelephonyManager call
-        s = mPhone.getLine1Number();
-        if (s == null) s = r.getString(R.string.radioInfo_unknown);
+        SubscriptionManager subMgr = getSystemService(SubscriptionManager.class);
+        int subId = mPhone.getSubId();
+        s = subMgr.getPhoneNumber(subId)
+                + " { CARRIER:"
+                + subMgr.getPhoneNumber(subId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER)
+                + ", UICC:"
+                + subMgr.getPhoneNumber(subId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC)
+                + ", IMS:"
+                + subMgr.getPhoneNumber(subId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS)
+                + " }";
         mLine1Number.setText(s);
     }
 
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 0be927a..c62b4fa 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -257,7 +257,7 @@
 
                 @Override
                 public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
-                    Log.d(this, "onConnectionPropertiesChanged: Connection: %s,"
+                    Log.i(ImsConference.this, "onConnectionPropertiesChanged: Connection: %s,"
                             + " connectionProperties: %s", c, connectionProperties);
                     updateConnectionProperties(connectionProperties);
                 }
@@ -383,6 +383,11 @@
     private boolean mIsUsingSimCallManager = false;
 
     /**
+     * See {@link #isRemotelyHosted()} for details.
+     */
+    private boolean mWasRemotelyHosted = false;
+
+    /**
      * Where {@link #isMultiparty()} is {@code false}, contains the
      * {@link ConferenceParticipantConnection#getUserEntity()} and
      * {@link ConferenceParticipantConnection#getEndpoint()} of the single participant which this
@@ -522,11 +527,16 @@
                 (properties & Connection.PROPERTY_IS_EXTERNAL_CALL) != 0);
 
         conferenceProperties = changeBitmask(conferenceProperties,
-                Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
+                Connection.PROPERTY_REMOTELY_HOSTED, isRemotelyHosted());
 
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_IS_ADHOC_CONFERENCE,
                 (properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+        Log.i(this, "applyHostProperties: confProp=%s", conferenceProperties);
+
+        conferenceProperties = changeBitmask(conferenceProperties,
+                Connection.PROPERTY_CROSS_SIM,
+                (properties & Connection.PROPERTY_CROSS_SIM) != 0);
 
         return conferenceProperties;
     }
@@ -774,6 +784,26 @@
     }
 
     /**
+     * Returns whether the conference is remotely hosted or not.
+     * This method will cache the current remotely hosted state when the conference host or
+     * original connection becomes null.  This is important for scenarios where the conference host
+     * or original connection changes midway through a conference such as in an SRVCC scenario.
+     * @return {@code true} if the conference was remotely hosted based on the conference host and
+     * its original connection, or based on the last known remotely hosted state.  {@code false}
+     * otherwise.
+     */
+    public boolean isRemotelyHosted() {
+        if (mConferenceHost == null || mConferenceHost.getOriginalConnection() == null) {
+            return mWasRemotelyHosted;
+        }
+        com.android.internal.telephony.Connection originalConnection =
+                mConferenceHost.getOriginalConnection();
+        mWasRemotelyHosted = originalConnection.isMultiparty()
+                && !originalConnection.isConferenceHost();
+        return mWasRemotelyHosted;
+    }
+
+    /**
      * Determines if this conference is hosted on the current device or the peer device.
      *
      * @return {@code true} if this conference is hosted on the current device, {@code false} if it
@@ -1209,6 +1239,7 @@
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent.getOriginalConnection(), participant,
                 !isConferenceHost() /* isRemotelyHosted */);
+
         if (participant.getConnectTime() == 0) {
             connection.setConnectTimeMillis(parent.getConnectTimeMillis());
             connection.setConnectionStartElapsedRealtimeMillis(
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 228541a..9aa3dbe 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -40,6 +40,7 @@
  */
 final class TelephonyConferenceController {
     private static final int TELEPHONY_CONFERENCE_MAX_SIZE = 5;
+    private static final String RIL_REPORTED_CONFERENCE_CALL_STRING = "Conference Call";
 
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
             new TelephonyConnection.TelephonyConnectionListener() {
@@ -179,7 +180,6 @@
     private void recalculateConference() {
         Set<TelephonyConnection> conferencedConnections = new HashSet<>();
         int numGsmConnections = 0;
-
         for (TelephonyConnection connection : mTelephonyConnections) {
             com.android.internal.telephony.Connection radioConnection =
                 connection.getOriginalConnection();
@@ -271,11 +271,19 @@
                             // Remove all instances of PROPERTY_IS_DOWNGRADED_CONFERENCE. This
                             // property should only be set on the parent call (i.e. the newly
                             // created TelephonyConference.
-                            Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE from connection"
-                                    + " %s", connection);
-                            int newProperties = connection.getConnectionProperties()
-                                    & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
-                            connection.setTelephonyConnectionProperties(newProperties);
+                            // This doesn't apply to a connection whose address is "Conference
+                            // Call", which may be updated by some modem to create a connection
+                            // to represent a merged conference connection in SRVCC.
+                            if (connection.getAddress() == null
+                                    || !connection.getAddress().getSchemeSpecificPart()
+                                            .equalsIgnoreCase(
+                                                    RIL_REPORTED_CONFERENCE_CALL_STRING)) {
+                                Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE"
+                                        + " from connection %s", connection);
+                                int newProperties = connection.getConnectionProperties()
+                                        & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
+                                connection.setTelephonyConnectionProperties(newProperties);
+                            }
                             isDowngradedConference = true;
                         }
                         mTelephonyConference.addTelephonyConnection(connection);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 18a40cf..ed07726 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -3814,4 +3814,13 @@
             mCommunicator.sendMessages(set);
         }
     }
+
+    /**
+     * Returns the current telephony connection listeners for test purposes.
+     * @return list of telephony connection listeners.
+     */
+    @VisibleForTesting
+    public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
+        return new ArrayList<>(mTelephonyListeners);
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index e637791..b6f785c 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -58,10 +58,10 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.d2d.Communicator;
+import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
@@ -499,7 +499,8 @@
 
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
-        registerReceiver(mTtyBroadcastReceiver, intentFilter);
+        registerReceiver(mTtyBroadcastReceiver, intentFilter,
+                android.Manifest.permission.MODIFY_PHONE_STATE, null);
     }
 
     @Override
@@ -2085,8 +2086,7 @@
             if (phone.getEmergencyNumberTracker() != null) {
                 if (phone.getEmergencyNumberTracker().isEmergencyNumber(
                         emergencyNumberAddress, true)) {
-                    if (phone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)
-                            || isAvailableForEmergencyCalls(phone)) {
+                    if (isAvailableForEmergencyCalls(phone)) {
                         // a)
                         if (phone.getPhoneId() == defaultVoicePhoneId) {
                             Log.i(this, "getPhoneForEmergencyCall, Phone Id that supports"
@@ -2209,12 +2209,6 @@
                 // Only sort if there are enough elements to do so.
                 if (phoneSlotStatus.size() > 1) {
                     Collections.sort(phoneSlotStatus, (o1, o2) -> {
-                        if (!o1.hasDialedEmergencyNumber && o2.hasDialedEmergencyNumber) {
-                            return -1;
-                        }
-                        if (o1.hasDialedEmergencyNumber && !o2.hasDialedEmergencyNumber) {
-                            return 1;
-                        }
                         // Sort by non-absent SIM.
                         if (o1.simState == TelephonyManager.SIM_STATE_ABSENT
                                 && o2.simState != TelephonyManager.SIM_STATE_ABSENT) {
@@ -2233,6 +2227,13 @@
                         if (o2.isLocked && !o1.isLocked) {
                             return 1;
                         }
+                        // Prefer slots where the number is considered emergency.
+                        if (!o1.hasDialedEmergencyNumber && o2.hasDialedEmergencyNumber) {
+                            return -1;
+                        }
+                        if (o1.hasDialedEmergencyNumber && !o2.hasDialedEmergencyNumber) {
+                            return 1;
+                        }
                         // sort by number of RadioAccessFamily Capabilities.
                         int compare = RadioAccessFamily.compare(o1.capabilities, o2.capabilities);
                         if (compare == 0) {
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index cc1a2cc..0e1cb4b 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -128,7 +128,7 @@
     private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
             new FeatureConnector.Listener<RcsFeatureManager>() {
                 @Override
-                public void connectionReady(RcsFeatureManager manager)
+                public void connectionReady(RcsFeatureManager manager, int subId)
                         throws com.android.ims.ImsException {
                     if (manager == null) {
                         logw("connectionReady returned null RcsFeatureManager");
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index e72b0ab..dfcea74 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.ImsStateCallbackController;
 import com.android.phone.R;
@@ -164,6 +165,7 @@
         mFeatureControllers = new SparseArray<>(numSlots);
         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+        RcsStats.getInstance().registerUceCallback();
     }
 
     @VisibleForTesting
@@ -174,6 +176,7 @@
         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
         sResourceProxy = resourceProxy;
         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+        RcsStats.getInstance().registerUceCallback();
     }
 
     /**
diff --git a/testapps/EmbmsServiceTestApp/Android.bp b/testapps/EmbmsServiceTestApp/Android.bp
index 584e5bd..236ba43 100644
--- a/testapps/EmbmsServiceTestApp/Android.bp
+++ b/testapps/EmbmsServiceTestApp/Android.bp
@@ -1,13 +1,9 @@
-// Build the Sample Embms Services
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+// Build the Sample Embms Services
 android_app {
     name: "EmbmsTestService",
     srcs: ["src/**/*.java"],
diff --git a/testapps/EmbmsTestDownloadApp/Android.bp b/testapps/EmbmsTestDownloadApp/Android.bp
index c1b9425..7a70221 100644
--- a/testapps/EmbmsTestDownloadApp/Android.bp
+++ b/testapps/EmbmsTestDownloadApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 src_dirs = ["src"]
diff --git a/testapps/EmbmsTestStreamingApp/Android.bp b/testapps/EmbmsTestStreamingApp/Android.bp
index 9f082ee..d2b8d46 100644
--- a/testapps/EmbmsTestStreamingApp/Android.bp
+++ b/testapps/EmbmsTestStreamingApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_test {
diff --git a/testapps/GbaTestApp/Android.bp b/testapps/GbaTestApp/Android.bp
index b3c45dd..76e02a0 100644
--- a/testapps/GbaTestApp/Android.bp
+++ b/testapps/GbaTestApp/Android.bp
@@ -14,11 +14,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_test {
diff --git a/testapps/ImsTestService/Android.bp b/testapps/ImsTestService/Android.bp
index 7073749..a0bebce 100644
--- a/testapps/ImsTestService/Android.bp
+++ b/testapps/ImsTestService/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_app {
diff --git a/testapps/SmsManagerTestApp/Android.bp b/testapps/SmsManagerTestApp/Android.bp
index 4d4afcb..52fea61 100644
--- a/testapps/SmsManagerTestApp/Android.bp
+++ b/testapps/SmsManagerTestApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_app {
diff --git a/testapps/TelephonyManagerTestApp/Android.bp b/testapps/TelephonyManagerTestApp/Android.bp
index e95d62f..0ff917e 100644
--- a/testapps/TelephonyManagerTestApp/Android.bp
+++ b/testapps/TelephonyManagerTestApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_test {
diff --git a/testapps/TelephonyRegistryTestApp/Android.bp b/testapps/TelephonyRegistryTestApp/Android.bp
index 2439461..991e85c 100644
--- a/testapps/TelephonyRegistryTestApp/Android.bp
+++ b/testapps/TelephonyRegistryTestApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_test {
diff --git a/testapps/TestRcsApp/TestApp/Android.bp b/testapps/TestRcsApp/TestApp/Android.bp
index cda7d17..40254af 100644
--- a/testapps/TestRcsApp/TestApp/Android.bp
+++ b/testapps/TestRcsApp/TestApp/Android.bp
@@ -1,10 +1,6 @@
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_app {
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp b/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp
index 215c692..34b0a12 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp
@@ -1,22 +1,6 @@
-
-
 package {
-    default_applicable_licenses: [
-        "packages_services_Telephony_testapps_TestRcsApp_aosp_test_rcsclient_license",
-    ],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
-    name: "packages_services_Telephony_testapps_TestRcsApp_aosp_test_rcsclient_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-    ],
-    license_text: [
-        "LICENSE",
-    ],
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_library {
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE b/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE
deleted file mode 100644
index b9b9d2a..0000000
--- a/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE
+++ /dev/null
@@ -1,176 +0,0 @@
-Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/tests/Android.bp b/tests/Android.bp
index 083d64d..08cac05 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -16,11 +16,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_services_Telephony_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_services_Telephony_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_test {
diff --git a/tests/src/com/android/phone/ImsProvisioningControllerTest.java b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
new file mode 100644
index 0000000..c26b6a0
--- /dev/null
+++ b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
@@ -0,0 +1,1767 @@
+/*
+ * Copyright 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 android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+import static android.telephony.ims.ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
+import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NR;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyRegistryManager;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.ims.RcsFeatureManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit tests for ImsProvisioningContorller
+ */
+public class ImsProvisioningControllerTest {
+    private static final String TAG = "ImsProvisioningControllerTest";
+    private static final int[] MMTEL_CAPAS = new int[]{
+            CAPABILITY_TYPE_VOICE,
+            CAPABILITY_TYPE_VIDEO,
+            CAPABILITY_TYPE_UT,
+            CAPABILITY_TYPE_SMS,
+            CAPABILITY_TYPE_CALL_COMPOSER
+    };
+    private static final int MMTEL_CAPA_INVALID = 0;
+
+    private static final int RCS_CAPA_INVALID = RcsImsCapabilities.CAPABILITY_TYPE_NONE;
+
+    private static final int[] RADIO_TECHS = new int[]{
+            REGISTRATION_TECH_LTE,
+            REGISTRATION_TECH_IWLAN,
+            REGISTRATION_TECH_CROSS_SIM,
+            REGISTRATION_TECH_NR
+    };
+    private static final int RADIO_TECH_INVALID = ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+
+    @Mock
+    Context mContext;
+
+    @Mock
+    PhoneGlobals mPhone;
+    @Mock
+    CarrierConfigManager mCarrierConfigManager;
+    private PersistableBundle mPersistableBundle0;
+    private PersistableBundle mPersistableBundle1;
+    @Mock
+    SubscriptionManager mSubscriptionManager;
+    @Mock
+    TelephonyRegistryManager mTelephonyRegistryManager;
+    @Mock
+    ImsProvisioningLoader mImsProvisioningLoader;
+
+    @Mock
+    ImsManager mImsManager;
+    @Mock
+    ImsConfig mImsConfig;
+    @Mock
+    ImsProvisioningController.MmTelFeatureConnector mMmTelFeatureConnector;
+    @Mock
+    FeatureConnector<ImsManager> mMmTelFeatureConnector0;
+    @Mock
+    FeatureConnector<ImsManager> mMmTelFeatureConnector1;
+    @Captor
+    ArgumentCaptor<FeatureConnector.Listener<ImsManager>> mMmTelConnectorListener0;
+    @Captor
+    ArgumentCaptor<FeatureConnector.Listener<ImsManager>> mMmTelConnectorListener1;
+
+    @Mock
+    RcsFeatureManager mRcsFeatureManager;
+    @Mock
+    ImsProvisioningController.RcsFeatureConnector mRcsFeatureConnector;
+    @Mock
+    FeatureConnector<RcsFeatureManager> mRcsFeatureConnector0;
+    @Mock
+    FeatureConnector<RcsFeatureManager> mRcsFeatureConnector1;
+    @Captor
+    ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListener0;
+    @Captor
+    ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListener1;
+
+    @Mock
+    IFeatureProvisioningCallback mIFeatureProvisioningCallback0;
+    @Mock
+    IFeatureProvisioningCallback mIFeatureProvisioningCallback1;
+
+    @Mock
+    IBinder mIbinder0;
+    @Mock
+    IBinder mIbinder1;
+
+    private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
+
+    private Handler mHandler;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+
+    TestImsProvisioningController mTestImsProvisioningController;
+
+    int mPhoneId0 = 0;
+    int mPhoneId1 = 1;
+    int mSubId0 = 1234;
+    int mSubId1 = 5678;
+
+    SparseArray<int[]> mMmTelTechMap = new SparseArray<>();
+    SparseArray<int[]> mRcsTechMap = new SparseArray<>();
+
+    int[][] mMmTelProvisioningStorage;
+    int[][] mRcsProvisioningStorage;
+    int[][] mImsConfigStorage;
+
+    private class TestImsProvisioningController extends ImsProvisioningController {
+        boolean mIsValidSubId = true;
+
+        TestImsProvisioningController() {
+            super(mPhone, 2, mHandlerThread.getLooper(),
+                    mMmTelFeatureConnector, mRcsFeatureConnector,
+                    mImsProvisioningLoader);
+        }
+
+        protected int getSubId(int slotId) {
+            return (slotId == mPhoneId0) ? mSubId0 : mSubId1;
+        }
+
+        protected int getSlotId(int subId) {
+            return (subId == mSubId0) ? mPhoneId0 : mPhoneId1;
+        }
+
+        protected ImsConfig getImsConfig(ImsManager imsManager) {
+            return mImsConfig;
+        }
+
+        protected ImsConfig getImsConfig(IImsConfig iImsConfig) {
+            return mImsConfig;
+        }
+
+        protected int[] getTechsFromCarrierConfig(int subId, int capability, boolean isMmtel) {
+            if (isMmtel) {
+                return mMmTelTechMap.get(capability);
+            } else {
+                return mRcsTechMap.get(capability);
+            }
+        }
+
+        protected boolean isValidSubId(int subId) {
+            return mIsValidSubId;
+        }
+
+        public void setValidSubId(boolean valid) {
+            mIsValidSubId = valid;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("setUp");
+        MockitoAnnotations.initMocks(this);
+
+        when(mPhone.getSystemServiceName(eq(CarrierConfigManager.class)))
+                .thenReturn(Context.CARRIER_CONFIG_SERVICE);
+        when(mPhone.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE)))
+                .thenReturn(mCarrierConfigManager);
+
+        mPersistableBundle0 = new PersistableBundle();
+        mPersistableBundle1 = new PersistableBundle();
+
+        when(mCarrierConfigManager.getConfigForSubId(eq(mSubId0)))
+                .thenReturn(mPersistableBundle0);
+        when(mCarrierConfigManager.getConfigForSubId(eq(mSubId1)))
+                .thenReturn(mPersistableBundle1);
+
+        when(mPhone.getSystemServiceName(eq(SubscriptionManager.class)))
+                .thenReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        when(mPhone.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+                .thenReturn(mSubscriptionManager);
+
+
+        when(mPhone.getSystemServiceName(eq(TelephonyRegistryManager.class)))
+                .thenReturn(Context.TELEPHONY_REGISTRY_SERVICE);
+        when(mPhone.getSystemService(eq(Context.TELEPHONY_REGISTRY_SERVICE)))
+                .thenReturn(mTelephonyRegistryManager);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                mSubChangedListener = (SubscriptionManager.OnSubscriptionsChangedListener)
+                        invocation.getArguments()[0];
+                return null;
+            }
+        }).when(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class),
+                any());
+
+        mHandlerThread = new HandlerThread("ImsStateCallbackControllerTest");
+        mHandlerThread.start();
+
+        initializeDefaultData();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        logd("tearDown");
+        if (mTestImsProvisioningController != null) {
+            mTestImsProvisioningController.destroy();
+            mTestImsProvisioningController = null;
+        }
+
+        if (mLooper != null) {
+            mLooper.destroy();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void addFeatureProvisioningChangedCallback_withCallback() throws Exception {
+        createImsProvisioningController();
+
+        // add callback with valid obj
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        // add callback with invalid obj
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(mSubId0, null);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        // remove callback with valid obj
+        try {
+            mTestImsProvisioningController.removeFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        // remove callback with invalid obj
+        try {
+            mTestImsProvisioningController.removeFeatureProvisioningChangedCallback(mSubId0, null);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void connectionReady_MmTelFeatureListener() throws Exception {
+        createImsProvisioningController();
+
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId1, mIFeatureProvisioningCallback1);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+
+        // change subId to be invalid
+        mTestImsProvisioningController.setValidSubId(false);
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+
+        // setConfig not called, wait until subId is valid
+        verify(mImsConfig, times(0)).setConfig(anyInt(), anyInt());
+
+        // change subId
+        mSubChangedListener.onSubscriptionsChanged();
+        processAllMessages();
+
+        int[] keys = {
+                ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
+                ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
+                ProvisioningManager.KEY_VT_PROVISIONING_STATUS};
+
+        // verify # of read data times from storage : # of MmTel storage length
+        verify(mImsProvisioningLoader, atLeast(keys.length))
+                .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), anyInt(), anyInt());
+
+        for (int index = 0; index < keys.length; index++) {
+            // verify function call vendor interface
+            verify(mImsConfig, times(1)).setConfig(eq(keys[index]), anyInt());
+        }
+
+        // verify other interactions
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+    }
+
+    @Test
+    @SmallTest
+    public void connectionReady_RcsFeatureListener() throws Exception {
+        createImsProvisioningController();
+
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId1, mIFeatureProvisioningCallback1);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // verify # of read data times from storage : # of Rcs storage length
+        verify(mImsProvisioningLoader, times(1))
+                .getProvisioningStatus(eq(mSubId0), eq(FEATURE_RCS), anyInt(), anyInt());
+
+        int key = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+        // verify function call vendor interface
+        verify(mImsConfig, times(1)).setConfig(eq(key), anyInt());
+
+        // verify other interactions
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+    }
+
+    @Test
+    @SmallTest
+    public void isImsProvisioningRequiredForCapability_withInvalidCapabilityTech()
+            throws Exception {
+        createImsProvisioningController();
+
+        // invalid Capa. and valid Radio tech - IllegalArgumentException OK
+        try {
+            mTestImsProvisioningController.isImsProvisioningRequiredForCapability(
+                    mSubId0, MMTEL_CAPA_INVALID, RADIO_TECHS[0]);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        // valid Capa. and invalid Radio tech - IllegalArumentException OK
+        try {
+            mTestImsProvisioningController.isImsProvisioningRequiredForCapability(
+                    mSubId0, MMTEL_CAPAS[0], RADIO_TECH_INVALID);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void isImsProvisioningRequiredForCapability_withValidCapabilityTech() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability
+        // voice, all tech
+        // video, all tech
+        // UT, all tech
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_UT,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        boolean[][] expectedRequired = new boolean[][] {
+                {true, true, true, true},
+                {true, true, true, true},
+                {true, true, true, true},
+                {false, false, false, false},
+                {false, false, false, false}
+        };
+
+        boolean isRequired;
+        for (int i = 0; i < MMTEL_CAPAS.length; i++) {
+            for (int j = 0; j < RADIO_TECHS.length; j++) {
+                isRequired = mTestImsProvisioningController
+                .isImsProvisioningRequiredForCapability(
+                        mSubId0, MMTEL_CAPAS[i], RADIO_TECHS[j]);
+                assertEquals(expectedRequired[i][j], isRequired);
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void isRcsProvisioningRequiredForCapability_withInvalidCapabilityTech()
+            throws Exception {
+        createImsProvisioningController();
+
+        // invalid Capa. and valid Radio tech - IllegalArgumentException OK
+        try {
+            mTestImsProvisioningController.isRcsProvisioningRequiredForCapability(
+                    mSubId0, RCS_CAPA_INVALID, RADIO_TECHS[0]);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        // valid Capa. and invalid Radio tech - IllegalArumentException OK
+        try {
+            mTestImsProvisioningController.isRcsProvisioningRequiredForCapability(
+                    mSubId0, CAPABILITY_TYPE_PRESENCE_UCE, RADIO_TECH_INVALID);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void isRcsProvisioningRequiredForCapability_withValidCapabilityTech() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability : PRESENCE, tech : all
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        boolean[] expectedRequired = new boolean[] {true, true, true, true};
+
+        boolean isRequired;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            isRequired = mTestImsProvisioningController
+            .isRcsProvisioningRequiredForCapability(
+                    mSubId0, CAPABILITY_TYPE_PRESENCE_UCE, RADIO_TECHS[i]);
+            assertEquals(expectedRequired[i], isRequired);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void getImsProvisioningRequiredForCapability_withVoiceVideoUt() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability
+        // voice, all tech
+        // video, all tech
+        // UT, all tech
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_UT,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_IWLAN, 0},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_IWLAN, 0},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_CROSS_SIM, 0},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_NR, 0},
+        };
+
+        boolean[] expectedVoiceProvisioningStatus = new boolean[] {false, true, true, true};
+        boolean[] expectedVideoProvisioningStatus = new boolean[] {false, false, true, true};
+        boolean[] expectedUtProvisioningStatus = new boolean[] {false, false, false, false};
+
+        boolean provisioned = false;
+        int capability = CAPABILITY_TYPE_VOICE;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisioned = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value
+            assertEquals(expectedVoiceProvisioningStatus[i], provisioned);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1))
+                    .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]));
+        }
+
+        capability = CAPABILITY_TYPE_VIDEO;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisioned = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value
+            assertEquals(expectedVideoProvisioningStatus[i], provisioned);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1))
+                    .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]));
+        }
+
+        capability = CAPABILITY_TYPE_UT;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisioned = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value
+            assertEquals(expectedUtProvisioningStatus[i], provisioned);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1))
+                    .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]));
+        }
+
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void getImsProvisioningRequiredForCapability_withNotSet() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        processAllMessages();
+
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // voice, LTE, IWLAN
+        // video, LTE
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE});
+
+        // provisioning StatusP, all of provisioning status is not provisioned
+        mMmTelProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, -1}
+        };
+        // provisioning status in vendor ImsService
+        mImsConfigStorage = new int[][] {
+                {KEY_VOLTE_PROVISIONING_STATUS, 1},
+                {KEY_VT_PROVISIONING_STATUS, 0},
+                {KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, 1},
+        };
+
+        boolean provisioned;
+
+        // for KEY_VOLTE_PROVISIONING_STATUS
+        int capability = CAPABILITY_TYPE_VOICE;
+        int tech = REGISTRATION_TECH_LTE;
+        provisioned = mTestImsProvisioningController
+                .getImsProvisioningStatusForCapability(mSubId0, capability, tech);
+
+        // verify return value default false - not provisioned
+        assertEquals(true, provisioned);
+
+        verify(mImsProvisioningLoader, times(1))
+                .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability), eq(tech));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsConfig, times(1)).getConfigInt(eq(KEY_VOLTE_PROVISIONING_STATUS));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(1))
+                .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability), eq(tech),
+                        eq(provisioned));
+
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // for KEY_VT_PROVISIONING_STATUS
+        capability = CAPABILITY_TYPE_VIDEO;
+        tech = REGISTRATION_TECH_LTE;
+        provisioned = mTestImsProvisioningController
+                .getImsProvisioningStatusForCapability(mSubId0, capability, tech);
+
+        // verify return value default false - not provisioned
+        assertEquals(false, provisioned);
+
+        verify(mImsProvisioningLoader, times(1))
+                .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability), eq(tech));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsConfig, times(1)).getConfigInt(eq(KEY_VT_PROVISIONING_STATUS));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(1))
+                .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability), eq(tech),
+                        eq(provisioned));
+
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void getRcsProvisioningRequiredForCapability_withPresence() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability
+        // PRESENCE, all tech
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status
+        mRcsProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, 1}
+        };
+
+        boolean[] expectedPresenceProvisioningStatus = new boolean[] {true, true, true, true};
+
+        boolean provisioned = false;
+        int capability = CAPABILITY_TYPE_PRESENCE_UCE;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisioned = mTestImsProvisioningController
+                    .getRcsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value
+            assertEquals(expectedPresenceProvisioningStatus[i], provisioned);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(
+                    eq(mSubId0), eq(FEATURE_RCS), eq(capability), eq(RADIO_TECHS[i]));
+        }
+
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void getRcsProvisioningRequiredForCapability_withNotSet() throws Exception {
+        createImsProvisioningController();
+
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // PRESENCE, LTE, IWLAN
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN});
+
+        // provisioning Status, all of provisioning status is not provisioned
+        mRcsProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, -1}
+        };
+        // provisioning status in vendor ImsService
+        mImsConfigStorage = new int[][] {
+                {KEY_EAB_PROVISIONING_STATUS, 1}
+        };
+
+        boolean provisioned;
+
+        // for KEY_EAB_PROVISIONING_STATUS
+        int capability = CAPABILITY_TYPE_PRESENCE_UCE;
+        int tech = REGISTRATION_TECH_LTE;
+        provisioned = mTestImsProvisioningController
+                .getRcsProvisioningStatusForCapability(mSubId0, capability, tech);
+
+        // verify return value default false - not provisioned
+        assertEquals(true, provisioned);
+
+        verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capability), eq(tech));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsConfig, times(1)).getConfigInt(eq(KEY_EAB_PROVISIONING_STATUS));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(RADIO_TECHS.length)).setProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capability), anyInt(), eq(provisioned));
+
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setImsProvisioningRequiredForCapability_withVoice() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        processAllMessages();
+
+        // register callbacks
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // voice, all tech
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not provisioned
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, 0},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_CROSS_SIM, 0},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_NR, 0}
+        };
+
+        boolean provisionedFirst = false;
+        boolean provisionedSecond = false;
+        int capability = CAPABILITY_TYPE_VOICE;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisionedFirst = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value default false - not provisioned
+            assertEquals(false, provisionedFirst);
+
+            mTestImsProvisioningController.setImsProvisioningStatusForCapability(
+                    mSubId0, capability, RADIO_TECHS[i], !provisionedFirst);
+            processAllMessages();
+
+            provisionedSecond = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value default false - provisioned
+            assertEquals(!provisionedFirst, provisionedSecond);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(2))
+                    .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]));
+            verify(mImsProvisioningLoader, times(1))
+                    .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]), eq(provisionedSecond));
+
+            // verify weather Callback is called or not
+            verify(mIFeatureProvisioningCallback0, times(1))
+                    .onFeatureProvisioningChanged(eq(capability), eq(RADIO_TECHS[i]),
+                            eq(provisionedSecond));
+        }
+
+        // verify weather ImsConfig is called or not
+        verify(mImsConfig, times(1)).setConfig(
+                eq(KEY_VOLTE_PROVISIONING_STATUS), eq(PROVISIONING_VALUE_ENABLED));
+        verify(mImsConfig, times(1)).setConfig(
+                eq(KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE), eq(PROVISIONING_VALUE_ENABLED));
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setImsProvisioningRequiredForCapability_withVideo() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        processAllMessages();
+
+        // register callbacks
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // video, all tech
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not provisioned
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_IWLAN, 0},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_CROSS_SIM, 0},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_NR, 0}
+        };
+
+        boolean provisionedFirst = false;
+        boolean provisionedSecond = false;
+        int capability = CAPABILITY_TYPE_VIDEO;
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            // get provisioning status
+            provisionedFirst = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value default false - not provisioned
+            assertEquals(false, provisionedFirst);
+
+            mTestImsProvisioningController.setImsProvisioningStatusForCapability(
+                    mSubId0, capability, RADIO_TECHS[i], !provisionedFirst);
+            processAllMessages();
+
+            provisionedSecond = mTestImsProvisioningController
+                    .getImsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value default false - provisioned
+            assertEquals(!provisionedFirst, provisionedSecond);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(2))
+                    .getProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]));
+            verify(mImsProvisioningLoader, times(1))
+                    .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capability),
+                            eq(RADIO_TECHS[i]), eq(provisionedSecond));
+
+            // verify weather Callback is called or not
+            verify(mIFeatureProvisioningCallback0, times(1))
+                    .onFeatureProvisioningChanged(eq(capability), eq(RADIO_TECHS[i]),
+                            eq(provisionedSecond));
+        }
+
+        // verify weather ImsConfig is called or not
+        verify(mImsConfig, times(1)).setConfig(
+                eq(KEY_VT_PROVISIONING_STATUS), eq(PROVISIONING_VALUE_ENABLED));
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setRcsProvisioningRequiredForCapability_withPresence() throws Exception {
+        createImsProvisioningController();
+
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // register callbacks
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // PRESENCE, all tech
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not provisioned
+        mRcsProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, 0},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, 0},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, 0},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, 0}
+        };
+
+        boolean provisionedFirst;
+        boolean provisionedSecond;
+        int capability = CAPABILITY_TYPE_PRESENCE_UCE;
+
+        // get provisioning status
+        provisionedFirst = mTestImsProvisioningController
+                .getRcsProvisioningStatusForCapability(mSubId0, capability, REGISTRATION_TECH_LTE);
+
+        // verify return value default false - not provisioned
+        assertEquals(false, provisionedFirst);
+
+        mTestImsProvisioningController.setRcsProvisioningStatusForCapability(
+                mSubId0, capability, REGISTRATION_TECH_LTE, !provisionedFirst);
+        processAllMessages();
+
+        provisionedSecond = mTestImsProvisioningController
+                .getRcsProvisioningStatusForCapability(mSubId0, capability, REGISTRATION_TECH_LTE);
+
+        // verify return value default false - provisioned
+        assertEquals(!provisionedFirst, provisionedSecond);
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(2)).getProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capability), eq(REGISTRATION_TECH_LTE));
+        // verify setProvisioningStatus is called RADIO_TECHS.length times for all tech or not
+        verify(mImsProvisioningLoader, times(1)).setProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capability), anyInt(), eq(provisionedSecond));
+
+        // verify weather Callback is called RADIO_TECHS.length times for all tech or not
+        verify(mIFeatureProvisioningCallback0, times(1))
+                .onRcsFeatureProvisioningChanged(eq(capability), eq(REGISTRATION_TECH_LTE),
+                        eq(provisionedSecond));
+
+        // verify weather ImsConfig is called or not
+        verify(mImsConfig, times(1)).setConfig(
+                eq(KEY_EAB_PROVISIONING_STATUS), eq(PROVISIONING_VALUE_ENABLED));
+
+        // verify reset
+        clearInvocations(mImsProvisioningLoader);
+
+        // only CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE - provisioned
+        boolean[] expected = {true, false, false, false};
+        for (int i = 0; i < RADIO_TECHS.length; i++) {
+            provisionedSecond = mTestImsProvisioningController
+                    .getRcsProvisioningStatusForCapability(mSubId0, capability, RADIO_TECHS[i]);
+
+            // verify return value
+            assertEquals(expected[i], provisionedSecond);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(
+                    eq(mSubId0), eq(FEATURE_RCS), eq(capability), eq(RADIO_TECHS[i]));
+        }
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setProvisioningValue_withMmTelKey() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        processAllMessages();
+
+        // add callback with valid obj
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // voice, all tech
+        // video, all tech
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not set
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_CROSS_SIM, -1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_NR, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_CROSS_SIM, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_NR, -1}
+        };
+
+        // MmTel valid
+        int[] keys = {
+                ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
+                ProvisioningManager.KEY_VT_PROVISIONING_STATUS,
+                ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
+        };
+        int[] capas = {
+                MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                MmTelCapabilities.CAPABILITY_TYPE_VOICE
+        };
+        int[] techs = {
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+        };
+
+        int result;
+        for (int i = 0; i < keys.length; i++) {
+            clearInvocations(mIFeatureProvisioningCallback0);
+            result = mTestImsProvisioningController.setProvisioningValue(
+                    mSubId0, keys[i], PROVISIONING_VALUE_ENABLED);
+            processAllMessages();
+
+            // check return value
+            assertEquals(ImsConfig.OperationStatusConstants.SUCCESS, result);
+
+            // check weather to save
+            verify(mImsProvisioningLoader, times(1)).setProvisioningStatus(
+                    eq(mSubId0), eq(FEATURE_MMTEL), eq(capas[i]), eq(techs[i]), eq(true));
+
+            verify(mIFeatureProvisioningCallback0, times(1))
+                    .onFeatureProvisioningChanged(eq(capas[i]), eq(techs[i]), eq(true));
+
+            verify(mImsConfig, times(1)).setConfig(eq(keys[i]), eq(PROVISIONING_VALUE_ENABLED));
+        }
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setProvisioningValue_withRcsKey() throws Exception {
+        createImsProvisioningController();
+
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // add callback with valid obj
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // presence, all tech
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not set
+        mRcsProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, -1}
+        };
+
+        int key = KEY_EAB_PROVISIONING_STATUS;
+        int capa = CAPABILITY_TYPE_PRESENCE_UCE;
+
+        int result = mTestImsProvisioningController.setProvisioningValue(
+                    mSubId0, key, PROVISIONING_VALUE_ENABLED);
+        processAllMessages();
+
+        // check return value
+        assertEquals(ImsConfig.OperationStatusConstants.SUCCESS, result);
+
+        // check weather to save, for all techs 4 times
+        verify(mImsProvisioningLoader, times(RADIO_TECHS.length)).setProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capa), anyInt(), eq(true));
+
+        verify(mIFeatureProvisioningCallback0, times(RADIO_TECHS.length))
+                .onRcsFeatureProvisioningChanged(eq(capa), anyInt(), eq(true));
+
+        verify(mImsConfig, times(1)).setConfig(eq(key), eq(PROVISIONING_VALUE_ENABLED));
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void setProvisioningValue_withInvalidKey() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // add callback with valid obj
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // invalid key
+        int[] keys = {
+                ProvisioningManager.KEY_SIP_SESSION_TIMER_SEC,
+                ProvisioningManager.KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC,
+                ProvisioningManager.KEY_TF_TIMER_VALUE_MS
+        };
+        for (int key : keys) {
+            int result = mTestImsProvisioningController.setProvisioningValue(
+                    mSubId0, key, PROVISIONING_VALUE_ENABLED);
+            processAllMessages();
+
+            // check return value
+            assertEquals(ImsConfigImplBase.CONFIG_RESULT_UNKNOWN, result);
+        }
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void getProvisioningValue_withValidKey() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // add callback with valid obj
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // voice, LTE, IWLAN
+        // video, LTE
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE});
+
+        // provisioning Status, all of provisioning status is not set
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, 1}
+        };
+
+        // provisioning required capability
+        // presence, all tech
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not set
+        mRcsProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, 1}
+        };
+
+        // MmTel keys
+        int[] keys = {
+                KEY_VOLTE_PROVISIONING_STATUS,
+                KEY_VT_PROVISIONING_STATUS,
+                KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
+        };
+        int[] capas = {
+                CAPABILITY_TYPE_VOICE,
+                CAPABILITY_TYPE_VIDEO,
+                CAPABILITY_TYPE_VOICE
+        };
+        int[] techs = {
+                REGISTRATION_TECH_LTE,
+                REGISTRATION_TECH_LTE,
+                REGISTRATION_TECH_IWLAN
+        };
+        for (int i = 0; i < keys.length; i++) {
+            int result = mTestImsProvisioningController.getProvisioningValue(mSubId0, keys[i]);
+            processAllMessages();
+
+            // check return value
+            assertEquals(PROVISIONING_VALUE_ENABLED, result);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(eq(mSubId0),
+                    eq(FEATURE_MMTEL), eq(capas[i]), eq(techs[i]));
+        }
+        clearInvocations(mImsProvisioningLoader);
+
+        // Rcs keys
+        int key = KEY_EAB_PROVISIONING_STATUS;
+        int capa = CAPABILITY_TYPE_PRESENCE_UCE;
+
+        int result = mTestImsProvisioningController.getProvisioningValue(mSubId0, key);
+        processAllMessages();
+
+        // check return value
+        assertEquals(PROVISIONING_VALUE_ENABLED, result);
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capa), anyInt());
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void getProvisioningValue_withNotSet() throws Exception {
+        createImsProvisioningController();
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+        processAllMessages();
+
+        // add callback with valid obj
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId0, mIFeatureProvisioningCallback0);
+        mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                mSubId1, mIFeatureProvisioningCallback1);
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // provisioning required capability
+        // voice, LTE, IWLAN
+        // video, LTE
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE});
+
+        // provisioning Status, all of provisioning status is not set
+        mMmTelProvisioningStorage = new int[][] {
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, -1}
+        };
+
+        // provisioning required capability
+        // presence, all tech
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+
+        // provisioning Status, all of provisioning status is not set
+        mRcsProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, -1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, -1}
+        };
+        // provisioning status in ImsService
+        mImsConfigStorage = new int[][] {
+                {KEY_VOLTE_PROVISIONING_STATUS, 1},
+                {KEY_VT_PROVISIONING_STATUS, 1},
+                {KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, 1},
+                {KEY_EAB_PROVISIONING_STATUS, 1}
+        };
+
+        // MmTel keys
+        int[] keys = {
+                KEY_VOLTE_PROVISIONING_STATUS,
+                KEY_VT_PROVISIONING_STATUS,
+                KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
+        };
+        int[] capas = {
+                CAPABILITY_TYPE_VOICE,
+                CAPABILITY_TYPE_VIDEO,
+                CAPABILITY_TYPE_VOICE
+        };
+        int[] techs = {
+                REGISTRATION_TECH_LTE,
+                REGISTRATION_TECH_LTE,
+                REGISTRATION_TECH_IWLAN
+        };
+        for (int i = 0; i < keys.length; i++) {
+            int result = mTestImsProvisioningController.getProvisioningValue(mSubId0, keys[i]);
+            processAllMessages();
+
+            // check return value
+            assertEquals(PROVISIONING_VALUE_ENABLED, result);
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(eq(mSubId0),
+                    eq(FEATURE_MMTEL), eq(capas[i]), eq(techs[i]));
+
+            // verify weather ImsConfig is called or not
+            verify(mImsConfig, times(1)).getConfigInt(eq(keys[i]));
+
+            // verify weather ImsProvisioningLoader is called or not
+            verify(mImsProvisioningLoader, times(1)).setProvisioningStatus(eq(mSubId0),
+                    eq(FEATURE_MMTEL), eq(capas[i]), eq(techs[i]), eq(true));
+
+            // verify weather callback is called or not
+            verify(mIFeatureProvisioningCallback0, times(1)).onFeatureProvisioningChanged(
+                    eq(capas[i]), eq(techs[i]), eq(true));
+        }
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // Rcs keys
+        int key = KEY_EAB_PROVISIONING_STATUS;
+        int capa = CAPABILITY_TYPE_PRESENCE_UCE;
+
+        int result = mTestImsProvisioningController.getProvisioningValue(mSubId0, key);
+        processAllMessages();
+
+        // check return value
+        assertEquals(PROVISIONING_VALUE_ENABLED, result);
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(1)).getProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capa), anyInt());
+
+        // verify weather ImsConfig is called or not
+        verify(mImsConfig, times(1)).getConfigInt(eq(key));
+
+        // verify weather ImsProvisioningLoader is called or not
+        verify(mImsProvisioningLoader, times(RADIO_TECHS.length)).setProvisioningStatus(
+                eq(mSubId0), eq(FEATURE_RCS), eq(capa), anyInt(), eq(true));
+
+        // verify weather callback is called or not
+        verify(mIFeatureProvisioningCallback0, times(RADIO_TECHS.length))
+                .onRcsFeatureProvisioningChanged(eq(capa), anyInt(), eq(true));
+
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback1);
+        verifyNoMoreInteractions(mImsConfig);
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+    }
+
+    @Test
+    @SmallTest
+    public void onMultiSimConfigChanged() throws Exception {
+        createImsProvisioningController();
+
+        // change number of slot 2 -> 1
+        mHandler.sendMessage(mHandler.obtainMessage(
+                mTestImsProvisioningController.EVENT_MULTI_SIM_CONFIGURATION_CHANGE,
+                0, 0, (Object) new AsyncResult(null, 1, null)));
+        processAllMessages();
+
+        // add callback with mSubId0, mPhoneId0 : Ok.
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        // add callbacks with new mSubId1, mPhoneId1 : IllegalArgumentException.
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId1, mIFeatureProvisioningCallback1);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        // check isImsProvisioningRequiredForCapability with mSubId1 : IllegalArgumentException
+        try {
+            mTestImsProvisioningController.isImsProvisioningRequiredForCapability(
+                    mSubId1, CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
+        } catch (IllegalArgumentException e) {
+            // expected result
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+
+        // change number of slot 1 -> 2
+        mHandler.sendMessage(mHandler.obtainMessage(
+                mTestImsProvisioningController.EVENT_MULTI_SIM_CONFIGURATION_CHANGE,
+                0, 0, (Object) new AsyncResult(null, 2, null)));
+        processAllMessages();
+
+        // add callback with mSubId0, mPhoneId0 : Ok.
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        // add callbacks with new mSubId1, mPhoneId1 : Ok.
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId1, mIFeatureProvisioningCallback1);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+
+        // check get,setImsProvisioningRequiredForCapability with mSubId1, mPhoneId1 : Ok
+        int capability = CAPABILITY_TYPE_VOICE;
+        int tech = REGISTRATION_TECH_LTE;
+        boolean provisioned;
+        provisioned = mTestImsProvisioningController.getImsProvisioningStatusForCapability(
+                mSubId1, capability, tech);
+        mTestImsProvisioningController.setImsProvisioningStatusForCapability(mSubId1,
+                capability, tech, !provisioned);
+        processAllMessages();
+
+        // verify weather Callback is called or not
+        verify(mIFeatureProvisioningCallback1, times(1))
+                .onFeatureProvisioningChanged(eq(capability), eq(tech), eq(!provisioned));
+
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mIFeatureProvisioningCallback1);
+        clearInvocations(mImsConfig);
+    }
+
+    private void createImsProvisioningController() throws Exception {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        when(mMmTelFeatureConnector
+                .create(any(), eq(0), any(), mMmTelConnectorListener0.capture(), any()))
+                .thenReturn(mMmTelFeatureConnector0);
+        when(mMmTelFeatureConnector
+                .create(any(), eq(1), any(), mMmTelConnectorListener1.capture(), any()))
+                .thenReturn(mMmTelFeatureConnector1);
+
+        when(mRcsFeatureConnector
+                .create(any(), eq(0), mRcsConnectorListener0.capture(), any(), any()))
+                .thenReturn(mRcsFeatureConnector0);
+        when(mRcsFeatureConnector
+                .create(any(), eq(1), mRcsConnectorListener1.capture(), any(), any()))
+                .thenReturn(mRcsFeatureConnector1);
+
+        when(mImsConfig.getConfigInt(anyInt()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[0]);
+                    return getImsConfigValue(i);
+                });
+        when(mImsConfig.setConfig(anyInt(), anyInt()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[0]);
+                    int j = (Integer) (invocation.getArguments()[1]);
+                    return setImsConfigValue(i, j);
+                });
+
+        when(mImsProvisioningLoader.getProvisioningStatus(anyInt(), eq(FEATURE_MMTEL), anyInt(),
+                anyInt()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[2]);
+                    int j = (Integer) (invocation.getArguments()[3]);
+                    return getProvisionedValue(i, j);
+                });
+        when(mImsProvisioningLoader.getProvisioningStatus(anyInt(), eq(FEATURE_RCS), anyInt(),
+                anyInt()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[2]);
+                    int j = (Integer) (invocation.getArguments()[3]);
+                    return getRcsProvisionedValue(i, j);
+                });
+        when(mImsProvisioningLoader
+                .setProvisioningStatus(anyInt(), eq(FEATURE_MMTEL), anyInt(), anyInt(),
+                        anyBoolean()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[2]);
+                    int j = (Integer) (invocation.getArguments()[3]);
+                    int k = (Boolean) (invocation.getArguments()[4]) ? 1 : 0;
+                    return setProvisionedValue(i, j, k);
+                });
+        when(mImsProvisioningLoader
+                .setProvisioningStatus(anyInt(), eq(FEATURE_RCS), anyInt(), anyInt(),
+                        anyBoolean()))
+                .thenAnswer(invocation -> {
+                    int i = (Integer) (invocation.getArguments()[2]);
+                    int j = (Integer) (invocation.getArguments()[3]);
+                    int k = (Boolean) (invocation.getArguments()[4]) ? 1 : 0;
+                    return setRcsProvisionedValue(i, j, k);
+                });
+
+        when(mIFeatureProvisioningCallback0.asBinder()).thenReturn(mIbinder0);
+        when(mIFeatureProvisioningCallback1.asBinder()).thenReturn(mIbinder1);
+
+        doNothing().when(mIFeatureProvisioningCallback0)
+                .onFeatureProvisioningChanged(anyInt(), anyInt(), anyBoolean());
+        doNothing().when(mIFeatureProvisioningCallback0)
+                .onRcsFeatureProvisioningChanged(anyInt(), anyInt(), anyBoolean());
+        doNothing().when(mIFeatureProvisioningCallback1)
+                .onFeatureProvisioningChanged(anyInt(), anyInt(), anyBoolean());
+        doNothing().when(mIFeatureProvisioningCallback1)
+                .onRcsFeatureProvisioningChanged(anyInt(), anyInt(), anyBoolean());
+
+        mTestImsProvisioningController = new TestImsProvisioningController();
+
+        mHandler = mTestImsProvisioningController.getHandler();
+        try {
+            mLooper = new TestableLooper(mHandler.getLooper());
+        } catch (Exception e) {
+            logd("create looper from handler failed");
+        }
+
+        verify(mRcsFeatureConnector0, atLeastOnce()).connect();
+        verify(mMmTelFeatureConnector0, atLeastOnce()).connect();
+
+        verify(mRcsFeatureConnector1, atLeastOnce()).connect();
+        verify(mMmTelFeatureConnector1, atLeastOnce()).connect();
+    }
+
+    private void initializeDefaultData() throws Exception {
+        mMmTelTechMap.clear();
+        mMmTelTechMap.put(CAPABILITY_TYPE_VOICE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_VIDEO,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_UT,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_SMS,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelTechMap.put(CAPABILITY_TYPE_CALL_COMPOSER,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mMmTelProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_UT, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_SMS, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_SMS, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_SMS, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_SMS, REGISTRATION_TECH_NR, 1},
+                {CAPABILITY_TYPE_CALL_COMPOSER, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_CALL_COMPOSER, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_CALL_COMPOSER, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_CALL_COMPOSER, REGISTRATION_TECH_NR, 1}
+        };
+
+        mRcsTechMap.clear();
+        mRcsTechMap.put(CAPABILITY_TYPE_OPTIONS_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mRcsTechMap.put(CAPABILITY_TYPE_PRESENCE_UCE,
+                new int[] {REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN,
+                        REGISTRATION_TECH_CROSS_SIM, REGISTRATION_TECH_NR});
+        mRcsProvisioningStorage = new int[][]{
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_IWLAN, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_CROSS_SIM, 1},
+                {CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_NR, 1}
+        };
+
+        mImsConfigStorage = new int[][] {
+                {KEY_VOLTE_PROVISIONING_STATUS, 1},
+                {KEY_VT_PROVISIONING_STATUS, 1},
+                {KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE, 1},
+                {KEY_EAB_PROVISIONING_STATUS, 1}
+        };
+    }
+
+    private int getProvisionedValue(int i, int j) {
+        for (int[] data : mMmTelProvisioningStorage) {
+            if (data[0] == i && data[1] == j) {
+                return data[2];
+            }
+        }
+        return 0;
+    }
+
+    private int getRcsProvisionedValue(int i, int j) {
+        for (int[] data : mRcsProvisioningStorage) {
+            if (data[0] == i && data[1] == j) {
+                return data[2];
+            }
+        }
+        return 0;
+    }
+
+    private boolean setProvisionedValue(int i, int j, int k) {
+        boolean retVal = false;
+        for (int[] data : mMmTelProvisioningStorage) {
+            if (data[0] == i && data[1] == j) {
+                if (data[2] != k) {
+                    data[2] = k;
+                    return true;
+                }
+                return false;
+            }
+        }
+        return retVal;
+    }
+
+    private boolean setRcsProvisionedValue(int i, int j, int k) {
+        boolean retVal = false;
+        for (int[] data : mRcsProvisioningStorage) {
+            if (data[0] == i && data[1] == j) {
+                if (data[2] != k) {
+                    data[2] = k;
+                    return true;
+                }
+                return false;
+            }
+        }
+        return retVal;
+    }
+
+    private int getImsConfigValue(int i) {
+        for (int[] data : mImsConfigStorage) {
+            if (data[0] == i) {
+                return data[1];
+            }
+        }
+        return -1;
+    }
+
+    private int setImsConfigValue(int i, int j) {
+        for (int[] data : mImsConfigStorage) {
+            if (data[0] == i) {
+                data[1] = j;
+                return ImsConfig.OperationStatusConstants.SUCCESS;
+            }
+        }
+        return ImsConfig.OperationStatusConstants.SUCCESS;
+    }
+
+    private void processAllMessages() {
+        while (!mLooper.getLooper().getQueue().isIdle()) {
+            mLooper.processAllMessages();
+        }
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/phone/ImsProvisioningLoaderTest.java b/tests/src/com/android/phone/ImsProvisioningLoaderTest.java
new file mode 100644
index 0000000..61cab1d
--- /dev/null
+++ b/tests/src/com/android/phone/ImsProvisioningLoaderTest.java
@@ -0,0 +1,260 @@
+/*
+ * 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 junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.PersistableBundle;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Unit Test for ImsProvisioningLoader.
+ */
+public class ImsProvisioningLoaderTest {
+    private static final String LOG_TAG = ImsProvisioningLoaderTest.class.getSimpleName();
+
+    private static final int IMS_FEATURE_MMTEL = ImsProvisioningLoader.IMS_FEATURE_MMTEL;
+    private static final int IMS_FEATURE_RCS = ImsProvisioningLoader.IMS_FEATURE_RCS;
+
+    private static final int TECH_LTE = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+    private static final int TECH_IWLAN = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+    private static final int TECH_NEW = Integer.MAX_VALUE;
+
+    private static final int CAPA_VOICE = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+    private static final int CAPA_VIDEO = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+    private static final int CAPA_UT = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+    private static final int CAPA_PRESENCE =
+            RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
+    private static final int CAPA_NEW = Integer.MAX_VALUE;
+
+    private static final int STATUS_NOT_PROVISIONED = ImsProvisioningLoader.STATUS_NOT_PROVISIONED;
+    private static final int STATUS_PROVISIONED = ImsProvisioningLoader.STATUS_PROVISIONED;
+
+    private static final int SUB_ID_1 = 111111;
+    private static final int SUB_ID_2 = 222222;
+
+    @Mock
+    Context mContext;
+    @Mock
+    SharedPreferences mSharedPreferences;
+    private ImsProvisioningLoader mImsProvisioningLoader;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
+        doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
+                mContext).getFilesDir();
+
+        mImsProvisioningLoader = new ImsProvisioningLoader(mContext);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mImsProvisioningLoader != null) {
+            mImsProvisioningLoader.clear();
+        }
+        deleteXml(SUB_ID_1, mContext);
+        deleteXml(SUB_ID_2, mContext);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetProvisioningStatus_ExistFeature() {
+        // Set MMTEL IWLAN VOICE to STATUS_PROVISIONED
+        String[] info =
+                new String[]{IMS_FEATURE_MMTEL + "," + TECH_IWLAN + "," + CAPA_VOICE + "," + getInt(
+                        true)};
+        mImsProvisioningLoader.setProvisioningToXml(SUB_ID_1, new PersistableBundle(), info);
+
+        int curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1, IMS_FEATURE_MMTEL,
+                CAPA_VOICE, TECH_IWLAN);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(true), curValue);
+
+        // Change MMTEL IWLAN VOICE provisioning status
+        boolean saveResult = mImsProvisioningLoader.setProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_IWLAN, false);
+        curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_IWLAN);
+        assertEquals(getXmlContents(SUB_ID_1), true, saveResult);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(false), curValue);
+
+        // If set to the same provisioning status,  don't save it.
+        saveResult = mImsProvisioningLoader.setProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_IWLAN, false);
+        curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_IWLAN);
+        assertEquals(getXmlContents(SUB_ID_1), false, saveResult);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(false), curValue);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetProvisioningStatus_NewFeature() {
+        // Set new capability
+        // Return true as a result to setProvisioningStatus()
+        boolean saveResult = mImsProvisioningLoader.setProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_NEW, TECH_LTE, true);
+        int curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_NEW, TECH_LTE);
+        assertEquals(getXmlContents(SUB_ID_1), true, saveResult);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(true), curValue);
+
+        // Set new tech
+        saveResult = mImsProvisioningLoader.setProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_NEW, false);
+        curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_NEW);
+        assertEquals(getXmlContents(SUB_ID_1), true, saveResult);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(false), curValue);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetProvisioningStatus_DifferentSim() {
+        // Check whether the provisioning status does not change even if SIM is changed
+        // Sub id 2, set provisioning status
+        boolean prevValue = getBooleanFromProvisioningStatus(SUB_ID_2,
+                IMS_FEATURE_RCS, CAPA_PRESENCE, TECH_IWLAN);
+        boolean saveResult = mImsProvisioningLoader.setProvisioningStatus(
+                SUB_ID_2, IMS_FEATURE_RCS, CAPA_PRESENCE, TECH_IWLAN, !prevValue);
+        int curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_2,
+                IMS_FEATURE_RCS, CAPA_PRESENCE, TECH_IWLAN);
+        assertEquals(getXmlContents(SUB_ID_2), true, saveResult);
+        assertEquals(getXmlContents(SUB_ID_2), getInt(!prevValue), curValue);
+
+        // Sub id 1, set other provisioned status
+        mImsProvisioningLoader.setProvisioningStatus(
+                SUB_ID_1, IMS_FEATURE_RCS, CAPA_PRESENCE, TECH_IWLAN, prevValue);
+
+        // Sub id 2, check the previous provisioning status isn't changed
+        curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_2,
+                IMS_FEATURE_RCS, CAPA_PRESENCE, TECH_IWLAN);
+        assertEquals(getXmlContents(SUB_ID_2), getInt(!prevValue), curValue);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetProvisioningStatus_UtProvisioningStatusIsExistInPref() {
+        // Ut provisioning status exists in preference
+        doReturn(1).when(mSharedPreferences).getInt(anyString(), anyInt());
+        int curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_UT, TECH_LTE);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(true), curValue);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetProvisioningStatus_ExistXml() {
+        // Set MMTEL LTE VOICE to STATUS_PROVISIONED, MMTEL LTE VIDEO to STATUS_NOT_PROVISIONED
+        String[] info =
+                new String[]{IMS_FEATURE_MMTEL + "," + TECH_LTE + "," + CAPA_VOICE + "," + getInt(
+                        true),
+                        IMS_FEATURE_MMTEL + "," + TECH_LTE + "," + CAPA_VIDEO + "," + getInt(
+                                false)};
+        mImsProvisioningLoader.setProvisioningToXml(SUB_ID_1, new PersistableBundle(), info);
+
+        int curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VOICE, TECH_LTE);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(true), curValue);
+
+        curValue = mImsProvisioningLoader.getProvisioningStatus(SUB_ID_1,
+                IMS_FEATURE_MMTEL, CAPA_VIDEO, TECH_LTE);
+        assertEquals(getXmlContents(SUB_ID_1), getInt(false), curValue);
+    }
+
+    private boolean getBooleanFromProvisioningStatus(int subId, int imsFeature, int capa,
+            int tech) {
+        // Return provisioning status to bool
+        return mImsProvisioningLoader.getProvisioningStatus(
+                subId, imsFeature, capa, tech) == STATUS_PROVISIONED ? true
+                : false;
+    }
+
+    private int getInt(boolean isProvisioned) {
+        return isProvisioned ? STATUS_PROVISIONED : STATUS_NOT_PROVISIONED;
+    }
+
+    private void deleteXml(int subId, Context context) {
+        String fileName = getFileName(subId);
+        File file = null;
+        try {
+            file = new File(context.getFilesDir(), fileName);
+        } catch (Exception e) {
+            logd(e.toString());
+        }
+        file.delete();
+    }
+
+    private String getXmlContents(int subId) {
+        String fileName = getFileName(subId);
+
+        File file = null;
+        FileInputStream inFile = null;
+        StringBuilder readString = new StringBuilder();
+        readString.append("file name " + fileName + "\n");
+        byte[] buffer = new byte[1024];
+        int n = 0;
+        try {
+            file = new File(mContext.getFilesDir(), fileName);
+            inFile = new FileInputStream(file);
+            while ((n = inFile.read(buffer)) != -1) {
+                readString.append(new String(buffer, 0, n));
+            }
+            inFile.close();
+        } catch (FileNotFoundException e) {
+            logd(e.toString());
+
+        } catch (IOException e) {
+            logd(e.toString());
+        }
+        return readString.toString();
+    }
+
+    private String getFileName(int subId) {
+        // Resulting name is imsprovisioningstatus_{subId}.xml
+        return "imsprovisioningstatus_" + subId + ".xml";
+    }
+
+    private static void logd(String contents) {
+        Log.d(LOG_TAG, contents);
+    }
+
+}
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
index c493f6b..e4cdc6e 100644
--- a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -243,7 +243,7 @@
         verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
         verify(mCallback0, times(0)).onAvailable();
 
-        mMmTelConnectorListenerSlot0.getValue().connectionReady(null);
+        mMmTelConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, atLeastOnce()).onAvailable();
 
@@ -344,7 +344,7 @@
         processAllMessages();
         verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
 
-        mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+        mRcsConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(0)).onAvailable();
 
@@ -368,7 +368,7 @@
         processAllMessages();
         verify(mCallback0, times(1)).onAvailable();
 
-        mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+        mRcsConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(2)).onAvailable();
 
@@ -398,7 +398,7 @@
         processAllMessages();
         verify(mCallback0, times(0)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
 
-        mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+        mRcsConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(0)).onAvailable();
 
@@ -693,7 +693,7 @@
         verify(mCallback3, times(2)).onUnavailable(anyInt());
 
         // connectionReady
-        mMmTelConnectorListenerSlot0.getValue().connectionReady(null);
+        mMmTelConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(1)).onAvailable();
         verify(mCallback1, times(0)).onAvailable();
@@ -704,7 +704,7 @@
         verify(mCallback2, times(2)).onUnavailable(anyInt());
         verify(mCallback3, times(2)).onUnavailable(anyInt());
 
-        mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+        mRcsConnectorListenerSlot0.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(1)).onAvailable();
         verify(mCallback1, times(0)).onAvailable();
@@ -726,7 +726,7 @@
         verify(mCallback2, times(2)).onUnavailable(anyInt());
         verify(mCallback3, times(2)).onUnavailable(anyInt());
 
-        mMmTelConnectorListenerSlot1.getValue().connectionReady(null);
+        mMmTelConnectorListenerSlot1.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(1)).onAvailable();
         verify(mCallback1, times(1)).onAvailable();
@@ -737,7 +737,7 @@
         verify(mCallback2, times(2)).onUnavailable(anyInt());
         verify(mCallback3, times(2)).onUnavailable(anyInt());
 
-        mRcsConnectorListenerSlot1.getValue().connectionReady(null);
+        mRcsConnectorListenerSlot1.getValue().connectionReady(null, SLOT_0_SUB_ID);
         processAllMessages();
         verify(mCallback0, times(1)).onAvailable();
         verify(mCallback1, times(1)).onAvailable();
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index 8e5e073..57f9f6b 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -16,6 +16,10 @@
 
 package com.android.phone;
 
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
@@ -64,6 +68,7 @@
 import com.android.ims.FeatureConnector;
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.metrics.RcsStats;
 
 import org.junit.After;
 import org.junit.Before;
@@ -147,6 +152,7 @@
     private MockContentResolver mContentResolver = new MockContentResolver();
     private SimInfoContentProvider mProvider;
     private BroadcastReceiver mReceiver;
+    private static final int TEST_SUB_ID = 1;
     @Mock
     private Cursor mCursor;
     @Mock
@@ -179,6 +185,10 @@
     private IRcsConfigCallback mCallback;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private RcsStats mRcsStats;
+    @Mock
+    private RcsStats.RcsProvisioningCallback mRcsProvisioningCallback;
 
     private Executor mExecutor = new Executor() {
         @Override
@@ -768,6 +778,66 @@
         assertNull(mRcsProvisioningMonitor.getImsFeatureValidationOverride(FAKE_SUB_ID_BASE));
     }
 
+    @Test
+    @SmallTest
+    public void testMetricsAcsNotUsed() throws Exception {
+        createMonitor(1);
+
+        // Not used ACS
+        mBundle.putBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL, false);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+        verify(mRcsStats, never()).onRcsAcsProvisioningStats(anyInt(), anyInt(),
+                anyInt(), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testMetricsAcsUsed() throws Exception {
+        when(mRcsStats.getRcsProvisioningCallback(anyInt(), anyBoolean()))
+                .thenReturn(mRcsProvisioningCallback);
+        createMonitor(1);
+
+        verify(mIImsConfig, times(1))
+                .notifyRcsAutoConfigurationReceived(any(), anyBoolean());
+        // verify RcsStats.getRcsProvisioningCallback() is called
+        verify(mRcsStats, times(1)).getRcsProvisioningCallback(
+                eq(FAKE_SUB_ID_BASE), anyBoolean());
+        // verify registered callback obj which comes from RcsStats.getRcsProvisioningCallback()
+        verify(mIImsConfig, times(1))
+                .addRcsConfigCallback(eq(mRcsProvisioningCallback));
+
+        // Config data received and ACS used
+        int errorCode = 200;
+        mBundle.putBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+        verify(mRcsStats, times(1)).onRcsAcsProvisioningStats(eq(FAKE_SUB_ID_BASE), eq(errorCode),
+                eq(RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testMetricsClientProvisioningStats() throws Exception {
+        createMonitor(1);
+
+        // reconfig trigger
+        mRcsProvisioningMonitor.requestReconfig(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        verify(mRcsStats, times(1)).onRcsClientProvisioningStats(eq(FAKE_SUB_ID_BASE),
+                eq(RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION));
+
+        // DMA changed
+        updateDefaultMessageApplication(DEFAULT_MESSAGING_APP2);
+        processAllMessages();
+        verify(mRcsStats, times(1)).onRcsClientProvisioningStats(eq(FAKE_SUB_ID_BASE),
+                eq(RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED));
+    }
+
     private void createMonitor(int subCount) throws Exception {
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -777,14 +847,14 @@
                 .thenReturn(mFeatureConnector);
         when(mFeatureManager.getConfig()).thenReturn(mIImsConfig);
         mRcsProvisioningMonitor = new RcsProvisioningMonitor(mPhone, mHandlerThread.getLooper(),
-                mRoleManager, mFeatureFactory);
+                mRoleManager, mFeatureFactory, mRcsStats);
         mHandler = mRcsProvisioningMonitor.getHandler();
         try {
             mLooper = new TestableLooper(mHandler.getLooper());
         } catch (Exception e) {
             logd("Unable to create looper from handler.");
         }
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
 
         verify(mFeatureConnector, atLeastOnce()).connect();
     }
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
index 532b1c0..4bbde79 100644
--- a/tests/src/com/android/phone/ServiceStateProviderTest.java
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -28,6 +28,7 @@
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
 
 import static com.android.phone.ServiceStateProvider.ENFORCE_LOCATION_PERMISSION_CHECK;
+import static com.android.phone.ServiceStateProvider.NETWORK_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,6 +41,8 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
@@ -332,11 +335,8 @@
         setCanReadPrivilegedPhoneState(true);
         setLocationPermissions(false);
 
-        // NETWORK_ID is a location-sensitive column
-        String[] projection = new String[]{"network_id"};
-
         assertThrows(SecurityException.class,
-                () -> verifyServiceStateWithLocationColumns(mTestServiceState, projection));
+                () -> verifyServiceStateWithLocationColumns(mTestServiceState));
     }
 
     /**
@@ -345,7 +345,7 @@
      */
     @Test
     @CoreCompatChangeRule.EnableCompatChanges({ENFORCE_LOCATION_PERMISSION_CHECK})
-    public void query_allColumn_enforceLoationEnabled_targetR_withLocation_getUnredacted() {
+    public void query_allColumn_enforceLocationEnabled_targetR_withLocation_getUnredacted() {
         setTargetSdkVersion(Build.VERSION_CODES.R);
         setLocationPermissions(true);
 
@@ -402,9 +402,24 @@
                 mTestServiceState, true /*hasPermission*/);
     }
 
-    private void verifyServiceStateWithLocationColumns(ServiceState ss, String[] projection) {
-        try (Cursor cursor = mContentResolver.query(ServiceStateTable.CONTENT_URI, projection, null,
-                null)) {
+    /**
+     * Verify that when caller with targetSDK S+ has location permission and try to query
+     * location non-sensitive info, it should not get blamed.
+     */
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ENFORCE_LOCATION_PERMISSION_CHECK})
+    public void testQuery_noLocationBlamed_whenQueryNonLocationInfo_withPermission() {
+        setTargetSdkVersion(Build.VERSION_CODES.S);
+        setLocationPermissions(true);
+
+        verifyServiceStateWithPublicColumns(mTestServiceState, null /*projection*/);
+        verify(mAppOpsManager, never()).noteOpNoThrow(any(), anyInt(), any(), any(), any());
+    }
+
+    private void verifyServiceStateWithLocationColumns(ServiceState ss) {
+        // NETWORK_ID is a location-sensitive column
+        try (Cursor cursor = mContentResolver.query(ServiceStateTable.CONTENT_URI,
+                new String[]{NETWORK_ID}, null, null)) {
             assertNotNull(cursor);
         }
     }
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 3bc5ee8..9d2f5ac 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -622,6 +622,46 @@
     }
 
     /**
+     * Tests a scenario where a handover connection arrives via
+     * {@link TelephonyConnection#onOriginalConnectionRedialed(
+     * com.android.internal.telephony.Connection)}.  During this process, the conference properties
+     * get updated.  Since the original connection is null at this point, we need to verify that
+     * the remotely hosted property is retained from before the original connection was nulled.
+     */
+    @Test
+    public void testIsConferenceRemotelyHostedCachingOnSRVCC() {
+        mConferenceHost.setIsImsConnection(true);
+        when(mConferenceHost.getMockImsPhoneConnection().isMultiparty()).thenReturn(true);
+        when(mConferenceHost.getMockImsPhoneConnection().isConferenceHost()).thenReturn(true);
+
+        // Start out with a valid conference host.
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
+
+        // By default it is not remotely hosted.
+        assertFalse(imsConference.isRemotelyHosted());
+        assertEquals(0,
+                imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+
+        // Simulate a change to the original connection due to srvcc
+        com.android.internal.telephony.Connection previousOriginalConnection =
+                mConferenceHost.getMockImsPhoneConnection();
+        mConferenceHost.setMockImsPhoneConnection(null);
+
+        // Trigger the property update which takes place when the original connection changes.
+        mConferenceHost.getTelephonyConnectionListeners().forEach(
+                l -> l.onConnectionPropertiesChanged(mConferenceHost,
+                        mConferenceHost.getConnectionProperties()));
+
+        // Should still NOT be remotely hosted based on cached value.
+        assertFalse(imsConference.isRemotelyHosted());
+        assertEquals(0,
+                imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+    }
+
+    /**
      * Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
      * conference event package identities.
      */
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index cfdc2fd..b7fe988 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -23,9 +23,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.net.Uri;
 import android.os.Looper;
 import android.telecom.Conference;
 import android.telecom.Connection;
+import android.telecom.PhoneAccount;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
@@ -35,7 +37,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 
 /**
  * Tests the functionality in TelephonyConferenceController.java
@@ -111,6 +115,46 @@
     }
 
     /**
+     * Verify the connection with "Conference Call" with PROPERTY_IS_DOWNGRADED_CONFERENCE
+     * during SRVCC
+     */
+    @Test
+    @SmallTest
+    public void testSrvccConferenceConnection() {
+        when(mTestTelephonyConnectionA.mMockRadioConnection.getCall()
+                .isMultiparty()).thenReturn(true);
+        when(mTestTelephonyConnectionB.mMockRadioConnection.getCall()
+                .isMultiparty()).thenReturn(true);
+
+        List<Connection> listConnections = Arrays.asList(
+                mTestTelephonyConnectionA, mTestTelephonyConnectionB);
+        when(mMockTelephonyConnectionServiceProxy.getAllConnections()).thenReturn(listConnections);
+
+        mTestTelephonyConnectionA.setAddress(
+                Uri.fromParts(PhoneAccount.SCHEME_TEL, "Conference Call", null), 0);
+        mTestTelephonyConnectionA.setTelephonyConnectionProperties(
+                Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+        mTestTelephonyConnectionB.setAddress(
+                Uri.fromParts(PhoneAccount.SCHEME_TEL, "5551213", null), 0);
+        mTestTelephonyConnectionB.setTelephonyConnectionProperties(
+                Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+
+        // add telephony connection B
+        mControllerTest.add(mTestTelephonyConnectionB);
+
+        // add telephony connection A
+        mControllerTest.add(mTestTelephonyConnectionA);
+
+        // verify the connection with "Conference Call" has PROPERTY_IS_DOWNGRADED_CONFERENCE
+        assertTrue((mTestTelephonyConnectionA.getConnectionProperties()
+                & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+
+        // verify the connection with "5551213" hasn't PROPERTY_IS_DOWNGRADED_CONFERENCE
+        assertFalse((mTestTelephonyConnectionB.getConnectionProperties()
+                & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+    }
+
+    /**
      * Behavior: add telephony connection B and A to conference controller,
      *           set status for connections and merged calls, remove one call
      * Assumption: after performing the behaviours, the status of Connection A is STATE_ACTIVE;
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 4ce94ca..efa906e 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -61,8 +61,8 @@
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 817e9a7..e149d3b 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -305,4 +305,12 @@
     public PersistableBundle getCarrierConfigBundle() {
         return mCarrierConfig;
     }
+
+    public ImsPhoneConnection getMockImsPhoneConnection() {
+        return mImsPhoneConnection;
+    }
+
+    public void setMockImsPhoneConnection(ImsPhoneConnection connection) {
+        mImsPhoneConnection = connection;
+    }
 }
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index da614fc..2a30e1a 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -96,7 +96,7 @@
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
         verify(mMockFeature).onRcsDisconnected();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
 
         verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         verify(mFeatureManager).registerImsRegistrationCallback(any());
@@ -114,7 +114,7 @@
     public void testFeatureManagerConnectedAddRemoveFeature() throws Exception {
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
         verify(mMockFeature).onRcsConnected(mFeatureManager);
@@ -131,7 +131,7 @@
         IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
         IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
 
         try {
             controller.registerImsRegistrationCallback(TEST_SUB_ID, regCb);
@@ -165,7 +165,7 @@
     public void testFeatureManagerConnectedHelper() throws Exception {
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
         ArgumentCaptor<IImsRegistrationCallback> captor =
                 ArgumentCaptor.forClass(IImsRegistrationCallback.class);
         verify(mFeatureManager).registerImsRegistrationCallback(captor.capture());
@@ -257,7 +257,7 @@
     public void testCarrierConfigChanged() throws Exception {
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
         verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
@@ -272,7 +272,7 @@
     public void testChangeSubId() throws Exception {
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
         verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
@@ -286,7 +286,7 @@
     public void testDestroy() throws Exception {
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
-        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
         controller.destroy();