Add UT capability detection and caching
1) add the ability to set/query IMS provisioning
status for MMTEL capabilities.
2) Add sharedprefs cache for UT provisioning, since
it does not use an ImsConfig value internally in
the modem. This cache will be used by ImsManager
to determine whether to enable/disable the UT MMTEL
capability when the carrier config changes. Whether
or not we use the cache is determined by the new
ut provisioning required carrier config.
3) Do not throw IllegalArgumentException if the
subscription no longer is active. Instead, just
return error codes for Provisioning.
Bug: 119896853
Test: atest FrameworksTelephonyTests
Change-Id: If99b459dd4f08bde0059b2fd14a2a2c132cadf30
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 0135330..ad17964 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -86,6 +86,7 @@
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
+import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -93,6 +94,8 @@
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -255,6 +258,7 @@
private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
private static final String PREF_CARRIERS_SUBSCRIBER_PREFIX = "carrier_subscriber_";
+ private static final String PREF_PROVISION_IMS_MMTEL_PREFIX = "provision_ims_mmtel_";
// The AID of ISD-R.
private static final String ISDR_AID = "A0000005591010FFFFFFFF8900000100";
@@ -3050,15 +3054,249 @@
}
@Override
+ public void setImsProvisioningStatusForCapability(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");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("setImsProvisioningStatusForCapability: phone instance null for subid "
+ + subId);
+ return;
+ }
+ if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ 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(capability, tech, isProvisioned);
+ } catch (ImsException e) {
+ loge("setImsProvisioningStatusForCapability: couldn't change UT capability"
+ + ", Exception" + e.getMessage());
+ }
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Tried to set provisioning for capability '"
+ + capability + "', which does not require provisioning.");
+ }
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean getImsProvisioningStatusForCapability(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");
+ }
+ enforceReadPrivilegedPermission("getProvisioningStatusForCapability");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("getImsProvisioningStatusForCapability: phone instance null for subid "
+ + subId);
+ // We will fail with "true" as the provisioning status because this is the default
+ // if we do not require provisioning.
+ return true;
+ }
+
+ if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ 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 capability '"
+ + capability + "', which does not require provisioning.");
+ }
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @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");
+ }
+ 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));
+ }
+ }
+
+ /**
+ * @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();
+ }
+
+ private static String getMmTelProvisioningKey(int subId, int tech) {
+ // resulting key is provision_ims_mmtel_{subId}_{tech}
+ return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech;
+ }
+
+ /**
+ * Query CarrierConfig to see if the specified capability requires provisioning for the
+ * carrier associated with the subscription id.
+ */
+ private boolean doesImsCapabilityRequireProvisioning(Context context, int subId,
+ int capability) {
+ CarrierConfigManager configManager = new CarrierConfigManager(context);
+ PersistableBundle c = configManager.getConfigForSubId(subId);
+ boolean requireUtProvisioning = c.getBoolean(
+ // By default, this config is true (even if there is no SIM). We also check to make
+ // sure the subscription needs provisioning here, so we do not need to check for
+ // the no-SIM case, where we would normally shortcut this to false.
+ CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, true)
+ && c.getBoolean(CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL,
+ false);
+ boolean requireVoiceVtProvisioning = c.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+
+ // First check to make sure that the capability requires provisioning.
+ switch (capability) {
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+ // intentional fallthrough
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
+ if (requireVoiceVtProvisioning) {
+ // Voice and Video requires provisioning
+ return true;
+ }
+ break;
+ }
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
+ if (requireUtProvisioning) {
+ // UT requires provisioning
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
public int getImsProvisioningInt(int subId, int key) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+ }
enforceReadPrivilegedPermission("getImsProvisioningInt");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- return ImsManager.getInstance(mApp,
- getSlotIndexOrException(subId)).getConfigInterface().getConfigInt(key);
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "getImsProvisioningInt: called with an inactive subscription '"
+ + subId + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+ }
+ return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigInt(key);
} catch (ImsException e) {
- throw new IllegalArgumentException(e.getMessage());
+ Log.w(LOG_TAG, "getImsProvisioningInt: ImsService is not available for subscription '"
+ + subId + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3066,14 +3304,24 @@
@Override
public String getImsProvisioningString(int subId, int key) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+ }
enforceReadPrivilegedPermission("getImsProvisioningString");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- return ImsManager.getInstance(mApp,
- getSlotIndexOrException(subId)).getConfigInterface().getConfigString(key);
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "getImsProvisioningString: called for an inactive subscription id '"
+ + subId + "' for key:" + key);
+ return ProvisioningManager.STRING_QUERY_RESULT_ERROR_GENERIC;
+ }
+ return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigString(key);
} catch (ImsException e) {
- throw new IllegalArgumentException(e.getMessage());
+ Log.w(LOG_TAG, "getImsProvisioningString: ImsService is not available for sub '"
+ + subId + "' for key:" + key);
+ return ProvisioningManager.STRING_QUERY_RESULT_ERROR_NOT_READY;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3081,15 +3329,25 @@
@Override
public int setImsProvisioningInt(int subId, int key, int value) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+ }
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
"setImsProvisioningInt");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- return ImsManager.getInstance(mApp,
- getSlotIndexOrException(subId)).getConfigInterface().setConfig(key, value);
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "setImsProvisioningInt: called with an inactive subscription id '"
+ + subId + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+ }
+ return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
} catch (ImsException e) {
- throw new IllegalArgumentException(e.getMessage());
+ Log.w(LOG_TAG, "setImsProvisioningInt: ImsService unavailable for sub '" + subId
+ + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_FAILED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3097,15 +3355,25 @@
@Override
public int setImsProvisioningString(int subId, int key, String value) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+ }
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
"setImsProvisioningString");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- return ImsManager.getInstance(mApp,
- getSlotIndexOrException(subId)).getConfigInterface().setConfig(key, value);
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "setImsProvisioningString: called with an inactive subscription id '"
+ + subId + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+ }
+ return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
} catch (ImsException e) {
- throw new IllegalArgumentException(e.getMessage());
+ Log.w(LOG_TAG, "setImsProvisioningString: ImsService unavailable for sub '" + subId
+ + "' for key:" + key);
+ return ImsConfigImplBase.CONFIG_RESULT_FAILED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3119,6 +3387,14 @@
return slotId;
}
+ private int getSlotIndex(int subId) {
+ int slotId = SubscriptionManager.getSlotIndex(subId);
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ }
+ return slotId;
+ }
+
/**
* Returns the network type for a subId
*/