Merge "Remap call diagnostic service constants to internal D2D constants" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 60d5986..ce56d92 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -97,6 +97,9 @@
<protected-broadcast android:name= "android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_RESET" />
+ <protected-broadcast android:name= "com.android.phone.settings.CARRIER_PROVISIONING" />
+ <protected-broadcast android:name= "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING" />
+
<!-- For Vendor Debugging in Telephony -->
<protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e1eacbd..63390c8 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -780,7 +780,7 @@
<string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"تعذر إكمال المكالمة. رمز الخطأ 111."</string>
<string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"تعذر إكمال المكالمة. رمز الخطأ 127."</string>
<string name="labelCallBarring" msgid="4180377113052853173">"منع الاتصال"</string>
- <string name="sum_call_barring_enabled" msgid="5184331188926370824">"تفعيل"</string>
+ <string name="sum_call_barring_enabled" msgid="5184331188926370824">"مفعّلة"</string>
<string name="sum_call_barring_disabled" msgid="5699448000600153096">"متوقف"</string>
<string name="call_barring_baoc" msgid="7400892586336429326">"كل المكالمات الصادرة"</string>
<string name="call_barring_baoc_enabled" msgid="3131509193386668182">"هل تريد إيقاف حظر كل المكالمات الصادرة؟"</string>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 831b537..de41309 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -247,6 +247,12 @@
}
case EVENT_DO_FETCH_DEFAULT: {
+ // Clear in-memory cache for carrier app config, so when carrier app gets
+ // uninstalled, no stale config is left.
+ if (mConfigFromCarrierApp[phoneId] != null
+ && getCarrierPackageForPhoneId(phoneId) == null) {
+ mConfigFromCarrierApp[phoneId] = null;
+ }
// Restore persistent override values.
PersistableBundle config = restoreConfigFromXml(
mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
@@ -1142,12 +1148,6 @@
* have a saved config file to use instead.
*/
private void updateConfigForPhoneId(int phoneId) {
- // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no
- // stale config is left.
- if (mConfigFromCarrierApp[phoneId] != null &&
- getCarrierPackageForPhoneId(phoneId) == null) {
- mConfigFromCarrierApp[phoneId] = null;
- }
mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
}
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 5e616b7..ad33302 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -28,6 +28,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.DelegateRequest;
import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter.PublishState;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -56,6 +57,7 @@
import com.android.services.telephony.rcs.UceControllerManager;
import java.util.List;
+import java.util.Set;
/**
* Implementation of the IImsRcsController interface.
@@ -336,6 +338,82 @@
}
}
+ /**
+ * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+ Set<String> featureTags) throws ImsException {
+ // Permission check happening in PhoneInterfaceManager.
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ return null;
+ }
+ return uceCtrlManager.addUceRegistrationOverride(featureTags);
+ }
+
+ /**
+ * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+ Set<String> featureTags) throws ImsException {
+ // Permission check happening in PhoneInterfaceManager.
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ return null;
+ }
+ return uceCtrlManager.removeUceRegistrationOverride(featureTags);
+ }
+
+ /**
+ * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId)
+ throws ImsException {
+ // Permission check happening in PhoneInterfaceManager.
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ return null;
+ }
+ return uceCtrlManager.clearUceRegistrationOverride();
+ }
+
+ /**
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId)
+ throws ImsException {
+ // Permission check happening in PhoneInterfaceManager.
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ return null;
+ }
+ return uceCtrlManager.getLatestRcsContactUceCapability();
+ }
+
+ /**
+ * @return the PIDf XML used in the last PUBLISH procedure or "none" if the device is not
+ * published. Returns {@code null} if the operation failed due to an error.
+ */
+ // Used for SHELL command only right now.
+ public String getLastUcePidfXmlShell(int subId) throws ImsException {
+ // Permission check happening in PhoneInterfaceManager.
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ return null;
+ }
+ String pidfXml = uceCtrlManager.getLastPidfXml();
+ return pidfXml == null ? "none" : pidfXml;
+ }
+
@Override
public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
enforceReadPrivilegedPermission("registerUcePublishStateCallback");
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 2c282f7..702dd7f 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -70,7 +70,6 @@
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.ThermalMitigationResult;
import android.telephony.CallForwardingInfo;
-import android.telephony.CarrierBandwidth;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
@@ -114,6 +113,7 @@
import android.telephony.ims.ImsException;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
@@ -6153,7 +6153,7 @@
@TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "enableNRDualConnectivity");
- if (isRadioInterfaceCapabilitySupported(
+ if (!isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
}
@@ -6180,7 +6180,7 @@
TelephonyPermissions
.enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
mApp, subId, "isNRDualConnectivityEnabled");
- if (isRadioInterfaceCapabilitySupported(
+ if (!isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
return false;
}
@@ -6197,28 +6197,6 @@
}
/**
- * get carrier bandwidth per primary and secondary carrier
- * @param subId subscription id of the sim card
- * @return CarrierBandwidth with bandwidth of both primary and secondary carrier..
- */
- @Override
- public CarrierBandwidth getCarrierBandwidth(int subId) {
- TelephonyPermissions
- .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mApp, subId, "isNRDualConnectivityEnabled");
- WorkSource workSource = getWorkSource(Binder.getCallingUid());
- final long identity = Binder.clearCallingIdentity();
- try {
- CarrierBandwidth carrierBandwidth =
- getPhoneFromSubId(subId).getCarrierBandwidth();
- if (DBG) log("getCarrierBandwidth: " + carrierBandwidth);
- return carrierBandwidth;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
* Set the allowed network types of the device and
* provide the reason triggering the allowed network change.
*
@@ -10130,6 +10108,104 @@
}
}
+ /**
+ * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ @Override
+ public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+ List<String> featureTags) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+ "addUceRegistrationOverrideShell");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.imsRcsController.addUceRegistrationOverrideShell(subId,
+ new ArraySet<>(featureTags));
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ @Override
+ public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+ List<String> featureTags) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+ "removeUceRegistrationOverrideShell");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.imsRcsController.removeUceRegistrationOverrideShell(subId,
+ new ArraySet<>(featureTags));
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ @Override
+ public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+ "clearUceRegistrationOverrideShell");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.imsRcsController.clearUceRegistrationOverrideShell(subId);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ @Override
+ public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+ "getLatestRcsContactUceCapabilityShell");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.imsRcsController.getLatestRcsContactUceCapabilityShell(subId);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+ * device does not have an active PUBLISH.
+ */
+ // Used for SHELL command only right now.
+ @Override
+ public String getLastUcePidfXmlShell(int subId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceGetLastPidfXml");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.imsRcsController.getLastUcePidfXmlShell(subId);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+
@Override
public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request,
String callingPackage) {
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 47666ef..07136d7 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -28,15 +28,21 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.provider.BlockedNumberContract;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.feature.ImsFeature;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.ims.rcs.uce.util.FeatureTags;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
@@ -48,9 +54,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -124,6 +133,8 @@
private static final String UCE_REMOVE_EAB_CONTACT = "remove-eab-contact";
private static final String UCE_GET_DEVICE_ENABLED = "get-device-enabled";
private static final String UCE_SET_DEVICE_ENABLED = "set-device-enabled";
+ private static final String UCE_OVERRIDE_PUBLISH_CAPS = "override-published-caps";
+ private static final String UCE_GET_LAST_PIDF_XML = "get-last-publish-pidf";
// Check if a package has carrier privileges on any SIM, regardless of subId/phoneId.
private static final String HAS_CARRIER_PRIVILEGES_COMMAND = "has-carrier-privileges";
@@ -178,6 +189,48 @@
}
};
+ /**
+ * Map from a shorthand string to the feature tags required in registration required in order
+ * for the RCS feature to be considered "capable".
+ */
+ private static final Map<String, Set<String>> TEST_FEATURE_TAG_MAP;
+ static {
+ ArrayMap<String, Set<String>> map = new ArrayMap<>(18);
+ map.put("chat_v1", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_IM));
+ map.put("chat_v2", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_SESSION));
+ map.put("ft", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER));
+ map.put("ft_sms", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS));
+ map.put("mmtel", Collections.singleton(FeatureTags.FEATURE_TAG_MMTEL));
+ map.put("mmtel_vt", new ArraySet<>(Arrays.asList(FeatureTags.FEATURE_TAG_MMTEL,
+ FeatureTags.FEATURE_TAG_VIDEO)));
+ map.put("geo_push", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH));
+ map.put("geo_push_sms", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS));
+ map.put("call_comp",
+ Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING));
+ map.put("call_comp_mmtel",
+ Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY));
+ map.put("call_post", Collections.singleton(FeatureTags.FEATURE_TAG_POST_CALL));
+ map.put("map", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_MAP));
+ map.put("sketch", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_SKETCH));
+ // Feature tags defined twice for chatbot session because we want v1 and v2 based on bot
+ // version
+ map.put("chatbot", new ArraySet<>(Arrays.asList(
+ FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
+ 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)));
+ 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)));
+ map.put("chatbot_role", Collections.singleton(FeatureTags.FEATURE_TAG_CHATBOT_ROLE));
+ TEST_FEATURE_TAG_MAP = Collections.unmodifiableMap(map);
+ }
+
+
public TelephonyShellCommand(ITelephony binder, Context context) {
mInterface = binder;
mCarrierConfigManager =
@@ -346,6 +399,22 @@
pw.println(" uce set-device-enabled true|false");
pw.println(" Set the device config for RCS User Capability Exchange to the value.");
pw.println(" The value could be true, false.");
+ pw.println(" uce override-published-caps [-s SLOT_ID] add|remove|clear [CAPABILITIES]");
+ pw.println(" Override the existing SIP PUBLISH with different capabilities.");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to read carrier config value for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ pw.println(" add [CAPABILITY]: add a new capability");
+ pw.println(" remove [CAPABILITY]: remove a capability");
+ pw.println(" clear: clear all capability overrides");
+ pw.println(" CAPABILITY: \":\" separated list of capabilities.");
+ pw.println(" Valid options are: [mmtel(_vt), chat_v1, chat_v2, ft, ft_sms,");
+ pw.println(" geo_push, geo_push_sms, call_comp, call_post, map, sketch, chatbot,");
+ pw.println(" chatbot_sa, chatbot_role] as well as full length");
+ pw.println(" featureTag=\"featureValue\" feature tags that are not defined here.");
+ pw.println(" uce get-last-publish-pidf [-s SLOT_ID]");
+ pw.println(" Get the PIDF XML included in the last SIP PUBLISH, or \"none\" if no ");
+ pw.println(" PUBLISH is active");
}
private void onHelpNumberVerification() {
@@ -1709,8 +1778,8 @@
private int handleRcsUceCommand() {
String arg = getNextArg();
if (arg == null) {
- Log.w(LOG_TAG, "cannot get uce parameter");
- return -1;
+ onHelpUce();
+ return 0;
}
switch (arg) {
@@ -1722,6 +1791,10 @@
return handleUceGetDeviceEnabledCommand();
case UCE_SET_DEVICE_ENABLED:
return handleUceSetDeviceEnabledCommand();
+ case UCE_OVERRIDE_PUBLISH_CAPS:
+ return handleUceOverridePublishCaps();
+ case UCE_GET_LAST_PIDF_XML:
+ return handleUceGetPidfXml();
}
return -1;
}
@@ -1842,6 +1915,101 @@
return 0;
}
+ private int handleUceOverridePublishCaps() {
+ int subId = getSubId("uce override-published-caps");
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return -1;
+ }
+ //uce override-published-caps [-s SLOT_ID] add|remove|clear|list [CAPABILITIES]
+ String operation = getNextArgRequired();
+ String caps = getNextArg();
+ if (!"add".equals(operation) && !"remove".equals(operation) && !"clear".equals(operation)
+ && !"list".equals(operation)) {
+ getErrPrintWriter().println("Invalid operation: " + operation);
+ return -1;
+ }
+
+ // add/remove requires capabilities to be specified.
+ if ((!"clear".equals(operation) && !"list".equals(operation)) && TextUtils.isEmpty(caps)) {
+ getErrPrintWriter().println("\"" + operation + "\" requires capabilities to be "
+ + "specified");
+ return -1;
+ }
+
+ ArraySet<String> capSet = new ArraySet<>();
+ if (!TextUtils.isEmpty(caps)) {
+ String[] capArray = caps.split(":");
+ for (String cap : capArray) {
+ // Allow unknown tags to be passed in as well.
+ capSet.addAll(TEST_FEATURE_TAG_MAP.getOrDefault(cap, Collections.singleton(cap)));
+ }
+ }
+
+ RcsContactUceCapability result = null;
+ try {
+ switch (operation) {
+ case "add":
+ result = mInterface.addUceRegistrationOverrideShell(subId,
+ new ArrayList<>(capSet));
+ break;
+ case "remove":
+ result = mInterface.removeUceRegistrationOverrideShell(subId,
+ new ArrayList<>(capSet));
+ break;
+ case "clear":
+ result = mInterface.clearUceRegistrationOverrideShell(subId);
+ break;
+ case "list":
+ result = mInterface.getLatestRcsContactUceCapabilityShell(subId);
+ break;
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "uce override-published-caps, error " + e.getMessage());
+ getErrPrintWriter().println("Exception: " + e.getMessage());
+ return -1;
+ } catch (ServiceSpecificException sse) {
+ // Reconstruct ImsException
+ ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+ Log.w(LOG_TAG, "uce override-published-caps, error " + imsException);
+ getErrPrintWriter().println("Exception: " + imsException);
+ return -1;
+ }
+ if (result == null) {
+ getErrPrintWriter().println("Service not available");
+ return -1;
+ }
+ getOutPrintWriter().println(result);
+ return 0;
+ }
+
+ private int handleUceGetPidfXml() {
+ int subId = getSubId("uce get-last-publish-pidf");
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return -1;
+ }
+
+ String result;
+ try {
+ result = mInterface.getLastUcePidfXmlShell(subId);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "uce get-last-publish-pidf, error " + e.getMessage());
+ getErrPrintWriter().println("Exception: " + e.getMessage());
+ return -1;
+ } catch (ServiceSpecificException sse) {
+ // Reconstruct ImsException
+ ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+ Log.w(LOG_TAG, "uce get-last-publish-pidf error " + imsException);
+ getErrPrintWriter().println("Exception: " + imsException);
+ return -1;
+ }
+ if (result == null) {
+ getErrPrintWriter().println("Service not available");
+ return -1;
+ }
+ getOutPrintWriter().println(result);
+ return 0;
+ }
+
private int handleSrcSetDeviceEnabledCommand() {
String enabledStr = getNextArg();
if (enabledStr == null) {
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureManager.java b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
index 3c9e27e..818994a 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureManager.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
@@ -20,6 +20,7 @@
import android.location.Location;
import android.net.Uri;
import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.CallLog;
import android.telephony.CarrierConfigManager;
@@ -33,14 +34,12 @@
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.callcomposer.CallComposerPictureTransfer.PictureCallback;
import com.android.phone.R;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
-import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@@ -53,6 +52,7 @@
public class CallComposerPictureManager {
private static final String TAG = CallComposerPictureManager.class.getSimpleName();
private static final SparseArray<CallComposerPictureManager> sInstances = new SparseArray<>();
+ private static final String THREE_GPP_BOOTSTRAPPING = "3GPP-bootstrapping";
public static CallComposerPictureManager getInstance(Context context, int subscriptionId) {
synchronized (sInstances) {
@@ -104,7 +104,7 @@
private final HashMap<UUID, String> mCachedServerUrls = new HashMap<>();
private final HashMap<UUID, ImageData> mCachedImages = new HashMap<>();
- private final Map<String, GbaCredentials> mCachedCredentials = new HashMap<>();
+ private GbaCredentials mCachedCredentials = null;
private final int mSubscriptionId;
private final TelephonyManager mTelephonyManager;
private final Context mContext;
@@ -127,7 +127,8 @@
return;
}
- String uploadUrl = mTelephonyManager.getCarrierConfig().getString(
+ PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
+ String uploadUrl = carrierConfig.getString(
CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
if (TextUtils.isEmpty(uploadUrl)) {
Log.e(TAG, "Call composer upload URL not configured in carrier config");
@@ -141,7 +142,7 @@
mSubscriptionId, uploadUrl, sExecutorService);
AtomicBoolean hasRetried = new AtomicBoolean(false);
- transfer.setCallback(new PictureCallback() {
+ transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
@Override
public void onError(int error) {
callback.accept(Pair.create(null, error));
@@ -157,7 +158,7 @@
}
GbaCredentialsSupplier supplier =
(realm, executor) ->
- getGbaCredentials(credentialRefresh, realm, executor);
+ getGbaCredentials(credentialRefresh, carrierConfig, executor);
sExecutorService.schedule(() -> transfer.uploadPicture(imageData, supplier),
backoffMillis, TimeUnit.MILLISECONDS);
@@ -174,7 +175,7 @@
});
transfer.uploadPicture(imageData,
- (realm, executor) -> getGbaCredentials(false, realm, executor));
+ (realm, executor) -> getGbaCredentials(false, carrierConfig, executor));
}
public void handleDownloadFromServer(CallComposerPictureTransfer.Factory transferFactory,
@@ -187,11 +188,12 @@
return;
}
+ PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
CallComposerPictureTransfer transfer = transferFactory.create(mContext,
mSubscriptionId, remoteUrl, sExecutorService);
AtomicBoolean hasRetried = new AtomicBoolean(false);
- transfer.setCallback(new PictureCallback() {
+ transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
@Override
public void onError(int error) {
callback.accept(Pair.create(null, error));
@@ -207,7 +209,7 @@
}
GbaCredentialsSupplier supplier =
(realm, executor) ->
- getGbaCredentials(credentialRefresh, realm, executor);
+ getGbaCredentials(credentialRefresh, carrierConfig, executor);
sExecutorService.schedule(() -> transfer.downloadPicture(supplier),
backoffMillis, TimeUnit.MILLISECONDS);
@@ -237,7 +239,8 @@
}
});
- transfer.downloadPicture(((realm, executor) -> getGbaCredentials(false, realm, executor)));
+ transfer.downloadPicture(((realm, executor) ->
+ getGbaCredentials(false, carrierConfig, executor)));
}
public void storeUploadedPictureToCallLog(UUID id, Consumer<Uri> callback) {
@@ -298,32 +301,36 @@
}
private CompletableFuture<GbaCredentials> getGbaCredentials(
- boolean forceRefresh, String nafId, Executor executor) {
- synchronized (mCachedCredentials) {
- if (!forceRefresh && mCachedCredentials.containsKey(nafId)) {
- return CompletableFuture.completedFuture(mCachedCredentials.get(nafId));
+ boolean forceRefresh, PersistableBundle config, Executor executor) {
+ synchronized (this) {
+ if (!forceRefresh && mCachedCredentials != null) {
+ return CompletableFuture.completedFuture(mCachedCredentials);
}
+
if (forceRefresh) {
- mCachedCredentials.remove(nafId);
+ mCachedCredentials = null;
}
}
UaSecurityProtocolIdentifier securityProtocolIdentifier =
new UaSecurityProtocolIdentifier.Builder()
- .setOrg(UaSecurityProtocolIdentifier.ORG_3GPP)
- .setProtocol(UaSecurityProtocolIdentifier
- .UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION)
+ .setOrg(config.getInt(
+ CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT))
+ .setProtocol(config.getInt(
+ CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT))
+ .setTlsCipherSuite(config.getInt(
+ CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT))
.build();
CompletableFuture<GbaCredentials> resultFuture = new CompletableFuture<>();
- mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_UNKNOWN,
- Uri.parse(nafId), securityProtocolIdentifier, forceRefresh, executor,
+ mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_ISIM,
+ getNafUri(config), securityProtocolIdentifier, forceRefresh, executor,
new TelephonyManager.BootstrapAuthenticationCallback() {
@Override
public void onKeysAvailable(byte[] gbaKey, String transactionId) {
GbaCredentials creds = new GbaCredentials(transactionId, gbaKey);
- synchronized (mCachedCredentials) {
- mCachedCredentials.put(nafId, creds);
+ synchronized (CallComposerPictureManager.this) {
+ mCachedCredentials = creds;
}
resultFuture.complete(creds);
}
@@ -338,6 +345,30 @@
return resultFuture;
}
+ private static Uri getNafUri(PersistableBundle carrierConfig) {
+ String uploadUriString = carrierConfig.getString(
+ CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
+ Uri uploadUri = Uri.parse(uploadUriString);
+ String nafPrefix;
+ switch (carrierConfig.getInt(CarrierConfigManager.KEY_GBA_MODE_INT)) {
+ case CarrierConfigManager.GBA_U:
+ nafPrefix = THREE_GPP_BOOTSTRAPPING + "-uicc";
+ break;
+ case CarrierConfigManager.GBA_DIGEST:
+ nafPrefix = THREE_GPP_BOOTSTRAPPING + "-digest";
+ break;
+ case CarrierConfigManager.GBA_ME:
+ default:
+ nafPrefix = THREE_GPP_BOOTSTRAPPING;
+ }
+ String newAuthority = nafPrefix + "@" + uploadUri.getAuthority();
+ Uri nafUri = new Uri.Builder().scheme(uploadUri.getScheme())
+ .encodedAuthority(newAuthority)
+ .build();
+ Log.i(TAG, "using NAF uri " + nafUri + " for GBA");
+ return nafUri;
+ }
+
@VisibleForTesting
static ScheduledExecutorService getExecutor() {
return sExecutorService;
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
index 1a176dd..e4458cd 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
@@ -21,6 +21,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -49,6 +50,9 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -140,7 +144,10 @@
mExecutorService);
networkUrlFuture.thenAcceptAsync((result) -> {
if (result != null) mCallback.onUploadSuccessful(result);
- }, mExecutorService);
+ }, mExecutorService).exceptionally((ex) -> {
+ logException("Exception uploading image" , ex);
+ return null;
+ });
}
public void downloadPicture(GbaCredentialsSupplier credentialsSupplier) {
@@ -203,6 +210,9 @@
}
if (fromAuth != null) mCallback.onDownloadSuccessful(fromAuth);
mCallback.onDownloadSuccessful(fromImmediate);
+ }).exceptionally((ex) -> {
+ logException("Exception downloading image" , ex);
+ return null;
});
}
@@ -223,7 +233,7 @@
return resultFuture;
}
- private static HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
+ private HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
try {
HttpURLConnection connection =
(HttpURLConnection) network.openConnection(new URL(uploadUrl));
@@ -231,7 +241,7 @@
connection.setInstanceFollowRedirects(false);
connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
- connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+ connection.setRequestProperty("User-Agent", getUserAgent());
return connection;
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed URL: " + uploadUrl);
@@ -242,14 +252,14 @@
}
}
- private static HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
+ private HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
try {
HttpURLConnection connection =
(HttpURLConnection) network.openConnection(new URL(imageUrl));
connection.setRequestMethod("GET");
connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
- connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+ connection.setRequestProperty("User-Agent", getUserAgent());
return connection;
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed URL: " + imageUrl);
@@ -387,7 +397,7 @@
public void sendDispositionHeader(OutputStream out) throws IOException {
super.sendDispositionHeader(out);
if (filename != null) {
- String fileNameSuffix = ";filename=\"" + filename + "\"";
+ String fileNameSuffix = "; filename=\"" + filename + "\"";
out.write(fileNameSuffix.getBytes());
}
}
@@ -416,6 +426,11 @@
HttpURLConnection connection = prepareInitialPost(network, mUrl);
connection.setDoOutput(true);
connection.addRequestProperty("Authorization", authHeader);
+ connection.addRequestProperty("Content-Length",
+ String.valueOf(multipartEntity.getContentLength()));
+ connection.addRequestProperty("Content-Type", multipartEntity.getContentType().getValue());
+ connection.addRequestProperty("Accept-Encoding", "*");
+
try (OutputStream requestBodyOut = connection.getOutputStream()) {
multipartEntity.writeTo(requestBodyOut);
} catch (IOException e) {
@@ -425,6 +440,8 @@
try {
int response = connection.getResponseCode();
+ Log.i(TAG, "Received response code: " + response
+ + ", message=" + connection.getResponseMessage());
if (response == 401 || response == 403) {
deliverFailure(TelephonyManager.CallComposerException.ERROR_AUTHENTICATION_FAILED);
return null;
@@ -493,6 +510,22 @@
return sb.toString();
}
+ private String getUserAgent() {
+ String carrierName = mContext.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mSubscriptionId)
+ .getSimOperatorName();
+ String buildId = Build.ID;
+ String buildDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+ .withZone(ZoneId.systemDefault())
+ .format(Instant.ofEpochMilli(Build.TIME));
+ String buildVersion = Build.VERSION.RELEASE_OR_CODENAME;
+ String deviceName = Build.DEVICE;
+ return String.format("%s %s %s %s %s %s %s",
+ carrierName, buildId, buildDate, "Android", buildVersion,
+ deviceName, THREE_GPP_GBA);
+
+ }
+
private static void logException(String message, Throwable e) {
StringWriter log = new StringWriter();
log.append(message);
diff --git a/src/com/android/phone/callcomposer/DigestAuthUtils.java b/src/com/android/phone/callcomposer/DigestAuthUtils.java
index 52a278b..2f081f7 100644
--- a/src/com/android/phone/callcomposer/DigestAuthUtils.java
+++ b/src/com/android/phone/callcomposer/DigestAuthUtils.java
@@ -56,9 +56,13 @@
if (!TextUtils.isEmpty(parsedHeader.getAlgorithm())
&& !MD5_ALGORITHM.equals(parsedHeader.getAlgorithm().toLowerCase())) {
Log.e(TAG, "This client only supports MD5 auth");
+ return "";
}
-
- Log.i(TAG, "nonce=" + parsedHeader.getNonce());
+ if (!TextUtils.isEmpty(parsedHeader.getQop())
+ && !AUTH_QOP.equals(parsedHeader.getQop().toLowerCase())) {
+ Log.e(TAG, "This client only supports the auth qop");
+ return "";
+ }
String clientNonce = makeClientNonce();
@@ -71,7 +75,9 @@
replyHeader.setScheme(parsedHeader.getScheme());
replyHeader.setUsername(credentials.getTransactionId());
replyHeader.setURI(new WorkaroundURI(uri));
+ replyHeader.setRealm(parsedHeader.getRealm());
replyHeader.setQop(AUTH_QOP);
+ replyHeader.setNonce(parsedHeader.getNonce());
replyHeader.setCNonce(clientNonce);
replyHeader.setNonceCount(1);
replyHeader.setResponse(response);
@@ -83,7 +89,7 @@
return null;
}
- return replyHeader.encode();
+ return replyHeader.encodeBody();
}
public static String computeResponse(String serverNonce, String clientNonce, String qop,
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index dbb902d..88de151 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -210,6 +210,11 @@
private static final int MENU_ITEM_GET_IMS_STATUS = 4;
private static final int MENU_ITEM_TOGGLE_DATA = 5;
+ private static final String CARRIER_PROVISIONING_ACTION =
+ "com.android.phone.settings.CARRIER_PROVISIONING";
+ private static final String TRIGGER_CARRIER_PROVISIONING_ACTION =
+ "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING";
+
private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
private TextView mLine1Number;
private TextView mSubscriptionId;
@@ -571,11 +576,20 @@
mDnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
mDnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
mCarrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
- mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+ if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+ mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+ } else {
+ mCarrierProvisioningButton.setEnabled(false);
+ }
+
mTriggerCarrierProvisioningButton = (Button) findViewById(
R.id.trigger_carrier_provisioning);
- mTriggerCarrierProvisioningButton.setOnClickListener(
- mTriggerCarrierProvisioningButtonHandler);
+ if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+ mTriggerCarrierProvisioningButton.setOnClickListener(
+ mTriggerCarrierProvisioningButtonHandler);
+ } else {
+ mTriggerCarrierProvisioningButton.setEnabled(false);
+ }
mOemInfoButton = (Button) findViewById(R.id.oem_info);
mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
@@ -1610,21 +1624,23 @@
}
};
- OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
- public void onClick(View v) {
- final Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
- final ComponentName serviceComponent = ComponentName.unflattenFromString(
- "com.android.omadm.service/.DMIntentReceiver");
+ OnClickListener mCarrierProvisioningButtonHandler = v -> {
+ String carrierProvisioningApp = getCarrierProvisioningAppString();
+ if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+ final Intent intent = new Intent(CARRIER_PROVISIONING_ACTION);
+ final ComponentName serviceComponent =
+ ComponentName.unflattenFromString(carrierProvisioningApp);
intent.setComponent(serviceComponent);
sendBroadcast(intent);
}
};
- OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
- public void onClick(View v) {
- final Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
- final ComponentName serviceComponent = ComponentName.unflattenFromString(
- "com.android.omadm.service/.DMIntentReceiver");
+ OnClickListener mTriggerCarrierProvisioningButtonHandler = v -> {
+ String carrierProvisioningApp = getCarrierProvisioningAppString();
+ if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+ final Intent intent = new Intent(TRIGGER_CARRIER_PROVISIONING_ACTION);
+ final ComponentName serviceComponent =
+ ComponentName.unflattenFromString(carrierProvisioningApp);
intent.setComponent(serviceComponent);
sendBroadcast(intent);
}
@@ -1689,6 +1705,19 @@
}
};
+ private String getCarrierProvisioningAppString() {
+ if (mPhone != null) {
+ CarrierConfigManager configManager =
+ mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+ if (b != null) {
+ return b.getString(
+ CarrierConfigManager.KEY_CARRIER_PROVISIONING_APP_STRING, "");
+ }
+ }
+ return "";
+ }
+
boolean isCbrsSupported() {
return getResources().getBoolean(
com.android.internal.R.bool.config_cbrs_supported);
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index f243462..f9742b4 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -23,6 +23,7 @@
import android.telecom.DisconnectCause;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsReasonInfo;
import com.android.internal.telephony.CallFailCause;
import com.android.internal.telephony.Phone;
@@ -64,13 +65,13 @@
* message and tone.
*
* @param telephonyDisconnectCause The code for the reason for the disconnect.
- * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
+ * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
* @param reason Description of the reason for the disconnect, not intended for the user to see..
*/
public static DisconnectCause toTelecomDisconnectCause(
- int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason) {
- return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPerciseDisconnectCause,
- reason, SubscriptionManager.getDefaultVoicePhoneId());
+ int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason) {
+ return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause,
+ reason, SubscriptionManager.getDefaultVoicePhoneId(), null);
}
/**
@@ -84,30 +85,33 @@
public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
String reason, int phoneId) {
return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID,
- reason, phoneId);
+ reason, phoneId, null);
}
/**
* Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
* generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
* message and tone for Slot.
- *
* @param telephonyDisconnectCause The code for the reason for the disconnect.
- * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
- * @param reason Description of the reason for the disconnect, not intended for the user to see..
+ * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
+ * @param reason Description of the reason for the disconnect, not intended for the user to see.
* @param phoneId To support localized message based on phoneId
+ * @param imsReasonInfo
*/
public static DisconnectCause toTelecomDisconnectCause(
- int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason,
- int phoneId) {
+ int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason,
+ int phoneId, ImsReasonInfo imsReasonInfo) {
Context context = PhoneGlobals.getInstance();
return new DisconnectCause(
toTelecomDisconnectCauseCode(telephonyDisconnectCause),
toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
- telephonyPerciseDisconnectCause),
+ telephonyPreciseDisconnectCause),
toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
toTelecomDisconnectReason(context,telephonyDisconnectCause, reason, phoneId),
- toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId));
+ toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId),
+ telephonyDisconnectCause,
+ telephonyPreciseDisconnectCause,
+ imsReasonInfo);
}
/**
@@ -233,10 +237,10 @@
* Returns a label for to the disconnect cause to be shown to the user.
*/
private static CharSequence toTelecomDisconnectCauseLabel(
- Context context, int telephonyDisconnectCause, int telephonyPerciseDisconnectCause) {
+ Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause) {
CharSequence label;
- if (telephonyPerciseDisconnectCause != CallFailCause.NOT_VALID) {
- label = getLabelFromPreciseDisconnectCause(context, telephonyPerciseDisconnectCause,
+ if (telephonyPreciseDisconnectCause != CallFailCause.NOT_VALID) {
+ label = getLabelFromPreciseDisconnectCause(context, telephonyPreciseDisconnectCause,
telephonyDisconnectCause);
} else {
label = getLabelFromDisconnectCause(context, telephonyDisconnectCause);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 18b1426..e684c7d 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -50,6 +50,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
@@ -2371,12 +2372,18 @@
+ " -> " + mHangupDisconnectCause);
disconnectCause = mHangupDisconnectCause;
}
+ ImsReasonInfo imsReasonInfo = null;
+ if (isImsConnection()) {
+ ImsPhoneConnection imsPhoneConnection =
+ (ImsPhoneConnection) mOriginalConnection;
+ imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
+ }
setTelephonyConnectionDisconnected(
DisconnectCauseUtil.toTelecomDisconnectCause(
disconnectCause,
preciseDisconnectCause,
mOriginalConnection.getVendorDisconnectCause(),
- getPhone().getPhoneId()));
+ getPhone().getPhoneId(), imsReasonInfo));
close();
}
break;
@@ -3168,7 +3175,7 @@
}
/**
- * Set this {@link TelephonyConnection} to a held state.
+ * Set this {@link TelephonyConnection} to a disconnected state.
* <p>
* Note: This should be used instead of
* {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 5a1acb5..3eefdb0 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.net.Uri;
import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -63,12 +65,16 @@
void onRcsDisconnected();
/**
- * The subscription associated with the slot this controller is bound to has changed or its
- * carrier configuration has changed.
+ * The subscription associated with the slot this controller is bound to has changed.
*/
void onAssociatedSubscriptionUpdated(int subId);
/**
+ * The carrier configuration associated with the active subscription id has changed.
+ */
+ void onCarrierConfigChanged();
+
+ /**
* Called when the feature should be destroyed.
*/
void onDestroy();
@@ -118,6 +124,7 @@
private final Object mLock = new Object();
private FeatureConnector<RcsFeatureManager> mFeatureConnector;
private RcsFeatureManager mFeatureManager;
+ private int mAssociatedSubId;
private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
new FeatureConnector.Listener<RcsFeatureManager>() {
@@ -171,9 +178,10 @@
}
};
- public RcsFeatureController(Context context, int slotId) {
+ public RcsFeatureController(Context context, int slotId, int associatedSubId) {
mContext = context;
mSlotId = slotId;
+ mAssociatedSubId = associatedSubId;
mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
mContext.getMainExecutor());
}
@@ -182,9 +190,11 @@
* Should only be used to inject registration helpers for testing.
*/
@VisibleForTesting
- public RcsFeatureController(Context context, int slotId, RegistrationHelperFactory f) {
+ public RcsFeatureController(Context context, int slotId, int associatedSubId,
+ RegistrationHelperFactory f) {
mContext = context;
mSlotId = slotId;
+ mAssociatedSubId = associatedSubId;
mRegistrationHelperFactory = f;
mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
mContext.getMainExecutor());
@@ -248,17 +258,12 @@
}
/**
- * Update the subscription associated with this controller.
+ * Update the Features associated with this controller due to the associated subscription
+ * changing.
*/
public void updateAssociatedSubscription(int newSubId) {
- RcsFeatureManager manager = getFeatureManager();
- if (manager != null) {
- try {
- manager.updateCapabilities();
- } catch (ImsException e) {
- Log.w(LOG_TAG, "associatedSubscriptionChanged failed:" + e);
- }
- }
+ mAssociatedSubId = newSubId;
+ updateCapabilities();
synchronized (mLock) {
for (Feature c : mFeatures.values()) {
c.onAssociatedSubscriptionUpdated(newSubId);
@@ -267,6 +272,19 @@
}
/**
+ * Update the features associated with this controller due to the carrier configuration
+ * changing.
+ */
+ public void onCarrierConfigChangedForSubscription() {
+ updateCapabilities();
+ synchronized (mLock) {
+ for (Feature c : mFeatures.values()) {
+ c.onCarrierConfigChanged();
+ }
+ }
+ }
+
+ /**
* Call before this controller is destroyed to tear down associated features.
*/
public void destroy() {
@@ -314,8 +332,8 @@
}
/**
- * Register an {@link ImsRcsManager.AvailabilityCallback} with the associated RcsFeature,
- * which will provide availability updates.
+ * Register an {@link ImsRcsManager.OnAvailabilityChangedListener} with the associated
+ * RcsFeature, which will provide availability updates.
*/
public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
throws ImsException {
@@ -328,7 +346,7 @@
}
/**
- * Remove a registered {@link ImsRcsManager.AvailabilityCallback} from the RcsFeature.
+ * Remove a registered {@link ImsRcsManager.OnAvailabilityChangedListener} from the RcsFeature.
*/
public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
RcsFeatureManager manager = getFeatureManager();
@@ -381,10 +399,21 @@
callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
}
+ private void updateCapabilities() {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ try {
+ manager.updateCapabilities(mAssociatedSubId);
+ } catch (ImsException e) {
+ Log.w(LOG_TAG, "updateCapabilities failed:" + e);
+ }
+ }
+ }
+
private void setupConnectionToService(RcsFeatureManager manager) throws ImsException {
// Open persistent listener connection, sends RcsFeature#onFeatureReady.
manager.openConnection();
- manager.updateCapabilities();
+ manager.updateCapabilities(mAssociatedSubId);
manager.registerImsRegistrationCallback(mImsRcsRegistrationHelper.getCallbackBinder());
}
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index 028e49f..a948cdb 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -296,6 +296,11 @@
}
@Override
+ public void onCarrierConfigChanged() {
+ mExecutorService.submit(this::onCarrierConfigChangedInternal);
+ }
+
+ @Override
public void onDestroy() {
mExecutorService.submit(()-> {
// Ensure new create/destroy requests are denied.
@@ -903,8 +908,7 @@
}
/**
- * Called when either the sub ID associated with the slot has changed or the carrier
- * configuration associated with the same subId has changed.
+ * Called when the sub ID associated with the slot has changed.
*/
private void onSubIdChanged(int newSubId) {
logi("subId changed, " + mSubId + "->" + newSubId);
@@ -913,10 +917,14 @@
mSubId = newSubId;
scheduleDestroyDelegates(
SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
- return;
}
- // TODO: if subId hasn't changed this means that we should load in any new carrier configs
- // that we care about and apply.
+ }
+
+ /**
+ * Called when the carrier configuration associated with the same subId has changed.
+ */
+ private void onCarrierConfigChangedInternal() {
+ logi("Carrier Config changed for subId: " + mSubId);
}
/**
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 66492ae..034382c 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -55,7 +55,7 @@
/**
* @return an {@link RcsFeatureController} associated with the slot specified.
*/
- RcsFeatureController createController(Context context, int slotId);
+ RcsFeatureController createController(Context context, int slotId, int subId);
/**
* @return an instance of {@link UceControllerManager} associated with the slot specified.
@@ -71,8 +71,8 @@
private FeatureFactory mFeatureFactory = new FeatureFactory() {
@Override
- public RcsFeatureController createController(Context context, int slotId) {
- return new RcsFeatureController(context, slotId);
+ public RcsFeatureController createController(Context context, int slotId, int subId) {
+ return new RcsFeatureController(context, slotId, subId);
}
@Override
@@ -113,6 +113,8 @@
// Maps slot ID -> RcsFeatureController.
private SparseArray<RcsFeatureController> mFeatureControllers;
+ // Maps slotId -> associatedSubIds
+ private SparseArray<Integer> mSlotToAssociatedSubIds;
// Whether the device supports User Capability Exchange
private boolean mRcsUceEnabled;
@@ -132,7 +134,7 @@
SubscriptionManager.INVALID_PHONE_INDEX);
int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- updateFeatureControllerSubscription(slotId, subId);
+ onCarrierConfigChangedForSlot(slotId, subId);
}
}
};
@@ -159,6 +161,7 @@
mContext = context;
mNumSlots = numSlots;
mFeatureControllers = new SparseArray<>(numSlots);
+ mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
}
@@ -167,6 +170,7 @@
mContext = context;
mNumSlots = numSlots;
mFeatureControllers = new SparseArray<>(numSlots);
+ mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
sResourceProxy = resourceProxy;
mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
}
@@ -218,6 +222,8 @@
// Do not add feature controllers for inactive subscriptions
if (c.hasActiveFeatures()) {
mFeatureControllers.put(i, c);
+ // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier
+ // config change.
}
}
} else {
@@ -225,6 +231,7 @@
RcsFeatureController c = mFeatureControllers.get(i);
if (c != null) {
mFeatureControllers.remove(i);
+ mSlotToAssociatedSubIds.remove(i);
c.destroy();
}
}
@@ -232,19 +239,29 @@
}
}
- private void updateFeatureControllerSubscription(int slotId, int newSubId) {
+ /**
+ * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
+ * @param slotId The slotId associated with the event.
+ * @param subId The subId associated with the event. May cause the subId associated with the
+ * RcsFeatureController to change if the subscription itself has changed.
+ */
+ private void onCarrierConfigChangedForSlot(int slotId, int subId) {
synchronized (mLock) {
RcsFeatureController f = mFeatureControllers.get(slotId);
- Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
- + newSubId + ", existing feature=" + (f != null));
- if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+ final int oldSubId = mSlotToAssociatedSubIds.get(slotId,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mSlotToAssociatedSubIds.put(slotId, subId);
+ Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId
+ + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature="
+ + (f != null));
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (f == null) {
// A controller doesn't exist for this slot yet.
- f = mFeatureFactory.createController(mContext, slotId);
- updateSupportedFeatures(f, slotId, newSubId);
+ f = mFeatureFactory.createController(mContext, slotId, subId);
+ updateSupportedFeatures(f, slotId, subId);
if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
} else {
- updateSupportedFeatures(f, slotId, newSubId);
+ updateSupportedFeatures(f, slotId, subId);
// Do not keep an empty container around.
if (!f.hasActiveFeatures()) {
f.destroy();
@@ -252,13 +269,19 @@
}
}
}
- if (f != null) f.updateAssociatedSubscription(newSubId);
+ if (f != null) {
+ if (oldSubId == subId) {
+ f.onCarrierConfigChangedForSubscription();
+ } else {
+ f.updateAssociatedSubscription(subId);
+ }
+ }
}
}
private RcsFeatureController constructFeatureController(int slotId) {
- RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
int subId = getSubscriptionFromSlot(slotId);
+ RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId);
updateSupportedFeatures(c, slotId, subId);
return c;
}
diff --git a/src/com/android/services/telephony/rcs/UceControllerManager.java b/src/com/android/services/telephony/rcs/UceControllerManager.java
index 20ea17b..d62fb5e 100644
--- a/src/com/android/services/telephony/rcs/UceControllerManager.java
+++ b/src/com/android/services/telephony/rcs/UceControllerManager.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.Uri;
import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.RcsUceAdapter.PublishState;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
@@ -32,6 +33,7 @@
import java.io.PrintWriter;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -50,6 +52,7 @@
private final Context mContext;
private final ExecutorService mExecutorService;
+ private volatile int mSubId;
private volatile UceController mUceController;
private volatile RcsFeatureManager mRcsFeatureManager;
@@ -57,6 +60,7 @@
Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId);
mSlotId = slotId;
+ mSubId = subId;
mContext = context;
mExecutorService = Executors.newSingleThreadExecutor();
mUceController = new UceController(mContext, subId);
@@ -68,6 +72,7 @@
@VisibleForTesting
public UceControllerManager(Context context, int slotId, int subId, ExecutorService executor) {
mSlotId = slotId;
+ mSubId = subId;
mContext = context;
mExecutorService = executor;
mUceController = new UceController(mContext, subId);
@@ -98,15 +103,19 @@
}
/**
- * This method will be called when either the subscription ID associated with the slot has
- * changed or the carrier configuration associated with the same subId has changed.
+ * This method will be called when the subscription ID associated with the slot has
+ * changed.
*/
@Override
public void onAssociatedSubscriptionUpdated(int subId) {
mExecutorService.submit(() -> {
Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
- + ", subId=" + subId);
-
+ + ", subId=" + mSubId + ", newSubId=" + subId);
+ if (mSubId == subId) {
+ Log.w(LOG_TAG, "onAssociatedSubscriptionUpdated called with the same subId");
+ return;
+ }
+ mSubId = subId;
// Destroy existing UceController and create a new one.
mUceController.onDestroy();
mUceController = new UceController(mContext, subId);
@@ -119,6 +128,18 @@
});
}
+ /**
+ * This method will be called when the carrier config of the subscription associated with this
+ * manager has changed.
+ */
+ @Override
+ public void onCarrierConfigChanged() {
+ mExecutorService.submit(() -> {
+ Log.i(LOG_TAG, "onCarrierConfigChanged: subId=" + mSubId);
+ mUceController.onCarrierConfigChanged();
+ });
+ }
+
@VisibleForTesting
public void setUceController(UceController uceController) {
mUceController = uceController;
@@ -181,15 +202,15 @@
* @throws ImsException if the ImsService connected to this controller is currently down.
*/
public @PublishState int getUcePublishState() throws ImsException {
- Future future = mExecutorService.submit(() -> {
+ Future<Integer> future = mExecutorService.submit(() -> {
checkUceControllerState();
return mUceController.getUcePublishState();
});
try {
- return (Integer) future.get();
+ return future.get();
} catch (ExecutionException | InterruptedException e) {
- Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+ Log.w(LOG_TAG, "getUcePublishState exception: " + e);
Throwable cause = e.getCause();
if (cause instanceof ImsException) {
throw (ImsException) cause;
@@ -199,6 +220,114 @@
}
/**
+ * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+ */
+ public RcsContactUceCapability addUceRegistrationOverride(
+ Set<String> featureTags) throws ImsException {
+ Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.addRegistrationOverrideCapabilities(featureTags);
+ });
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+ */
+ public RcsContactUceCapability removeUceRegistrationOverride(
+ Set<String> featureTags) throws ImsException {
+ Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.removeRegistrationOverrideCapabilities(featureTags);
+ });
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+ */
+ public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException {
+ Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.clearRegistrationOverrideCapabilities();
+ });
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+ */
+ public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException {
+ Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.getLatestRcsContactUceCapability();
+ });
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @return The last PIDF XML sent to the IMS stack to be published.
+ */
+ public String getLastPidfXml() throws ImsException {
+ Future<String> future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.getLastPidfXml();
+ });
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "getLastPidfXml exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return null;
+ }
+ }
+
+ /**
* Register the Publish state changed callback.
*
* @throws ImsException if the ImsService connected to this controller is currently down.
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 05745c8..4e40120 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -19,8 +19,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.sample.rcsclient"
- android:versionCode="7"
- android:versionName="1.0.6">
+ android:versionCode="9"
+ android:versionName="1.0.8">
<uses-sdk
android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
index bb1283a..8f7e6a8 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -49,10 +49,10 @@
public class ChatActivity extends AppCompatActivity {
public static final String EXTRA_REMOTE_PHONE_NUMBER = "REMOTE_PHONE_NUMBER";
- public static final String TELURI_PREFIX = "tel:";
private static final String TAG = "TestRcsApp.ChatActivity";
private static final int INIT_LIST = 1;
private static final int SHOW_STATUS = 2;
+ private static final int EMPTY_MSG = 3;
private static final float TEXT_SIZE = 20.0f;
private static final int MARGIN_SIZE = 20;
private static final long TIMEOUT_IN_MS = 10000L;
@@ -85,6 +85,9 @@
case SHOW_STATUS:
mTips.setText(msg.obj.toString());
break;
+ case EMPTY_MSG:
+ mNewMessage.setText("");
+ break;
default:
Log.d(TAG, "unknown msg:" + msg.what);
break;
@@ -134,7 +137,7 @@
ChatActivity.this.getResources().getString(R.string.session_timeout)),
TIMEOUT_IN_MS);
ChatManager.getInstance(getApplicationContext(), mSubId).initChatSession(
- TELURI_PREFIX + mDestNumber, new SessionStateCallback() {
+ mDestNumber, new SessionStateCallback() {
@Override
public void onSuccess() {
Log.i(TAG, "session init succeeded");
@@ -176,7 +179,8 @@
ChatManager.getInstance(getApplicationContext(), mSubId).addNewMessage(
mNewMessage.getText().toString(), ChatManager.SELF, mDestNumber);
ChatManager.getInstance(getApplicationContext(), mSubId).sendMessage(
- TELURI_PREFIX + mDestNumber, mNewMessage.getText().toString());
+ mDestNumber, mNewMessage.getText().toString());
+ mHandler.sendMessage(mHandler.obtainMessage(EMPTY_MSG));
}
});
});
@@ -259,8 +263,7 @@
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
- ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(
- TELURI_PREFIX + mDestNumber);
+ ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(mDestNumber);
}
@Override
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
index 6835fea..0fae8f6 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
@@ -46,6 +46,7 @@
public class UceActivity extends AppCompatActivity {
private static final String TAG = "TestRcsApp.UceActivity";
+ private static final String TELURI_PREFIX = "tel:";
private Button mCapabilityButton;
private Button mAvailabilityButton;
private TextView mCapabilityResult;
@@ -146,7 +147,7 @@
public void onComplete() {
Log.i(TAG, "onComplete()");
- mCapabilityResult.append("complete");
+ mCapabilityResult.append("onComplete");
}
@@ -174,7 +175,7 @@
for (String number : numbers) {
String formattedNumber = NumberUtils.formatNumber(this, number);
if (formattedNumber != null) {
- contactList.add(Uri.parse(ChatActivity.TELURI_PREFIX + formattedNumber));
+ contactList.add(Uri.parse(TELURI_PREFIX + formattedNumber));
} else {
Log.w(TAG, "number formatted improperly, skipping: " + number);
}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
index ec048b8..399a860 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
@@ -55,6 +55,7 @@
public class ChatManager {
public static final String SELF = "self";
private static final String TAG = "TestRcsApp.ChatManager";
+ private static final String TELURI_PREFIX = "tel:";
private static AddressFactory sAddressFactory = new AddressFactoryImpl();
private static HashMap<Integer, ChatManager> sChatManagerInstances = new HashMap<>();
private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(5);
@@ -65,7 +66,7 @@
private SimpleRcsClient mSimpleRcsClient;
private State mState;
private int mSubId;
- private HashMap<URI, SimpleChatSession> mContactSessionMap = new HashMap<>();
+ private HashMap<String, SimpleChatSession> mContactSessionMap = new HashMap<>();
private RcsStateChangedCallback mRcsStateChangedCallback;
private ChatManager(Context context, int subId) {
@@ -90,15 +91,14 @@
mRcsStateChangedCallback.notifyStateChange(oldState, newState);
});
mImsService.setListener((session) -> {
- Log.i(TAG, "onIncomingSession()");
- mContactSessionMap.put(session.getRemoteUri(), session);
+ Log.i(TAG, "onIncomingSession():" + session.getRemoteUri());
+ String phoneNumber = getNumberFromUri(session.getRemoteUri().toString());
+ mContactSessionMap.put(phoneNumber, session);
session.setListener(
// implement onMessageReceived()
(message) -> {
mFixedThreadPool.execute(() -> {
String msg = message.content();
- String phoneNumber = getNumberFromUri(
- session.getRemoteUri().toString());
if (TextUtils.isEmpty(phoneNumber)) {
Log.i(TAG, "dest number is empty, uri:"
+ session.getRemoteUri());
@@ -173,33 +173,34 @@
/**
* Initiate 1 to 1 chat session.
- * @param telUriContact destination tel Uri.
+ *
+ * @param contact destination phone number.
* @param callback callback for session state.
*/
- public void initChatSession(String telUriContact, SessionStateCallback callback) {
+ public void initChatSession(String contact, SessionStateCallback callback) {
if (mState != State.REGISTERED) {
Log.i(TAG, "Could not init session due to State = " + mState);
return;
}
- URI uri = createUri(telUriContact);
- if (mContactSessionMap.containsKey(uri)) {
+ Log.i(TAG, "initChatSession contact: " + contact);
+ if (mContactSessionMap.containsKey(contact)) {
callback.onSuccess();
- Log.i(TAG, "uri exists");
+ Log.i(TAG, "contact exists");
return;
}
Futures.addCallback(
- mImsService.startOriginatingChatSession(telUriContact),
+ mImsService.startOriginatingChatSession(TELURI_PREFIX + contact),
new FutureCallback<SimpleChatSession>() {
@Override
public void onSuccess(SimpleChatSession chatSession) {
- mContactSessionMap.put(chatSession.getRemoteUri(), chatSession);
+ String phoneNumber = getNumberFromUri(
+ chatSession.getRemoteUri().toString());
+ mContactSessionMap.put(phoneNumber, chatSession);
chatSession.setListener(
// implement onMessageReceived()
(message) -> {
mFixedThreadPool.execute(() -> {
String msg = message.content();
- String phoneNumber = getNumberFromUri(
- chatSession.getRemoteUri().toString());
if (TextUtils.isEmpty(phoneNumber)) {
Log.i(TAG, "dest number is empty, uri:"
+ chatSession.getRemoteUri());
@@ -222,13 +223,14 @@
/**
* Send a chat message.
- * @param telUriContact destination tel Uri.
+ *
+ * @param contact destination phone number.
* @param message chat message.
*/
- public void sendMessage(String telUriContact, String message) {
- SimpleChatSession chatSession = mContactSessionMap.get(createUri(telUriContact));
+ public void sendMessage(String contact, String message) {
+ SimpleChatSession chatSession = mContactSessionMap.get(contact);
if (chatSession == null) {
- Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
+ Log.i(TAG, "session is unavailable for contact = " + contact);
return;
}
chatSession.sendMessage(message);
@@ -240,22 +242,23 @@
/**
* Terminate the chat session.
- * @param telUriContact destination tel Uri
+ *
+ * @param contact destination phone number.
*/
- public void terminateSession(String telUriContact) {
+ public void terminateSession(String contact) {
Log.i(TAG, "terminateSession");
- URI uri = createUri(telUriContact);
- SimpleChatSession chatSession = mContactSessionMap.get(uri);
+ SimpleChatSession chatSession = mContactSessionMap.get(contact);
if (chatSession == null) {
- Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
+ Log.i(TAG, "session is unavailable for contact = " + contact);
return;
}
chatSession.terminate();
- mContactSessionMap.remove(uri);
+ mContactSessionMap.remove(contact);
}
/**
* Insert chat information into database.
+ *
* @param message chat message.
* @param src source phone number.
* @param dest destination phone number.
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
index 81abe89..58a6eef 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
@@ -31,17 +31,19 @@
/** Provides creating and managing {@link MsrpSession} */
public class MsrpManager {
private final ImsPdnNetworkFetcher imsPdnNetworkFetcher;
+ private Context context;
public MsrpManager(Context context) {
+ this.context = context;
imsPdnNetworkFetcher = new ImsPdnNetworkFetcher(context);
}
- private static MsrpSession createMsrpSession(ConnectivityManager manager,
+ private MsrpSession createMsrpSession(ConnectivityManager manager,
Network network, String host, int port, String localIp, int localPort,
MsrpSessionListener listener) throws IOException {
Socket socket = network.getSocketFactory().createSocket(host, port,
InetAddress.getByName(localIp), localPort);
- MsrpSession msrpSession = new MsrpSession(manager,
+ MsrpSession msrpSession = new MsrpSession(manager, context,
network, socket, listener);
Thread thread = new Thread(msrpSession::run);
thread.start();
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
index 3f8b986..3a95a0a 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
@@ -19,6 +19,7 @@
import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.SEND;
import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.UNKNOWN;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.QosCallback;
@@ -27,6 +28,7 @@
import android.net.QosSessionAttributes;
import android.net.QosSocketInfo;
import android.util.Log;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -49,6 +51,8 @@
* Provides MSRP sending and receiving messages ability.
*/
public class MsrpSession {
+ private static final String DEDICATED_BEARER_SUCCESS = "Dedicated bearer succeeded";
+ private static final String DEDICATED_BEARER_ERROR = "Dedicated bearer error";
private final Network network;
private final Socket socket;
private final InputStream input;
@@ -59,11 +63,13 @@
private final MsrpSessionListener listener;
private final ConnectivityManager connectivityManager;
private final String LOG_TAG = MsrpSession.class.getSimpleName();
+ private final Context context;
/** Creates a new MSRP session on the given listener and the provided streams. */
- MsrpSession(ConnectivityManager connectivityManager, Network network, Socket socket,
- MsrpSessionListener listener) throws IOException {
+ MsrpSession(ConnectivityManager connectivityManager, Context context, Network network,
+ Socket socket, MsrpSessionListener listener) throws IOException {
this.connectivityManager = connectivityManager;
+ this.context = context;
this.network = network;
this.socket = socket;
this.input = socket.getInputStream();
@@ -76,6 +82,7 @@
private final QosCallback qosCallback = new QosCallback() {
@Override
public void onError(@NonNull QosCallbackException exception) {
+ Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
Log.e(LOG_TAG, "onError: " + exception.toString());
super.onError(exception);
}
@@ -83,6 +90,7 @@
@Override
public void onQosSessionAvailable(@NonNull QosSession session,
@NonNull QosSessionAttributes sessionAttributes) {
+ Toast.makeText(context, DEDICATED_BEARER_SUCCESS, Toast.LENGTH_SHORT).show();
Log.d(LOG_TAG, "onQosSessionAvailable: " + session.toString() + ", "
+ sessionAttributes.toString());
super.onQosSessionAvailable(session, sessionAttributes);
@@ -90,6 +98,7 @@
@Override
public void onQosSessionLost(@NonNull QosSession session) {
+ Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
Log.e(LOG_TAG, "onQosSessionLost: " + session.toString());
super.onQosSessionLost(session);
}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
index 94850fd..bc2c611 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
@@ -31,6 +31,7 @@
public final class ChatServiceException extends Exception {
public static final int CODE_ERROR_UNSPECIFIED = 0;
+ public static final int CODE_ERROR_SEND_MESSAGE_FAILED = 1;
private int mCode = CODE_ERROR_UNSPECIFIED;
/**
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
index 4cc474c..fbeb205 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
@@ -17,6 +17,7 @@
package com.android.libraries.rcs.simpleclient.service.chat;
import static com.android.libraries.rcs.simpleclient.protocol.cpim.CpimUtils.CPIM_CONTENT_TYPE;
+import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_SEND_MESSAGE_FAILED;
import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_UNSPECIFIED;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -101,11 +102,12 @@
}
/** Send a text message via MSRP session associated with this session. */
- public void sendMessage(String msg) {
+ public ListenableFuture<Void> sendMessage(String msg) {
MsrpSession session = mMsrpSession;
if (session == null || mRemoteSdp == null || mLocalSdp == null) {
Log.e(TAG, "Session is not established");
- return;
+ return Futures.immediateFailedFuture(
+ new IllegalStateException("Session is not established"));
}
// Build a new CPIM message and send it out through the MSRP session.
@@ -133,27 +135,21 @@
.build();
Log.i(TAG, "Send a MSRP chunk: " + msrpChunk);
- Futures.addCallback(
- session.send(msrpChunk),
- new FutureCallback<MsrpChunk>() {
- @Override
- public void onSuccess(MsrpChunk result) {
- if (result.responseCode() != 200) {
- Log.d(
- TAG,
- "Received error response id="
- + result.transactionId()
- + " code="
- + result.responseCode());
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.d(TAG, "Failed to send msrp chunk", t);
- }
- },
- MoreExecutors.directExecutor());
+ return Futures.transformAsync(session.send(msrpChunk), result -> {
+ if (result == null) {
+ return Futures.immediateFailedFuture(
+ new ChatServiceException("Failed to send a chunk",
+ CODE_ERROR_SEND_MESSAGE_FAILED));
+ }
+ if (result.responseCode() != 200) {
+ Log.d(TAG, "Received error response id=" + result.transactionId()
+ + " code=" + result.responseCode());
+ return Futures.immediateFailedFuture(
+ new ChatServiceException("Msrp response code: " + result.responseCode(),
+ CODE_ERROR_SEND_MESSAGE_FAILED));
+ }
+ return Futures.immediateFuture(null);
+ }, MoreExecutors.directExecutor());
}
/** Start outgoing chat session. */
diff --git a/tests/src/com/android/phone/callcomposer/PictureManagerTest.java b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
index b52b297..f1ce3b8 100644
--- a/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
+++ b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
@@ -34,6 +34,7 @@
import android.provider.CallLog;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
+import android.telephony.gba.TlsParams;
import android.telephony.gba.UaSecurityProtocolIdentifier;
import org.junit.After;
@@ -78,6 +79,14 @@
PersistableBundle b = new PersistableBundle();
b.putString(CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING,
FAKE_URL_BASE);
+ b.putInt(CarrierConfigManager.KEY_GBA_MODE_INT,
+ CarrierConfigManager.GBA_ME);
+ b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT,
+ UaSecurityProtocolIdentifier.ORG_3GPP);
+ b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT,
+ UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+ b.putInt(CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT,
+ TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);
when(telephonyManager.getCarrierConfig()).thenReturn(b);
}
@@ -263,7 +272,7 @@
public void testGbaCredLookup(GbaCredentialsSupplier supplier, boolean forceExpected)
throws Exception {
- String fakeRealm = "3gpp-bootstraping@naf1.example.com";
+ String fakeNafId = "https://3GPP-bootstrapping@www.example.com";
byte[] fakeKey = new byte[] {1, 2, 3, 4, 5};
String fakeTxId = "89sdfjggf";
@@ -271,8 +280,9 @@
ArgumentCaptor.forClass(TelephonyManager.BootstrapAuthenticationCallback.class);
CompletableFuture<GbaCredentials> credsFuture =
- supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
- verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+ supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
+ verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(),
+ eq(Uri.parse(fakeNafId)),
nullable(UaSecurityProtocolIdentifier.class), eq(forceExpected),
nullable(Executor.class),
authCallbackCaptor.capture());
@@ -285,9 +295,9 @@
// Do it again and see if we make another request, then make sure that matches up with what
// we expected.
CompletableFuture<GbaCredentials> credsFuture1 =
- supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
+ supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
verify(telephonyManager, times(forceExpected ? 2 : 1))
- .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+ .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeNafId)),
nullable(UaSecurityProtocolIdentifier.class),
eq(forceExpected),
nullable(Executor.class),
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index eecbd2e..da614fc 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -58,6 +58,8 @@
@RunWith(AndroidJUnit4.class)
public class RcsFeatureControllerTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+
private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo(
ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test");
@@ -96,7 +98,7 @@
// Connect the RcsFeatureManager
mConnectorListener.getValue().connectionReady(mFeatureManager);
- verify(mFeatureManager).updateCapabilities();
+ verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
verify(mFeatureManager).registerImsRegistrationCallback(any());
verify(mMockFeature).onRcsConnected(mFeatureManager);
@@ -132,16 +134,16 @@
mConnectorListener.getValue().connectionReady(mFeatureManager);
try {
- controller.registerImsRegistrationCallback(0 /*subId*/, regCb);
- controller.registerRcsAvailabilityCallback(0 /*subId*/, capCb);
+ controller.registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+ controller.registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
controller.getRegistrationTech(integer -> {
});
- verify(mFeatureManager).registerImsRegistrationCallback(0, regCb);
- verify(mFeatureManager).registerRcsAvailabilityCallback(0, capCb);
+ verify(mFeatureManager).registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+ verify(mFeatureManager).registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
verify(mFeatureManager).isCapable(
RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
@@ -153,10 +155,10 @@
fail("ImsException not expected.");
}
- controller.unregisterImsRegistrationCallback(0, regCb);
- controller.unregisterRcsAvailabilityCallback(0, capCb);
- verify(mFeatureManager).unregisterImsRegistrationCallback(0, regCb);
- verify(mFeatureManager).unregisterRcsAvailabilityCallback(0, capCb);
+ controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+ controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+ verify(mFeatureManager).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+ verify(mFeatureManager).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
}
@Test
@@ -216,13 +218,13 @@
FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
try {
- controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
+ controller.registerImsRegistrationCallback(TEST_SUB_ID, null /*callback*/);
fail("ImsException expected for IMS registration.");
} catch (ImsException e) {
//expected
}
try {
- controller.registerRcsAvailabilityCallback(0 /*subId*/, null /*callback*/);
+ controller.registerRcsAvailabilityCallback(TEST_SUB_ID, null /*callback*/);
fail("ImsException expected for availability");
} catch (ImsException e) {
//expected
@@ -245,10 +247,25 @@
assertNotNull(integer);
assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, integer.intValue());
});
- controller.unregisterImsRegistrationCallback(0, regCb);
- controller.unregisterRcsAvailabilityCallback(0, capCb);
- verify(mFeatureManager, never()).unregisterImsRegistrationCallback(0, regCb);
- verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(0, capCb);
+ controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+ controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+ verify(mFeatureManager, never()).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+ verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+ }
+
+ @Test
+ public void testCarrierConfigChanged() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+ verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+ controller.onCarrierConfigChangedForSubscription();
+
+ verify(mFeatureManager, times(2)).updateCapabilities(TEST_SUB_ID);
+ verify(mMockFeature).onCarrierConfigChanged();
+ verify(mMockFeature, never()).onAssociatedSubscriptionUpdated(anyInt());
}
@Test
@@ -256,13 +273,13 @@
RcsFeatureController controller = createFeatureController();
// Connect the RcsFeatureManager
mConnectorListener.getValue().connectionReady(mFeatureManager);
- verify(mFeatureManager).updateCapabilities();
+ verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
- controller.updateAssociatedSubscription(1 /*new sub id*/);
+ controller.updateAssociatedSubscription(2 /*new subId*/);
- verify(mFeatureManager, times(2)).updateCapabilities();
- verify(mMockFeature).onAssociatedSubscriptionUpdated(1 /*new sub id*/);
+ verify(mFeatureManager).updateCapabilities(2 /*new subId*/);
+ verify(mMockFeature).onAssociatedSubscriptionUpdated(2 /*new subId*/);
}
@Test
@@ -281,7 +298,7 @@
private RcsFeatureController createFeatureController() {
RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/,
- mRegistrationFactory);
+ TEST_SUB_ID, mRegistrationFactory);
controller.setFeatureConnectorFactory(mFeatureFactory);
doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(),
mConnectorListener.capture(), any(), any());
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index c367af3..39469b6 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -67,10 +67,12 @@
super.setUp();
doReturn(mFeatureConnector).when(mFeatureConnectorFactory).create(any(), anyInt(),
any(), any(), any());
- mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/);
- mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
- doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
- doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
+ mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/, 1 /*subId*/);
+ mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/, 2 /*subId*/);
+ doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0),
+ anyInt());
+ doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1),
+ anyInt());
doReturn(mMockUceSlot0).when(mFeatureFactory).createUceControllerManager(any(), eq(0),
anyInt());
doReturn(mMockUceSlot1).when(mFeatureFactory).createUceControllerManager(any(), eq(1),
@@ -227,7 +229,7 @@
}
@Test
- public void testCarrierConfigUpdate() {
+ public void testCarrierConfigUpdateAssociatedSub() {
setCarrierConfig(1 /*subId*/,
CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
true /*isEnabled*/);
@@ -251,6 +253,26 @@
}
@Test
+ public void testCarrierConfigNotifyFeatures() {
+ setCarrierConfig(1 /*subId*/,
+ CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
+ true /*isEnabled*/);
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
+ verify(mFeatureControllerSlot0).connect();
+
+
+ // Send carrier config update twice with no update to subId
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ verify(mFeatureControllerSlot0, never()).onCarrierConfigChangedForSubscription();
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0, times(1)).updateAssociatedSubscription(1);
+ // carrier config changed should be sent here
+ verify(mFeatureControllerSlot0).onCarrierConfigChangedForSubscription();
+ }
+
+ @Test
public void testCarrierConfigUpdateUceToNoUce() {
setCarrierConfig(1 /*subId*/,
CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
@@ -334,10 +356,10 @@
return service;
}
- private RcsFeatureController createFeatureController(int slotId) {
+ private RcsFeatureController createFeatureController(int slotId, int subId) {
// Create a spy instead of a mock because TelephonyRcsService relies on state provided by
// RcsFeatureController.
- RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId,
+ RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId, subId,
mRegistrationFactory));
controller.setFeatureConnectorFactory(mFeatureConnectorFactory);
return controller;
diff --git a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
index 4148d13..82687f8 100644
--- a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.net.Uri;
@@ -94,11 +95,15 @@
}
@Test
- public void testSubscriptionUpdated() throws Exception {
+ public void testSubIdAndCarrierConfigUpdate() throws Exception {
UceControllerManager uceCtrlManager = getUceControllerManager();
- uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId);
+ // Updates with the same subId should not destroy the UceController
+ uceCtrlManager.onCarrierConfigChanged();
+ verify(mUceController, never()).onDestroy();
+ // Updates with different subIds should trigger the creation of a new controller.
+ uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId + 1);
verify(mUceController).onDestroy();
}