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();