diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:19:02 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:19:02 +0000 |
commit | fd8f5457cc7ee98b33001228c152c6e0da717bbe (patch) | |
tree | 52933cf2b7713a4c25d0fd324994920974372bf7 | |
parent | ae83f640eaa766c7713923ce9093c93037e922b9 (diff) | |
parent | f61dbc79584e2bd99c6588abed0d176b368a20d5 (diff) | |
download | ims-android14-mainline-uwb-release.tar.gz |
Snap for 10453563 from f61dbc79584e2bd99c6588abed0d176b368a20d5 to mainline-uwb-releaseaml_uwb_341513070aml_uwb_341511050aml_uwb_341310300aml_uwb_341310030aml_uwb_341111010aml_uwb_341011000android14-mainline-uwb-release
Change-Id: I31ebc4d80ca8606c0d2312cc18064f70d47fa188
35 files changed, 1130 insertions, 361 deletions
@@ -1,4 +1,7 @@ breadley@google.com -hallliu@google.com tgunn@google.com -dbright@google.com +chinmayd@google.com +xiaotonj@google.com +tjstuart@google.com +grantmenke@google.com +pmadapurmath@google.com diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java index 06230a16..29f6acd4 100755 --- a/src/java/com/android/ims/ImsCall.java +++ b/src/java/com/android/ims/ImsCall.java @@ -537,6 +537,22 @@ public class ImsCall implements ICall { public void onCallSessionRtpHeaderExtensionsReceived(ImsCall imsCall, @NonNull Set<RtpHeaderExtension> rtpHeaderExtensionData) { } + + /** + * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114. + * This API triggers radio to send ANBRQ message to the access network to query the + * desired bitrate. + * + * @param imsCall The ImsCall the data was received on. + * @param mediaType MediaType is used to identify media stream such as audio or video. + * @param direction Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond This value is the bitrate requested by the other party UE through + * RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate + * (defined in TS36.321, range: 0 ~ 8000 kbit/s). + */ + public void onCallSessionSendAnbrQuery(ImsCall imsCall, int mediaType, int direction, + int bitsPerSecond) { + } } // List of update operation for IMS call control @@ -886,11 +902,25 @@ public class ImsCall implements ICall { } /** - * Gets the specified property of this call. + * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer. * - * @param name key to get the extra call information defined in {@link ImsCallProfile} - * @return the extra call information as string + * @param mediaType MediaType is used to identify media stream such as audio or video. + * @param direction Direction of this packet stream (e.g. uplink or downlink). + * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended + * bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate + * to audio/video codec bitrate (defined in TS26.114). + * @hide */ + public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) { + synchronized(mLockObj) { + if (mSession != null) { + mSession.callSessionNotifyAnbr(mediaType, direction, bitsPerSecond); + } else { + logi("callSessionNotifyAnbr : session - null"); + } + } + } + public String getCallExtra(String name) throws ImsException { // Lookup the cache @@ -3491,6 +3521,25 @@ public class ImsCall implements ICall { } } } + + @Override + public void callSessionSendAnbrQuery(int mediaType, int direction, int bitsPerSecond) { + ImsCall.Listener listener; + + logi("callSessionSendAnbrQuery in ImsCall"); + synchronized (ImsCall.this) { + listener = mListener; + } + + if (listener != null) { + try { + listener.onCallSessionSendAnbrQuery(ImsCall.this, mediaType, + direction, bitsPerSecond); + } catch (Throwable t) { + loge("callSessionSendAnbrQuery:: ", t); + } + } + } } /** diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java index c41426d0..b5a1168b 100644 --- a/src/java/com/android/ims/ImsManager.java +++ b/src/java/com/android/ims/ImsManager.java @@ -49,9 +49,12 @@ import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsMmTelManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsService; +import android.telephony.ims.MediaQualityStatus; +import android.telephony.ims.MediaThreshold; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RegistrationManager; import android.telephony.ims.RtpHeaderExtensionType; +import android.telephony.ims.SrvccCall; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; @@ -60,6 +63,7 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.aidl.IImsSmsListener; import android.telephony.ims.aidl.ISipTransport; +import android.telephony.ims.aidl.ISrvccStartedCallback; import android.telephony.ims.feature.CapabilityChangeRequest; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; @@ -257,7 +261,7 @@ public class ImsManager implements FeatureUpdates { @VisibleForTesting public interface SubscriptionManagerProxy { boolean isValidSubscriptionId(int subId); - int[] getSubscriptionIds(int slotIndex); + int getSubscriptionId(int slotIndex); int getDefaultVoicePhoneId(); int getIntegerSubscriptionProperty(int subId, String propKey, int defValue); void setSubscriptionProperty(int subId, String propKey, String propValue); @@ -292,8 +296,8 @@ public class ImsManager implements FeatureUpdates { } @Override - public int[] getSubscriptionIds(int slotIndex) { - return getSubscriptionManager().getSubscriptionIds(slotIndex); + public int getSubscriptionId(int slotIndex) { + return SubscriptionManager.getSubscriptionId(slotIndex); } @Override @@ -1433,12 +1437,7 @@ public class ImsManager implements FeatureUpdates { } private int getSubId() { - int[] subIds = mSubscriptionManagerProxy.getSubscriptionIds(mPhoneId); - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - if (subIds != null && subIds.length >= 1) { - subId = subIds[0]; - } - return subId; + return mSubscriptionManagerProxy.getSubscriptionId(mPhoneId); } private void setWfcModeInternal(int wfcMode) { @@ -2689,11 +2688,139 @@ public class ImsManager implements FeatureUpdates { } } + /** + * Notifies the change of user setting. + * + * @param enabled indicates whether the user setting for call waiting is enabled or not. + */ + public void setTerminalBasedCallWaitingStatus(boolean enabled) throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.setTerminalBasedCallWaitingStatus(enabled); + } catch (ServiceSpecificException se) { + if (se.errorCode + == android.telephony.ims.ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) { + throw new ImsException("setTerminalBasedCallWaitingStatus()", se, + ImsReasonInfo.CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE); + } else { + throw new ImsException("setTerminalBasedCallWaitingStatus()", se, + ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR); + } + } catch (RemoteException e) { + throw new ImsException("setTerminalBasedCallWaitingStatus()", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Returns whether all of the capabilities specified are capable or not. + */ + public boolean isCapable(@ImsService.ImsServiceCapability long capabilities) + throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + return c.isCapable(capabilities); + } catch (RemoteException e) { + throw new ImsException("isCapable()", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Notifies SRVCC started. + * @param cb The callback to receive the list of {@link SrvccCall}. + */ + public void notifySrvccStarted(ISrvccStartedCallback cb) + throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.notifySrvccStarted(cb); + } catch (RemoteException e) { + throw new ImsException("notifySrvccStarted", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Notifies SRVCC is completed, IMS service will hang up all calls. + */ + public void notifySrvccCompleted() throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.notifySrvccCompleted(); + } catch (RemoteException e) { + throw new ImsException("notifySrvccCompleted", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Notifies SRVCC failed. IMS service will recover and continue calls over IMS. + */ + public void notifySrvccFailed() throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.notifySrvccFailed(); + } catch (RemoteException e) { + throw new ImsException("notifySrvccFailed", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Notifies SRVCC is canceled. IMS service will recover and continue calls over IMS. + */ + public void notifySrvccCanceled() throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.notifySrvccCanceled(); + } catch (RemoteException e) { + throw new ImsException("notifySrvccCanceled", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + + /** + * Notifies that radio triggered IMS deregistration. + * @param reason the reason why the deregistration is triggered. + */ + public void triggerDeregistration(@ImsRegistrationImplBase.ImsDeregistrationReason int reason) + throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.triggerDeregistration(reason); + } catch (RemoteException e) { + throw new ImsException("triggerDeregistration", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + public int getImsServiceState() throws ImsException { MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); return c.getFeatureState(); } + public void setMediaThreshold(@MediaQualityStatus.MediaSessionType int sessionType, + MediaThreshold threshold) throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + c.setMediaThreshold(sessionType, threshold); + } catch (RemoteException e) { + loge("setMediaThreshold Failed."); + } + } + + public MediaQualityStatus queryMediaQualityStatus ( + @MediaQualityStatus.MediaSessionType int sessionType) throws ImsException { + MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); + try { + return c.queryMediaQualityStatus(sessionType); + } catch (RemoteException e) { + loge("queryMediaQualityStatus Failed."); + return null; + } + } + @Override public void updateFeatureState(int state) { mMmTelConnectionRef.get().updateFeatureState(state); @@ -2975,6 +3102,15 @@ public class ImsManager implements FeatureUpdates { } } + public void onMemoryAvailable(int token) throws ImsException { + try { + mMmTelConnectionRef.get().onMemoryAvailable(token); + } catch (RemoteException e) { + throw new ImsException("onMemoryAvailable()", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + public void acknowledgeSms(int token, int messageRef, int result) throws ImsException { try { mMmTelConnectionRef.get().acknowledgeSms(token, messageRef, result); @@ -2984,6 +3120,15 @@ public class ImsManager implements FeatureUpdates { } } + public void acknowledgeSms(int token, int messageRef, int result, byte[] pdu) throws ImsException { + try { + mMmTelConnectionRef.get().acknowledgeSms(token, messageRef, result, pdu); + } catch (RemoteException e) { + throw new ImsException("acknowledgeSms()", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + } + public void acknowledgeSmsReport(int token, int messageRef, int result) throws ImsException{ try { mMmTelConnectionRef.get().acknowledgeSmsReport(token, messageRef, result); diff --git a/src/java/com/android/ims/ImsUt.java b/src/java/com/android/ims/ImsUt.java index d02ffaa5..17e34154 100644 --- a/src/java/com/android/ims/ImsUt.java +++ b/src/java/com/android/ims/ImsUt.java @@ -382,9 +382,9 @@ public class ImsUt implements ImsUtInterface { String[] barrList, int serviceClass, String password) { if (DBG) { if (barrList != null) { - String bList = new String(); + String bList = ""; for (int i = 0; i < barrList.length; i++) { - bList.concat(barrList[i] + " "); + bList += barrList[i] + " "; } log("updateCallBarring :: Ut=" + miUt + ", cbType=" + cbType + ", action=" + action + ", serviceClass=" + serviceClass diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java index 3170d413..c09fd94f 100644 --- a/src/java/com/android/ims/MmTelFeatureConnection.java +++ b/src/java/com/android/ims/MmTelFeatureConnection.java @@ -22,8 +22,11 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Message; import android.os.RemoteException; +import android.telephony.SubscriptionManager; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsService; +import android.telephony.ims.MediaQualityStatus; +import android.telephony.ims.MediaThreshold; import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; @@ -33,11 +36,14 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.aidl.IImsSmsListener; import android.telephony.ims.aidl.ISipTransport; +import android.telephony.ims.aidl.ISrvccStartedCallback; import android.telephony.ims.feature.CapabilityChangeRequest; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsEcbmImplBase; +import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; import android.util.Log; +import android.util.SparseArray; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; @@ -45,7 +51,7 @@ import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsUt; import java.util.ArrayList; -import java.util.Optional; +import java.util.HashMap; import java.util.Set; /** @@ -100,7 +106,6 @@ public class MmTelFeatureConnection extends FeatureConnection { } private class CapabilityCallbackManager extends ImsCallbackAdapterManager<IImsCapabilityCallback> { - public CapabilityCallbackManager(Context context, Object lock) { super(context, lock, mSlotId, mSubId); } @@ -376,6 +381,22 @@ public class MmTelFeatureConnection extends FeatureConnection { mProvisioningCallbackManager.removeCallback(callback); } + public void setMediaThreshold(@MediaQualityStatus.MediaSessionType int sessionType, + MediaThreshold threshold) throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).setMediaQualityThreshold(sessionType, threshold); + } + } + + public MediaQualityStatus queryMediaQualityStatus( + @MediaQualityStatus.MediaSessionType int sessionType) throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + return getServiceInterface(mBinder).queryMediaQualityStatus(sessionType); + } + } + public void changeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback callback) throws RemoteException { synchronized (mLock) { @@ -504,6 +525,13 @@ public class MmTelFeatureConnection extends FeatureConnection { } } + public void onMemoryAvailable(int token) throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).onMemoryAvailable(token); + } + } + public void acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result) throws RemoteException { synchronized (mLock) { @@ -512,6 +540,14 @@ public class MmTelFeatureConnection extends FeatureConnection { } } + public void acknowledgeSms(int token, int messageRef, + @ImsSmsImplBase.SendStatusResult int result, byte[] pdu) throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).acknowledgeSmsWithPdu(token, messageRef, result, pdu); + } + } + public void acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result) throws RemoteException { synchronized (mLock) { @@ -541,6 +577,45 @@ public class MmTelFeatureConnection extends FeatureConnection { } } + public void notifySrvccStarted(ISrvccStartedCallback cb) + throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).notifySrvccStarted(cb); + } + } + + public void notifySrvccCompleted() throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).notifySrvccCompleted(); + } + } + + public void notifySrvccFailed() throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).notifySrvccFailed(); + } + } + + public void notifySrvccCanceled() throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).notifySrvccCanceled(); + } + } + + public void triggerDeregistration(@ImsRegistrationImplBase.ImsDeregistrationReason int reason) + throws RemoteException { + IImsRegistration registration = getRegistration(); + if (registration != null) { + registration.triggerDeregistration(reason); + } else { + Log.e(TAG + " [" + mSlotId + "]", "triggerDeregistration IImsRegistration is null"); + } + } + public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency, String[] numbers) throws RemoteException { if (isEmergency && !isEmergencyMmTelAvailable()) { @@ -576,6 +651,19 @@ public class MmTelFeatureConnection extends FeatureConnection { } } + /** + * Notifies the MmTelFeature of the enablement status of terminal based call waiting + * + * @param enabled indicates whether the user setting for call waiting is enabled or not. + */ + public void setTerminalBasedCallWaitingStatus(boolean enabled) + throws RemoteException { + synchronized (mLock) { + checkServiceIsReady(); + getServiceInterface(mBinder).setTerminalBasedCallWaitingStatus(enabled); + } + } + private IImsMmTelFeature getServiceInterface(IBinder b) { return IImsMmTelFeature.Stub.asInterface(b); } diff --git a/src/java/com/android/ims/RcsFeatureManager.java b/src/java/com/android/ims/RcsFeatureManager.java index e034a68d..e3f50c34 100644 --- a/src/java/com/android/ims/RcsFeatureManager.java +++ b/src/java/com/android/ims/RcsFeatureManager.java @@ -16,6 +16,8 @@ package com.android.ims; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.net.Uri; import android.os.IBinder; @@ -30,6 +32,7 @@ import android.telephony.ims.ImsException; import android.telephony.ims.ImsService; import android.telephony.ims.RcsUceAdapter.StackPublishTriggerType; import android.telephony.ims.RegistrationManager; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; @@ -44,7 +47,6 @@ import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.feature.CapabilityChangeRequest; import android.telephony.ims.feature.ImsFeature; -import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; @@ -96,8 +98,7 @@ public class RcsFeatureManager implements FeatureUpdates { * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+) * codes in order to keep the AOSP stack up to date. */ - void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText); + void onPublishUpdated(SipDetails details); /** * Receive a capabilities request from the remote client. @@ -123,10 +124,9 @@ public class RcsFeatureManager implements FeatureUpdates { } @Override - public void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { - mCapabilityEventCallback.forEach(callback -> callback.onPublishUpdated( - reasonCode, reasonPhrase, reasonHeaderCause, reasonHeaderText)); + public void onPublishUpdated(@NonNull SipDetails details) { + mCapabilityEventCallback.forEach( + callback ->callback.onPublishUpdated(details)); } @Override @@ -604,38 +604,15 @@ public class RcsFeatureManager implements FeatureUpdates { mRcsFeatureConnection.updateFeatureCapabilities(capabilities); } - /** - * Testing interface used to mock SubscriptionManager in testing - * @hide - */ - @VisibleForTesting - public interface SubscriptionManagerProxy { - /** - * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing. - */ - int getSubId(int slotId); - } - public IImsConfig getConfig() { return mRcsFeatureConnection.getConfig(); } - private static SubscriptionManagerProxy sSubscriptionManagerProxy - = slotId -> { - int[] subIds = SubscriptionManager.getSubId(slotId); - if (subIds != null) { - return subIds[0]; - } - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - }; - /** - * Testing function used to mock SubscriptionManager in testing - * @hide + * @return the subscription ID associated with this ImsService connection. */ - @VisibleForTesting - public static void setSubscriptionManager(SubscriptionManagerProxy proxy) { - sSubscriptionManagerProxy = proxy; + public int getSubId() { + return mRcsFeatureConnection.getSubId(); } private void log(String s) { diff --git a/src/java/com/android/ims/rcs/uce/UceController.java b/src/java/com/android/ims/rcs/uce/UceController.java index 6fb27b06..aeab0611 100644 --- a/src/java/com/android/ims/rcs/uce/UceController.java +++ b/src/java/com/android/ims/rcs/uce/UceController.java @@ -18,6 +18,7 @@ package com.android.ims.rcs.uce; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.net.Uri; import android.os.HandlerThread; @@ -28,6 +29,7 @@ import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.RcsUceAdapter.PublishState; import android.telephony.ims.RcsUceAdapter.StackPublishTriggerType; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IOptionsRequestCallback; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; @@ -212,7 +214,7 @@ public class UceController { private static class CachedCapabilityEvent { private Optional<Integer> mRequestPublishCapabilitiesEvent; private Optional<Boolean> mUnpublishEvent; - private Optional<SomeArgs> mPublishUpdatedEvent; + private Optional<SipDetails> mPublishUpdatedEvent; private Optional<SomeArgs> mRemoteCapabilityRequestEvent; public CachedCapabilityEvent() { @@ -239,14 +241,8 @@ public class UceController { /** * Cache the publish update event triggered by the ImsService. */ - public synchronized void setOnPublishUpdatedEvent(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = reasonCode; - args.arg2 = reasonPhrase; - args.arg3 = reasonHeaderCause; - args.arg4 = reasonHeaderText; - mPublishUpdatedEvent = Optional.of(args); + public synchronized void setOnPublishUpdatedEvent(SipDetails details) { + mPublishUpdatedEvent = Optional.of(details); } /** @@ -272,7 +268,7 @@ public class UceController { } /** @Return the cached pubilsh update event */ - public synchronized Optional<SomeArgs> getPublishUpdatedEvent() { + public synchronized Optional<SipDetails> getPublishUpdatedEvent() { return mPublishUpdatedEvent; } @@ -285,7 +281,6 @@ public class UceController { public synchronized void clear() { mRequestPublishCapabilitiesEvent = Optional.empty(); mUnpublishEvent = Optional.empty(); - mPublishUpdatedEvent.ifPresent(args -> args.recycle()); mPublishUpdatedEvent = Optional.empty(); mRemoteCapabilityRequestEvent.ifPresent(args -> args.recycle()); mRemoteCapabilityRequestEvent = Optional.empty(); @@ -489,14 +484,9 @@ public class UceController { Optional<Boolean> unpublishEvent = mCachedCapabilityEvent.getUnpublishEvent(); unpublishEvent.ifPresent(unpublish -> onUnpublish()); - Optional<SomeArgs> publishUpdatedEvent = mCachedCapabilityEvent.getPublishUpdatedEvent(); - publishUpdatedEvent.ifPresent(args -> { - int reasonCode = (Integer) args.arg1; - String reasonPhrase = (String) args.arg2; - int reasonHeaderCause = (Integer) args.arg3; - String reasonHeaderText = (String) args.arg4; - onPublishUpdated(reasonCode, reasonPhrase, reasonHeaderCause, reasonHeaderText); - }); + Optional<SipDetails> publishUpdatedEvent = mCachedCapabilityEvent.getPublishUpdatedEvent(); + publishUpdatedEvent.ifPresent(details -> + onPublishUpdated(details)); Optional<SomeArgs> remoteRequest = mCachedCapabilityEvent.getRemoteCapabilityRequestEvent(); remoteRequest.ifPresent(args -> { @@ -606,15 +596,12 @@ public class UceController { } @Override - public void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + public void onPublishUpdated(@NonNull SipDetails details) { if (isRcsConnecting()) { - mCachedCapabilityEvent.setOnPublishUpdatedEvent(reasonCode, reasonPhrase, - reasonHeaderCause, reasonHeaderText); + mCachedCapabilityEvent.setOnPublishUpdatedEvent(details); return; } - UceController.this.onPublishUpdated(reasonCode, reasonPhrase, - reasonHeaderCause, reasonHeaderText); + UceController.this.onPublishUpdated(details); } @Override @@ -648,14 +635,14 @@ public class UceController { if (uriList == null || uriList.isEmpty() || c == null) { logw("requestCapabilities: parameter is empty"); if (c != null) { - c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); } return; } if (isUnavailable()) { logw("requestCapabilities: controller is unavailable"); - c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); return; } @@ -668,7 +655,7 @@ public class UceController { long retryAfterMillis = deviceStateResult.getRequestRetryAfterMillis(); logw("requestCapabilities: The device is disallowed, deviceState= " + deviceState + ", errorCode=" + errorCode + ", retryAfterMillis=" + retryAfterMillis); - c.onError(errorCode, retryAfterMillis); + c.onError(errorCode, retryAfterMillis, null); return; } @@ -687,14 +674,14 @@ public class UceController { if (uri == null || c == null) { logw("requestAvailability: parameter is empty"); if (c != null) { - c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); } return; } if (isUnavailable()) { logw("requestAvailability: controller is unavailable"); - c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); return; } @@ -707,7 +694,7 @@ public class UceController { long retryAfterMillis = deviceStateResult.getRequestRetryAfterMillis(); logw("requestAvailability: The device is disallowed, deviceState= " + deviceState + ", errorCode=" + errorCode + ", retryAfterMillis=" + retryAfterMillis); - c.onError(errorCode, retryAfterMillis); + c.onError(errorCode, retryAfterMillis, null); return; } @@ -740,11 +727,9 @@ public class UceController { * This method is triggered by the ImsService to notify framework that the device's * publish status has been changed. */ - public void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + public void onPublishUpdated(@NonNull SipDetails details) { logi("onPublishUpdated"); - mPublishController.onPublishUpdated(reasonCode, reasonPhrase, - reasonHeaderCause, reasonHeaderText); + mPublishController.onPublishUpdated(details); } /** diff --git a/src/java/com/android/ims/rcs/uce/eab/EabBulkCapabilityUpdater.java b/src/java/com/android/ims/rcs/uce/eab/EabBulkCapabilityUpdater.java index b4406fdd..738a4fc8 100644 --- a/src/java/com/android/ims/rcs/uce/eab/EabBulkCapabilityUpdater.java +++ b/src/java/com/android/ims/rcs/uce/eab/EabBulkCapabilityUpdater.java @@ -37,6 +37,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.ims.ImsManager; import android.telephony.ims.ImsRcsManager; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.util.Log; @@ -142,12 +143,12 @@ public final class EabBulkCapabilityUpdater { } @Override - public void onComplete() { + public void onComplete(SipDetails details) { Log.d(TAG, "onComplete"); } @Override - public void onError(int errorCode, long retryAfterMilliseconds) { + public void onError(int errorCode, long retryAfterMilliseconds, SipDetails details) { Log.d(TAG, "Refresh capabilities failed. Error code: " + errorCode + ", retryAfterMilliseconds: " + retryAfterMilliseconds); if (retryAfterMilliseconds != 0) { diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfo.java b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfo.java index dc794331..49291484 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfo.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfo.java @@ -39,10 +39,13 @@ import android.util.Log; import com.android.ims.rcs.uce.util.FeatureTags; import com.android.ims.rcs.uce.util.UceUtils; +import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -101,10 +104,14 @@ public class DeviceCapabilityInfo { // Whether the settings are changed or not private int mTtyPreferredMode; - private boolean mAirplaneMode; private boolean mMobileData; private boolean mVtSetting; + // The service description associated with the last publication update. + private final Set<ServiceDescription> mLastSuccessfulCapabilities = new ArraySet<>(); + // The service description to temporarily store the presence capability being sent. + private Set<ServiceDescription> mPendingPublishCapabilities; + public DeviceCapabilityInfo(int subId, String[] capToRegistrationMap) { mSubId = subId; mServiceCapRegTracker = PublishServiceDescTracker.fromCarrierConfig(capToRegistrationMap); @@ -121,12 +128,13 @@ public class DeviceCapabilityInfo { mRcsRegistered = false; mRcsNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; mTtyPreferredMode = TelecomManager.TTY_MODE_OFF; - mAirplaneMode = false; mMobileData = true; mVtSetting = true; mMmTelCapabilities = new MmTelCapabilities(); mMmtelAssociatedUris = Collections.EMPTY_LIST; mRcsAssociatedUris = Collections.EMPTY_LIST; + mLastSuccessfulCapabilities.clear(); + mPendingPublishCapabilities = null; } /** @@ -169,12 +177,17 @@ public class DeviceCapabilityInfo { /** * Update the status that IMS MMTEL is unregistered. */ - public synchronized void updateImsMmtelUnregistered() { + public synchronized boolean updateImsMmtelUnregistered() { logi("IMS MMTEL unregistered: original state=" + mMmtelRegistered); + boolean changed = false; if (mMmtelRegistered) { mMmtelRegistered = false; + changed = true; } mMmtelNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + mLastSuccessfulCapabilities.clear(); + mPendingPublishCapabilities = null; + return changed; } /** @@ -242,7 +255,12 @@ public class DeviceCapabilityInfo { mRcsRegistered = false; changed = true; } + + mLastRegistrationFeatureTags = Collections.emptySet(); + updateRegistration(mLastRegistrationFeatureTags); mRcsNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + mLastSuccessfulCapabilities.clear(); + mPendingPublishCapabilities = null; return changed; } @@ -373,19 +391,6 @@ public class DeviceCapabilityInfo { } /** - * Update airplane mode state. - * @return {@code true} if the airplane mode is changed, {@code false} otherwise. - */ - public synchronized boolean updateAirplaneMode(boolean state) { - if (mAirplaneMode != state) { - logd("Airplane mode changes from " + mAirplaneMode + " to " + state); - mAirplaneMode = state; - return true; - } - return false; - } - - /** * Update mobile data setting. * @return {@code true} if the mobile data setting is changed, {@code false} otherwise. */ @@ -454,6 +459,67 @@ public class DeviceCapabilityInfo { return mPresenceCapable; } + // Get the device's capabilities with the PRESENCE mechanism. + public RcsContactUceCapability getChangedPresenceCapability(Context context) { + if (context == null) { + return null; + } + Set<ServiceDescription> capableFromReg = + mServiceCapRegTracker.copyRegistrationCapabilities(); + if (isPresenceCapabilityChanged(capableFromReg)) { + RcsContactUceCapability rcsContactUceCapability = getPresenceCapabilities(context); + if (rcsContactUceCapability != null) { + mPendingPublishCapabilities = mServiceCapRegTracker.copyRegistrationCapabilities(); + } + return rcsContactUceCapability; + } + return null; + } + + public void setPresencePublishResult(boolean isSuccess) { + if (isSuccess) { + mLastSuccessfulCapabilities.clear(); + if (mPendingPublishCapabilities != null) { + mLastSuccessfulCapabilities.addAll(mPendingPublishCapabilities); + } + } + mPendingPublishCapabilities = null; + } + + public void resetPresenceCapability() { + mLastSuccessfulCapabilities.clear(); + mPendingPublishCapabilities = null; + } + + public List<RcsContactPresenceTuple> getLastSuccessfulPresenceTuplesWithoutContactUri() { + List<RcsContactPresenceTuple> presenceTuples = new ArrayList<>(); + if (mLastSuccessfulCapabilities.isEmpty()) { + return presenceTuples; + } + + for (ServiceDescription capability : mLastSuccessfulCapabilities) { + presenceTuples.add(capability.getTupleBuilder().build()); + } + return presenceTuples; + } + + @VisibleForTesting + public void addLastSuccessfulServiceDescription(ServiceDescription capability) { + mLastSuccessfulCapabilities.add(capability); + } + + @VisibleForTesting + public boolean isPresenceCapabilityChanged(Set<ServiceDescription> capableFromReg) { + if (mLastSuccessfulCapabilities.isEmpty()) { + return true; + } + + if (capableFromReg.equals(mLastSuccessfulCapabilities)) { + return false; + } + return true; + } + private boolean isVolteAvailable(int networkRegType, MmTelCapabilities capabilities) { return (networkRegType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) && capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); @@ -486,7 +552,12 @@ public class DeviceCapabilityInfo { @CapabilityMechanism int mechanism, Context context) { switch (mechanism) { case RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE: - return getPresenceCapabilities(context); + RcsContactUceCapability rcsContactUceCapability = getPresenceCapabilities(context); + if (rcsContactUceCapability != null) { + mPendingPublishCapabilities = + mServiceCapRegTracker.copyRegistrationCapabilities(); + } + return rcsContactUceCapability; case RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS: return getOptionsCapabilities(context); default: diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java index 442cf7da..b58f7ec6 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java @@ -162,6 +162,8 @@ public class DeviceCapabilityListener { public void sendImsUnregisteredMessage() { logd("sendImsUnregisteredMessage"); + // The IMS has been unregistered. Remove the existing message not processed. + removeMessages(EVENT_REQUEST_PUBLISH); // Remove the existing message and resend a new message. removeMessages(EVENT_IMS_UNREGISTERED); Message msg = obtainMessage(EVENT_IMS_UNREGISTERED); @@ -272,9 +274,9 @@ public class DeviceCapabilityListener { private void registerReceivers() { logd("registerReceivers"); IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED); - mContext.registerReceiver(mReceiver, filter); + mContext.registerReceiver(mReceiver, filter, android.Manifest.permission.MODIFY_PHONE_STATE, + null, Context.RECEIVER_EXPORTED); ContentResolver resolver = mContext.getContentResolver(); if (resolver != null) { @@ -389,11 +391,6 @@ public class DeviceCapabilityListener { TelecomManager.TTY_MODE_OFF); handleTtyPreferredModeChanged(preferredMode); break; - - case Intent.ACTION_AIRPLANE_MODE_CHANGED: - boolean airplaneMode = intent.getBooleanExtra("state", false); - handleAirplaneModeChanged(airplaneMode); - break; } } }; @@ -492,7 +489,7 @@ public class DeviceCapabilityListener { public void onSubscriberAssociatedUriChanged(Uri[] uris) { synchronized (mLock) { logi("onRcsSubscriberAssociatedUriChanged"); - handleRcsSubscriberAssociatedUriChanged(uris, true); + handleRcsSubscriberAssociatedUriChanged(uris, false); } } }; @@ -523,7 +520,7 @@ public class DeviceCapabilityListener { public void onSubscriberAssociatedUriChanged(Uri[] uris) { synchronized (mLock) { logi("onMmTelSubscriberAssociatedUriChanged"); - handleMmTelSubscriberAssociatedUriChanged(uris, true); + handleMmTelSubscriberAssociatedUriChanged(uris, false); } } }; @@ -571,15 +568,6 @@ public class DeviceCapabilityListener { } } - private void handleAirplaneModeChanged(boolean state) { - boolean isChanged = mCapabilityInfo.updateAirplaneMode(state); - logi("Airplane mode changed: " + state + ", isChanged="+ isChanged); - if (isChanged) { - mHandler.sendTriggeringPublishMessage( - PublishController.PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE); - } - } - private void handleMobileDataChanged(boolean isEnabled) { boolean isChanged = mCapabilityInfo.updateMobileData(isEnabled); logi("Mobile data changed: " + isEnabled + ", isChanged=" + isChanged); @@ -602,18 +590,18 @@ public class DeviceCapabilityListener { * This method is called when the MMTEL is registered. */ private void handleImsMmtelRegistered(int imsTransportType) { + // update capability, but not trigger PUBLISH message. + // PUBLISH message will be sent when the Capability status changed callback is called. mCapabilityInfo.updateImsMmtelRegistered(imsTransportType); - mHandler.sendTriggeringPublishMessage( - PublishController.PUBLISH_TRIGGER_MMTEL_REGISTERED); } /* * This method is called when the MMTEL is unregistered. */ private void handleImsMmtelUnregistered() { - mCapabilityInfo.updateImsMmtelUnregistered(); + boolean hasChanged = mCapabilityInfo.updateImsMmtelUnregistered(); // When the MMTEL is unregistered, the mmtel associated uri should be cleared. - handleMmTelSubscriberAssociatedUriChanged(null, false); + handleMmTelSubscriberAssociatedUriChanged(null, hasChanged); // If the RCS is already unregistered, it informs that the IMS is unregistered. if (mCapabilityInfo.isImsRegistered() == false) { @@ -624,16 +612,17 @@ public class DeviceCapabilityListener { /* * This method is called when the MMTEL associated uri has changed. */ - private void handleMmTelSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish) { + private void handleMmTelSubscriberAssociatedUriChanged(Uri[] uris, boolean regiChanged) { Uri originalUri = mCapabilityInfo.getMmtelAssociatedUri(); mCapabilityInfo.updateMmTelAssociatedUri(uris); Uri currentUri = mCapabilityInfo.getMmtelAssociatedUri(); - boolean hasChanged = !(Objects.equals(originalUri, currentUri)); - logi("handleMmTelSubscriberAssociatedUriChanged: triggerPublish=" + triggerPublish + - ", hasChanged=" + hasChanged); + boolean hasChanged = regiChanged || !(Objects.equals(originalUri, currentUri)); - if (triggerPublish && hasChanged) { + logi("handleMmTelSubscriberAssociatedUriChanged: hasChanged=" + hasChanged); + + // Send internal request to send a modification PUBLISH if the MMTEL or RCS is registered. + if (mCapabilityInfo.isImsRegistered() && hasChanged) { mHandler.sendTriggeringPublishMessage( PublishController.PUBLISH_TRIGGER_MMTEL_URI_CHANGE); } @@ -663,7 +652,7 @@ public class DeviceCapabilityListener { private void handleImsRcsUnregistered() { boolean hasChanged = mCapabilityInfo.updateImsRcsUnregistered(); // When the RCS is unregistered, the rcs associated uri should be cleared. - handleRcsSubscriberAssociatedUriChanged(null, false); + handleRcsSubscriberAssociatedUriChanged(null, hasChanged); // If the MMTEL is already unregistered, it informs that the IMS is unregistered. if (mCapabilityInfo.isImsRegistered() == false) { mHandler.sendImsUnregisteredMessage(); @@ -673,16 +662,17 @@ public class DeviceCapabilityListener { /* * This method is called when the RCS associated uri has changed. */ - private void handleRcsSubscriberAssociatedUriChanged(Uri[] uris, boolean triggerPublish) { + private void handleRcsSubscriberAssociatedUriChanged(Uri[] uris, boolean regiChanged) { Uri originalUri = mCapabilityInfo.getRcsAssociatedUri(); mCapabilityInfo.updateRcsAssociatedUri(uris); Uri currentUri = mCapabilityInfo.getRcsAssociatedUri(); - boolean hasChanged = !(Objects.equals(originalUri, currentUri)); - logi("handleRcsSubscriberAssociatedUriChanged: triggerPublish=" + triggerPublish + - ", hasChanged=" + hasChanged); + boolean hasChanged = regiChanged || !(Objects.equals(originalUri, currentUri)); + + logi("handleRcsSubscriberAssociatedUriChanged: hasChanged=" + hasChanged); - if (triggerPublish && hasChanged) { + // Send internal request to send a modification PUBLISH if the MMTEL or RCS is registered. + if (mCapabilityInfo.isImsRegistered() && hasChanged) { mHandler.sendTriggeringPublishMessage(PublishController.PUBLISH_TRIGGER_RCS_URI_CHANGE); } } diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishController.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishController.java index b20f5ceb..732a558b 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishController.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishController.java @@ -18,9 +18,11 @@ package com.android.ims.rcs.uce.presence.publish; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; import android.telephony.ims.RcsUceAdapter.PublishState; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import com.android.ims.rcs.uce.ControllerBase; @@ -45,53 +47,49 @@ public interface PublishController extends ControllerBase { /** Publish trigger type: TTY preferred changes */ int PUBLISH_TRIGGER_TTY_PREFERRED_CHANGE = 3; - /** Publish trigger type: Airplane mode changes */ - int PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE = 4; - /** Publish trigger type: Mobile data changes */ - int PUBLISH_TRIGGER_MOBILE_DATA_CHANGE = 5; + int PUBLISH_TRIGGER_MOBILE_DATA_CHANGE = 4; /** Publish trigger type: VT setting changes */ - int PUBLISH_TRIGGER_VT_SETTING_CHANGE = 6; + int PUBLISH_TRIGGER_VT_SETTING_CHANGE = 5; /** Publish trigger type: MMTEL registered */ - int PUBLISH_TRIGGER_MMTEL_REGISTERED = 7; + int PUBLISH_TRIGGER_MMTEL_REGISTERED = 6; /** Publish trigger type: MMTEL unregistered */ - int PUBLISH_TRIGGER_MMTEL_UNREGISTERED = 8; + int PUBLISH_TRIGGER_MMTEL_UNREGISTERED = 7; /** Publish trigger type: MMTEL capability changes */ - int PUBLISH_TRIGGER_MMTEL_CAPABILITY_CHANGE = 9; + int PUBLISH_TRIGGER_MMTEL_CAPABILITY_CHANGE = 8; /** Publish trigger type: MMTEL associated uri changes */ - int PUBLISH_TRIGGER_MMTEL_URI_CHANGE = 10; + int PUBLISH_TRIGGER_MMTEL_URI_CHANGE = 9; /** Publish trigger type: RCS registered */ - int PUBLISH_TRIGGER_RCS_REGISTERED = 11; + int PUBLISH_TRIGGER_RCS_REGISTERED = 10; /** Publish trigger type: RCS unregistered */ - int PUBLISH_TRIGGER_RCS_UNREGISTERED = 12; + int PUBLISH_TRIGGER_RCS_UNREGISTERED = 11; /** Publish trigger type: RCS associated uri changes */ - int PUBLISH_TRIGGER_RCS_URI_CHANGE = 13; + int PUBLISH_TRIGGER_RCS_URI_CHANGE = 12; /** Publish trigger type: provisioning changes */ - int PUBLISH_TRIGGER_PROVISIONING_CHANGE = 14; + int PUBLISH_TRIGGER_PROVISIONING_CHANGE = 13; /**The caps have been overridden for a test*/ - int PUBLISH_TRIGGER_OVERRIDE_CAPS = 15; + int PUBLISH_TRIGGER_OVERRIDE_CAPS = 14; /** The Carrier Config for the subscription has Changed **/ - int PUBLISH_TRIGGER_CARRIER_CONFIG_CHANGED = 16; + int PUBLISH_TRIGGER_CARRIER_CONFIG_CHANGED = 15; /** MMTEL and RCS are unregistered. **/ - int PUBLISH_TRIGGER_MMTEL_RCS_UNREGISTERED = 17; + int PUBLISH_TRIGGER_MMTEL_RCS_UNREGISTERED = 16; @IntDef(value = { PUBLISH_TRIGGER_SERVICE, PUBLISH_TRIGGER_RETRY, PUBLISH_TRIGGER_TTY_PREFERRED_CHANGE, - PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE, PUBLISH_TRIGGER_MOBILE_DATA_CHANGE, PUBLISH_TRIGGER_VT_SETTING_CHANGE, PUBLISH_TRIGGER_MMTEL_REGISTERED, @@ -142,7 +140,8 @@ public interface PublishController extends ControllerBase { /** * Update the publish request result. */ - void updatePublishRequestResult(int publishState, Instant updatedTimestamp, String pidfXml); + void updatePublishRequestResult(int publishState, Instant updatedTimestamp, String pidfXml, + SipDetails details); /** * Update the value of the publish throttle. @@ -212,9 +211,7 @@ public interface PublishController extends ControllerBase { /** * Notify that the device's publish status have been changed. */ - void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText); - + void onPublishUpdated(@NonNull SipDetails details); /** * Retrieve the device's capabilities. */ diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java index 034d9ac2..101aa810 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java @@ -16,7 +16,10 @@ package com.android.ims.rcs.uce.presence.publish; +import static android.telephony.ims.RcsUceAdapter.PUBLISH_STATE_OK; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.os.Build; import android.os.Handler; @@ -27,10 +30,13 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.CarrierConfigManager; import android.telephony.ims.ImsException; +import android.telephony.ims.PublishAttributes; +import android.telephony.ims.RcsContactPresenceTuple; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.RcsUceAdapter.PublishState; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities; @@ -53,6 +59,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.time.Instant; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -198,7 +205,7 @@ public class PublishControllerImpl implements PublishController { if (capabilityType == RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE) { return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED; } else if (capabilityType == RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE) { - return RcsUceAdapter.PUBLISH_STATE_OK; + return PUBLISH_STATE_OK; } else { return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR; } @@ -303,7 +310,7 @@ public class PublishControllerImpl implements PublishController { boolean supportPublishingState) { synchronized (mPublishStateLock) { if (mIsDestroyedFlag) return; - mPublishStateCallbacks.register(c, new Boolean(supportPublishingState)); + mPublishStateCallbacks.register(c, Boolean.valueOf(supportPublishingState)); logd("registerPublishStateCallback: size=" + mPublishStateCallbacks.getRegisteredCallbackCount()); } @@ -363,11 +370,9 @@ public class PublishControllerImpl implements PublishController { * Notify that the device's publish status have been changed. */ @Override - public void onPublishUpdated(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + public void onPublishUpdated(@NonNull SipDetails details) { if (mIsDestroyedFlag) return; - mPublishHandler.sendPublishUpdatedMessage(reasonCode, reasonPhrase, reasonHeaderCause, - reasonHeaderText); + mPublishHandler.sendPublishUpdatedMessage(details); } @Override @@ -412,9 +417,10 @@ public class PublishControllerImpl implements PublishController { @Override public void updatePublishRequestResult(@PublishState int state, - Instant updatedTime, String pidfXml) { + Instant updatedTime, String pidfXml, SipDetails details) { logd("updatePublishRequestResult: " + state + ", time=" + updatedTime); - mPublishHandler.sendPublishStateChangedMessage(state, updatedTime, pidfXml); + mPublishHandler.sendPublishStateChangedMessage(state, updatedTime, pidfXml, + details); } @Override @@ -514,9 +520,10 @@ public class PublishControllerImpl implements PublishController { int newPublishState = (Integer) args.arg1; Instant updatedTimestamp = (Instant) args.arg2; String pidfXml = (String) args.arg3; + SipDetails details = (SipDetails) args.arg4; args.recycle(); publishCtrl.handlePublishStateChangedMessage(newPublishState, updatedTimestamp, - pidfXml); + pidfXml, details); break; } case MSG_NOTIFY_CURRENT_PUBLISH_STATE: @@ -566,14 +573,8 @@ public class PublishControllerImpl implements PublishController { break; case MSG_PUBLISH_UPDATED: { - SomeArgs args = (SomeArgs) message.obj; - int reasonCode = (Integer) args.arg1; - String reasonPhrase = (String) args.arg2; - int reasonHeaderCause = (Integer) args.arg3; - String reasonHeaderText = (String) args.arg4; - args.recycle(); - publishCtrl.handlePublishUpdatedMessage(reasonCode, reasonPhrase, - reasonHeaderCause, reasonHeaderText); + SipDetails details = (SipDetails) message.obj; + publishCtrl.handlePublishUpdatedMessage(details); break; } @@ -654,7 +655,7 @@ public class PublishControllerImpl implements PublishController { * Send the message to notify the publish state is changed. */ public void sendPublishStateChangedMessage(@PublishState int publishState, - @NonNull Instant updatedTimestamp, String pidfXml) { + @NonNull Instant updatedTimestamp, String pidfXml, SipDetails details) { PublishControllerImpl publishCtrl = mPublishControllerRef.get(); if (publishCtrl == null) return; if (publishCtrl.mIsDestroyedFlag) return; @@ -663,6 +664,7 @@ public class PublishControllerImpl implements PublishController { args.arg1 = publishState; args.arg2 = updatedTimestamp; args.arg3 = pidfXml; + args.arg4 = details; Message message = obtainMessage(); message.what = MSG_PUBLISH_STATE_CHANGED; message.obj = args; @@ -689,20 +691,14 @@ public class PublishControllerImpl implements PublishController { /** * Send the message to notify the publish state is changed. */ - public void sendPublishUpdatedMessage(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + public void sendPublishUpdatedMessage(@NonNull SipDetails details) { PublishControllerImpl publishCtrl = mPublishControllerRef.get(); if (publishCtrl == null) return; if (publishCtrl.mIsDestroyedFlag) return; - SomeArgs args = SomeArgs.obtain(); - args.arg1 = reasonCode; - args.arg2 = reasonPhrase; - args.arg3 = reasonHeaderCause; - args.arg4 = reasonHeaderText; Message message = obtainMessage(); message.what = MSG_PUBLISH_UPDATED; - message.obj = args; + message.obj = details; sendMessage(message); } @@ -924,7 +920,7 @@ public class PublishControllerImpl implements PublishController { // PUBLISH is enabled. if (isPresencePublishEnabled()) { handlePublishStateChangedMessage(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, - Instant.now(), null /*pidfXml*/); + Instant.now(), null /*pidfXml*/, null /*SipDetails*/); } } @@ -1011,7 +1007,8 @@ public class PublishControllerImpl implements PublishController { // Update the publish state directly. Because this method is called in the // handler thread already, the process of updating publish state does not need to be // sent to the looper again. - handlePublishStateChangedMessage(updatedPublishState, Instant.now(), null /*pidfxml*/); + handlePublishStateChangedMessage(updatedPublishState, Instant.now(), null /*pidfxml*/, + null /*SipDetails*/); } } @@ -1040,7 +1037,7 @@ public class PublishControllerImpl implements PublishController { * from original state. */ private void handlePublishStateChangedMessage(@PublishState int newPublishState, - Instant updatedTimestamp, String pidfXml) { + Instant updatedTimestamp, String pidfXml, SipDetails details) { synchronized (mPublishStateLock) { if (mIsDestroyedFlag) return; // Check if the time of the given publish state is not earlier than existing time. @@ -1067,7 +1064,7 @@ public class PublishControllerImpl implements PublishController { logd("Notify publish state changed: " + mCurrentPublishState); mPublishStateCallbacks.broadcast(c -> { try { - c.onPublishStateChanged(mCurrentPublishState); + c.onPublishUpdated(getPublishAttributes(mCurrentPublishState, details)); } catch (RemoteException e) { logw("Notify publish state changed error: " + e); } @@ -1075,11 +1072,25 @@ public class PublishControllerImpl implements PublishController { logd("Notify publish state changed: completed"); } + private PublishAttributes getPublishAttributes(@PublishState int mCurrentPublishState, + SipDetails details) { + List<RcsContactPresenceTuple> tuples = null; + if (mCurrentPublishState == PUBLISH_STATE_OK) { + tuples = mDeviceCapabilityInfo.getLastSuccessfulPresenceTuplesWithoutContactUri(); + } + if (tuples != null && !tuples.isEmpty()) { + return new PublishAttributes.Builder(mCurrentPublishState).setSipDetails(details) + .setPresenceTuples(tuples).build(); + } + return new PublishAttributes.Builder(mCurrentPublishState).setSipDetails(details).build(); + } + private void handleNotifyCurrentPublishStateMessage(IRcsUcePublishStateCallback callback, boolean supportPublishingState) { if (mIsDestroyedFlag || callback == null) return; try { - callback.onPublishStateChanged(getUcePublishState(supportPublishingState)); + int publishState = getUcePublishState(supportPublishingState); + callback.onPublishUpdated(getPublishAttributes(publishState, null /*SipDetails*/)); } catch (RemoteException e) { logw("handleCurrentPublishStateUpdateMessage exception: " + e); } @@ -1146,7 +1157,8 @@ public class PublishControllerImpl implements PublishController { Instant updatedTimestamp) { if (mIsDestroyedFlag) return; mPublishProcessor.resetState(); - handlePublishStateChangedMessage(newPublishState, updatedTimestamp, null); + handlePublishStateChangedMessage(newPublishState, updatedTimestamp, + null /*pidfXml*/, null /*SipDetails*/); } private void handlePublishSentMessage() { @@ -1170,20 +1182,22 @@ public class PublishControllerImpl implements PublishController { mCurrentPublishState = RcsUceAdapter.PUBLISH_STATE_PUBLISHING; if (isSupportPublishingState) { if (callback != null) { - callback.onPublishStateChanged(mCurrentPublishState); + callback.onPublishUpdated(getPublishAttributes(mCurrentPublishState, + null /*SipDetails*/)); } } else { // If it is currently PUBLISH_STATE_OK, the state must not be changed to // PUBLISH_STATE_NOT_PUBLISHED. // And in the case of the current PUBLISH_STATE_NOT_PUBLISHED, it is // necessary to avoid reporting the duplicate state. - if (tempPublishState != RcsUceAdapter.PUBLISH_STATE_OK + if (tempPublishState != PUBLISH_STATE_OK && tempPublishState != RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED) { // set the state to PUBLISH_STATE_NOT_PUBLISHED so that // getUcePublishState is consistent with the callback mLastPublishState = RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED; if (callback != null) { - callback.onPublishStateChanged(mLastPublishState); + callback.onPublishUpdated(getPublishAttributes(mLastPublishState, + null /*SipDetails*/)); } } } @@ -1194,11 +1208,10 @@ public class PublishControllerImpl implements PublishController { } } - private void handlePublishUpdatedMessage(int reasonCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + private void handlePublishUpdatedMessage(@NonNull SipDetails details) { if (mIsDestroyedFlag) return; PublishRequestResponse updatedPublish = new PublishRequestResponse(getLastPidfXml(), - reasonCode, reasonPhrase, reasonHeaderCause, reasonHeaderText); + details); mPublishProcessor.publishUpdated(updatedPublish); } diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java index d87eea9e..c239dd5a 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.os.RemoteException; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.SipDetails; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.text.TextUtils; @@ -112,6 +113,8 @@ public class PublishProcessor { logi("onRcsDisconnected"); mRcsFeatureManager = null; mProcessorState.onRcsDisconnected(); + // reset the publish capabilities. + mDeviceCapabilities.resetPresenceCapability(); } /** @@ -152,10 +155,15 @@ public class PublishProcessor { } // Get the latest device's capabilities. - RcsContactUceCapability deviceCapability = - mDeviceCapabilities.getDeviceCapabilities(CAPABILITY_MECHANISM_PRESENCE, mContext); + RcsContactUceCapability deviceCapability; + if (triggerType == PublishController.PUBLISH_TRIGGER_SERVICE) { + deviceCapability = mDeviceCapabilities.getDeviceCapabilities( + CAPABILITY_MECHANISM_PRESENCE, mContext); + } else { + deviceCapability = mDeviceCapabilities.getChangedPresenceCapability(mContext); + } if (deviceCapability == null) { - logw("doPublishInternal: device capability is null"); + logi("doPublishInternal: device capability hasn't changed or is null"); return false; } @@ -349,6 +357,8 @@ public class PublishProcessor { // Increase the retry count mProcessorState.increaseRetryCount(); + // reset the last capabilities because of the request is failed + mDeviceCapabilities.setPresencePublishResult(false); // Reset the pending flag because it is going to resend a request. clearPendingRequest(); @@ -373,15 +383,21 @@ public class PublishProcessor { Instant responseTime = response.getResponseTimestamp(); // Record the time when the request is successful and reset the retry count. + boolean publishSuccess = false; if (response.isRequestSuccess()) { mProcessorState.setLastPublishedTime(responseTime); mProcessorState.resetRetryCount(); + publishSuccess = true; } + // set the last capabilities according to the result of request. + mDeviceCapabilities.setPresencePublishResult(publishSuccess); // Update the publish state after the request has finished. int publishState = response.getPublishState(); String pidfXml = response.getPidfXml(); - mPublishCtrlCallback.updatePublishRequestResult(publishState, responseTime, pidfXml); + SipDetails details = response.getSipDetails().orElse(null); + mPublishCtrlCallback.updatePublishRequestResult(publishState, responseTime, pidfXml, + details); // Refresh the device state with the publish request result. response.getResponseSipCode().ifPresent(sipCode -> { @@ -492,6 +508,8 @@ public class PublishProcessor { */ public void resetState() { mProcessorState.resetState(); + // reset the publish capabilities. + mDeviceCapabilities.resetPresenceCapability(); } /** diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishRequestResponse.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishRequestResponse.java index 47aa37c3..aa1b27c1 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishRequestResponse.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishRequestResponse.java @@ -16,8 +16,10 @@ package com.android.ims.rcs.uce.presence.publish; +import android.annotation.NonNull; import android.annotation.Nullable; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IPublishResponseCallback; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.text.TextUtils; @@ -47,7 +49,7 @@ public class PublishRequestResponse { private Optional<String> mReasonPhrase; private Optional<Integer> mReasonHeaderCause; private Optional<String> mReasonHeaderText; - + private Optional<SipDetails> mSipDetails; // The timestamp when receive the response from the network. private Instant mResponseTimestamp; @@ -61,29 +63,29 @@ public class PublishRequestResponse { mReasonPhrase = Optional.empty(); mReasonHeaderCause = Optional.empty(); mReasonHeaderText = Optional.empty(); + mSipDetails = Optional.empty(); } - public PublishRequestResponse(String pidfXml, int sipCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { + public PublishRequestResponse(String pidfXml, @Nullable SipDetails details) { mTaskId = 0L; mPublishCtrlCallback = null; mCmdErrorCode = Optional.empty(); mPidfXml = pidfXml; mResponseTimestamp = Instant.now(); - mNetworkRespSipCode = Optional.of(sipCode); - mReasonPhrase = Optional.ofNullable(reasonPhrase); - if (reasonHeaderCause != 0) { - mReasonHeaderCause = Optional.of(reasonHeaderCause); + mNetworkRespSipCode = Optional.of(details.getResponseCode()); + mReasonPhrase = Optional.ofNullable(details.getResponsePhrase()); + if (details.getReasonHeaderCause() != 0) { + mReasonHeaderCause = Optional.of(details.getReasonHeaderCause()); } else { mReasonHeaderCause = Optional.empty(); } - if (TextUtils.isEmpty(reasonHeaderText)) { + if (TextUtils.isEmpty(details.getReasonHeaderText())) { mReasonHeaderText = Optional.empty(); } else { - mReasonHeaderText = Optional.ofNullable(reasonHeaderText); + mReasonHeaderText = Optional.ofNullable(details.getReasonHeaderText()); } - + mSipDetails = Optional.ofNullable(details); } // The result callback of the publish capability request. @@ -94,15 +96,8 @@ public class PublishRequestResponse { } @Override - public void onNetworkResponse(int code, String reason) { - PublishRequestResponse.this.onNetworkResponse(code, reason); - } - - @Override - public void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, - String reasonHeaderText) { - PublishRequestResponse.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause, - reasonHeaderText); + public void onNetworkResponse(@NonNull SipDetails details) { + PublishRequestResponse.this.onNetworkResponse(details); } }; @@ -150,6 +145,12 @@ public class PublishRequestResponse { } /** + * Retrieve the sip information which received from the network. + */ + public Optional<SipDetails> getSipDetails() { + return mSipDetails; + } + /** * Retrieve the SIP code from the network response. It will get the value from the Reason * Header first. If the ReasonHeader is not present, it will get the value from the Network * response instead. @@ -198,49 +199,34 @@ public class PublishRequestResponse { } } - private void onNetworkResponse(int sipCode, String reason) { + private void onNetworkResponse(@NonNull SipDetails details) { // When we send a request to PUBLISH and there is no change to the UCE capabilities, we // expected onCommandError() with COMMAND_CODE_NO_CHANGE. // But some of the vendor will instead send SIP code 999. - if (sipCode == 999) { + if (details.getResponseCode() == 999) { onCommandError(RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE); return; } mResponseTimestamp = Instant.now(); - mNetworkRespSipCode = Optional.of(sipCode); - mReasonPhrase = Optional.ofNullable(reason); - updateRetryFlagByNetworkResponse(); - - PublishControllerCallback ctrlCallback = mPublishCtrlCallback; - if (ctrlCallback != null) { - ctrlCallback.onRequestNetworkResp(this); - } else { - Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sip code=" + sipCode); + mNetworkRespSipCode = Optional.of(details.getResponseCode()); + mReasonPhrase = Optional.ofNullable(details.getResponsePhrase()); + if (details.getReasonHeaderCause() != 0) { + mReasonHeaderCause = Optional.of(details.getReasonHeaderCause()); } - } - - private void onNetworkResponse(int sipCode, String reasonPhrase, int reasonHeaderCause, - String reasonHeaderText) { - // When we send a request to PUBLISH and there is no change to the UCE capabilities, we - // expected onCommandError() with COMMAND_CODE_NO_CHANGE. - // But some of the vendor will instead send SIP code 999. - if (sipCode == 999) { - onCommandError(RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE); - return; + if (TextUtils.isEmpty(details.getReasonHeaderText())) { + mReasonHeaderText = Optional.empty(); + } else { + mReasonHeaderText = Optional.ofNullable(details.getReasonHeaderText()); } - mResponseTimestamp = Instant.now(); - mNetworkRespSipCode = Optional.of(sipCode); - mReasonPhrase = Optional.ofNullable(reasonPhrase); - mReasonHeaderCause = Optional.of(reasonHeaderCause); - mReasonHeaderText = Optional.ofNullable(reasonHeaderText); + mSipDetails = Optional.ofNullable(details); updateRetryFlagByNetworkResponse(); PublishControllerCallback ctrlCallback = mPublishCtrlCallback; if (ctrlCallback != null) { ctrlCallback.onRequestNetworkResp(this); } else { - Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sipCode=" + sipCode + - ", reasonHeader=" + reasonHeaderCause); + Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sip code=" + + details.getResponseCode()); } } diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTracker.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTracker.java index a107b1ad..7f7ea3e6 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTracker.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTracker.java @@ -53,7 +53,7 @@ public class PublishServiceDescTracker { */ private static final Map<ServiceDescription, Set<String>> DEFAULT_SERVICE_DESCRIPTION_MAP; static { - ArrayMap<ServiceDescription, Set<String>> map = new ArrayMap<>(21); + ArrayMap<ServiceDescription, Set<String>> map = new ArrayMap<>(23); map.put(ServiceDescription.SERVICE_DESCRIPTION_CHAT_IM, Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_IM)); map.put(ServiceDescription.SERVICE_DESCRIPTION_CHAT_SESSION, @@ -111,6 +111,14 @@ public class PublishServiceDescTracker { FeatureTags.FEATURE_TAG_CHATBOT_VERSION_V2_SUPPORTED))); map.put(ServiceDescription.SERVICE_DESCRIPTION_CHATBOT_ROLE, Collections.singleton(FeatureTags.FEATURE_TAG_CHATBOT_ROLE)); + map.put(ServiceDescription.SERVICE_DESCRIPTION_SLM, new ArraySet<>( + Arrays.asList(FeatureTags.FEATURE_TAG_PAGER_MODE, + FeatureTags.FEATURE_TAG_LARGE_MODE, + FeatureTags.FEATURE_TAG_DEFERRED_MESSAGING, + FeatureTags.FEATURE_TAG_LARGE_PAGER_MODE))); + map.put(ServiceDescription.SERVICE_DESCRIPTION_SLM_PAGER_LARGE, new ArraySet<>( + Arrays.asList(FeatureTags.FEATURE_TAG_PAGER_MODE, + FeatureTags.FEATURE_TAG_LARGE_MODE))); DEFAULT_SERVICE_DESCRIPTION_MAP = Collections.unmodifiableMap(map); } diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/ServiceDescription.java b/src/java/com/android/ims/rcs/uce/presence/publish/ServiceDescription.java index 0f271e1c..e175f2a4 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/ServiceDescription.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/ServiceDescription.java @@ -169,6 +169,20 @@ public class ServiceDescription { null /*description*/ ); + public static final ServiceDescription SERVICE_DESCRIPTION_SLM = + new ServiceDescription( + RcsContactPresenceTuple.SERVICE_ID_SLM, + "2.0" /*version*/, + null /*description*/ + ); + + public static final ServiceDescription SERVICE_DESCRIPTION_SLM_PAGER_LARGE = + new ServiceDescription( + RcsContactPresenceTuple.SERVICE_ID_SLM, + "2.0" /*version*/, + "Standalone Messaging" /*description*/ + ); + /** Mandatory "service-id" element */ public final @NonNull String serviceId; /** Mandatory "version" element */ diff --git a/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java b/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java index 97371b8b..6da31301 100644 --- a/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java +++ b/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java @@ -16,11 +16,14 @@ package com.android.ims.rcs.uce.request; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.Uri; import android.telephony.ims.RcsContactTerminatedReason; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.RcsUceAdapter.ErrorCode; +import android.telephony.ims.SipDetails; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode; import android.text.TextUtils; @@ -85,6 +88,9 @@ public class CapabilityRequestResponse { // The collection to record whether the request contacts have received the capabilities updated. private Map<Uri, Boolean> mContactCapsReceived; + // The SIP detail information of the network response. + private Optional<SipDetails> mSipDetails; + public CapabilityRequestResponse() { mRequestInternalError = Optional.empty(); mCommandError = Optional.empty(); @@ -99,6 +105,7 @@ public class CapabilityRequestResponse { mUpdatedCapabilityList = new ArrayList<>(); mRemoteCaps = new HashSet<>(); mContactCapsReceived = new HashMap<>(); + mSipDetails = Optional.empty(); } /** @@ -169,12 +176,20 @@ public class CapabilityRequestResponse { /** * Set the network response of this request which is sent by the network. */ - public synchronized void setNetworkResponseCode(int sipCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { - mNetworkRespSipCode = Optional.of(sipCode); - mReasonPhrase = Optional.ofNullable(reasonPhrase); - mReasonHeaderCause = Optional.of(reasonHeaderCause); - mReasonHeaderText = Optional.ofNullable(reasonHeaderText); + public synchronized void setSipDetails(@NonNull SipDetails details) { + setNetworkResponseCode(details.getResponseCode(), details.getResponsePhrase()); + if (details.getReasonHeaderCause() != 0) { + mReasonHeaderCause = Optional.of(details.getReasonHeaderCause()); + } else { + mReasonHeaderCause = Optional.empty(); + } + if (TextUtils.isEmpty(details.getReasonHeaderText())) { + mReasonHeaderText = Optional.empty(); + } else { + mReasonHeaderText = Optional.ofNullable(details.getReasonHeaderText()); + } + + mSipDetails = Optional.ofNullable(details); } // Get the sip code of the network response. @@ -239,6 +254,13 @@ public class CapabilityRequestResponse { } /** + * Retrieve the SIP information which received from the network. + */ + public Optional<SipDetails> getSipDetails() { + return mSipDetails; + } + + /** * Add the capabilities which are retrieved from the cache. */ public synchronized void addCachedCapabilities(List<RcsContactUceCapability> capabilityList) { diff --git a/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java index a2660931..b2db1788 100644 --- a/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java @@ -315,7 +315,7 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { private void triggerCompletedCallback() { try { logd("triggerCompletedCallback"); - mCapabilitiesCallback.onComplete(); + mCapabilitiesCallback.onComplete(null); } catch (RemoteException e) { logw("triggerCompletedCallback exception: " + e); } finally { @@ -329,7 +329,7 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { private void triggerErrorCallback(int errorCode, long retryAfterMillis) { try { logd("triggerErrorCallback: errorCode=" + errorCode + ", retry=" + retryAfterMillis); - mCapabilitiesCallback.onError(errorCode, retryAfterMillis); + mCapabilitiesCallback.onError(errorCode, retryAfterMillis, null); } catch (RemoteException e) { logw("triggerErrorCallback exception: " + e); } finally { diff --git a/src/java/com/android/ims/rcs/uce/request/SubscribeRequest.java b/src/java/com/android/ims/rcs/uce/request/SubscribeRequest.java index bee71771..a306eb42 100644 --- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequest.java +++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequest.java @@ -17,11 +17,13 @@ package com.android.ims.rcs.uce.request; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsContactTerminatedReason; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode; @@ -53,14 +55,8 @@ public class SubscribeRequest extends CapabilityRequest { SubscribeRequest.this.onCommandError(code); } @Override - public void onNetworkResponse(int code, String reason) { - SubscribeRequest.this.onNetworkResponse(code, reason); - } - @Override - public void onNetworkRespHeader(int code, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { - SubscribeRequest.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause, - reasonHeaderText); + public void onNetworkResponse(@NonNull SipDetails details) { + SubscribeRequest.this.onNetworkResponse(details); } @Override public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) { @@ -135,28 +131,14 @@ public class SubscribeRequest extends CapabilityRequest { } // Receive the network response callback which is triggered by ISubscribeResponseCallback. - private void onNetworkResponse(int sipCode, String reason) { - logd("onNetworkResponse: code=" + sipCode + ", reason=" + reason); - if (mIsFinished) { - logw("onNetworkResponse: request is already finished"); - return; - } - mRequestResponse.setNetworkResponseCode(sipCode, reason); - mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); - } + private void onNetworkResponse(@NonNull SipDetails details) { + logd("onNetworkResponse: sip details=" + details.toString()); - // Receive the network response callback which is triggered by ISubscribeResponseCallback. - private void onNetworkResponse(int sipCode, String reasonPhrase, - int reasonHeaderCause, String reasonHeaderText) { - logd("onNetworkResponse: code=" + sipCode + ", reasonPhrase=" + reasonPhrase + - ", reasonHeaderCause=" + reasonHeaderCause + - ", reasonHeaderText=" + reasonHeaderText); if (mIsFinished) { logw("onNetworkResponse: request is already finished"); return; } - mRequestResponse.setNetworkResponseCode(sipCode, reasonPhrase, reasonHeaderCause, - reasonHeaderText); + mRequestResponse.setSipDetails(details); mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); } diff --git a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java index f44686ac..26143f94 100644 --- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java @@ -18,10 +18,12 @@ package com.android.ims.rcs.uce.request; import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_GENERIC_FAILURE; +import android.annotation.Nullable; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IRcsUceControllerCallback; import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; @@ -111,21 +113,25 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { private static final RequestResultCreator sNetworkRespErrorCreator = (taskId, response, requestMgrCallback) -> { DeviceStateResult deviceState = requestMgrCallback.getDeviceState(); + SipDetails details = response.getSipDetails().orElse(null); if (deviceState.isRequestForbidden()) { int errorCode = deviceState.getErrorCode().orElse(RcsUceAdapter.ERROR_FORBIDDEN); long retryAfter = deviceState.getRequestRetryAfterMillis(); - return RequestResult.createFailedResult(taskId, errorCode, retryAfter); + return RequestResult.createFailedResult(taskId, errorCode, retryAfter, details); } else { int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response); long retryAfter = response.getRetryAfterMillis(); - return RequestResult.createFailedResult(taskId, errorCode, retryAfter); + return RequestResult.createFailedResult(taskId, errorCode, retryAfter, details); } }; // The RequestResult creator of the network response is not 200 OK, however, we can to treat // it as a successful result and finish the request private static final RequestResultCreator sNetworkRespSuccessfulCreator = (taskId, response, - requestMgrCallback) -> RequestResult.createSuccessResult(taskId); + requestMgrCallback) -> { + SipDetails detail = response.getSipDetails().orElse(null); + return RequestResult.createSuccessResult(taskId, detail); + }; // The RequestResult creator of the request terminated. private static final RequestResultCreator sTerminatedCreator = (taskId, response, @@ -134,6 +140,7 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { TerminatedResult terminatedResult = SubscriptionTerminatedHelper.getAnalysisResult( response.getTerminatedReason(), response.getRetryAfterMillis(), response.haveAllRequestCapsUpdatedBeenReceived()); + SipDetails details = response.getSipDetails().orElse(null); if (terminatedResult.getErrorCode().isPresent()) { // If the terminated error code is present, it means that the request is failed. int errorCode = terminatedResult.getErrorCode().get(); @@ -143,9 +150,9 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // If the network response is failed or the retryAfter is not 0, this request is failed. long retryAfterMillis = response.getRetryAfterMillis(); int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response); - return RequestResult.createFailedResult(taskId, errorCode, retryAfterMillis); + return RequestResult.createFailedResult(taskId, errorCode, retryAfterMillis, details); } else { - return RequestResult.createSuccessResult(taskId); + return RequestResult.createSuccessResult(taskId, details); } }; @@ -297,7 +304,9 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // Trigger capabilities updated callback if there is any. List<RcsContactUceCapability> updatedCapList = response.getUpdatedContactCapability(); if (!updatedCapList.isEmpty()) { - mRequestManagerCallback.saveCapabilities(updatedCapList); + if (response.isNotFound()) { + mRequestManagerCallback.saveCapabilities(updatedCapList); + } triggerCapabilitiesReceivedCallback(updatedCapList); response.removeUpdatedCapabilities(updatedCapList); } @@ -538,14 +547,23 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { .max(Comparator.comparingLong(result -> result.getRetryMillis().orElse(-1L))); + Optional<RequestResult> optDebugInfoResult = mFinishedRequests.values().stream() + .filter(result -> !result.getSipDetails().isEmpty()) + .findFirst(); + + SipDetails details = null; + if (optDebugInfoResult.isPresent()) { + RequestResult result = optDebugInfoResult.get(); + details = result.getSipDetails().orElse(null); + } // Trigger the callback if (optRequestResult.isPresent()) { RequestResult result = optRequestResult.get(); int errorCode = result.getErrorCode().orElse(DEFAULT_ERROR_CODE); long retryAfter = result.getRetryMillis().orElse(0L); - triggerErrorCallback(errorCode, retryAfter); + triggerErrorCallback(errorCode, retryAfter, details); } else { - triggerCompletedCallback(); + triggerCompletedCallback(details); } // Notify UceRequestManager to remove this instance from the collection. @@ -572,10 +590,10 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { /** * Trigger the onComplete callback to notify the request is completed. */ - private void triggerCompletedCallback() { + private void triggerCompletedCallback(@Nullable SipDetails details) { try { logd("triggerCompletedCallback"); - mCapabilitiesCallback.onComplete(); + mCapabilitiesCallback.onComplete(details); } catch (RemoteException e) { logw("triggerCompletedCallback exception: " + e); } finally { @@ -586,10 +604,11 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { /** * Trigger the onError callback to notify the request is failed. */ - private void triggerErrorCallback(int errorCode, long retryAfterMillis) { + private void triggerErrorCallback(int errorCode, long retryAfterMillis, + @Nullable SipDetails details) { try { logd("triggerErrorCallback: errorCode=" + errorCode + ", retry=" + retryAfterMillis); - mCapabilitiesCallback.onError(errorCode, retryAfterMillis); + mCapabilitiesCallback.onError(errorCode, retryAfterMillis, details); } catch (RemoteException e) { logw("triggerErrorCallback exception: " + e); } finally { diff --git a/src/java/com/android/ims/rcs/uce/request/UceRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/UceRequestCoordinator.java index eea4fbe3..5d3a35da 100644 --- a/src/java/com/android/ims/rcs/uce/request/UceRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/UceRequestCoordinator.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.util.Log; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; @@ -135,6 +136,15 @@ public abstract class UceRequestCoordinator { } /** + * Create a RequestResult that successfully completes the request. + * @param taskId the task id of the UceRequest + * @param details The SIP information related to this request. + */ + public static RequestResult createSuccessResult(long taskId, @Nullable SipDetails details) { + return new RequestResult(taskId, details); + } + + /** * Create a RequestResult for the failed request. * @param taskId the task id of the UceRequest * @param errorCode the error code of the failed request @@ -144,29 +154,66 @@ public abstract class UceRequestCoordinator { return new RequestResult(taskId, errorCode, retry); } + /** + * Create a RequestResult for the failed request. + * @param taskId the task id of the UceRequest + * @param errorCode the error code of the failed request + * @param retry When the request can be retried. + * @param details The SIP information related to this request. + */ + public static RequestResult createFailedResult(long taskId, int errorCode, long retry, + @Nullable SipDetails details) { + return new RequestResult(taskId, errorCode, retry, details); + } + private final Long mTaskId; private final Boolean mIsSuccess; private final Optional<Integer> mErrorCode; private final Optional<Long> mRetryMillis; + private final Optional<SipDetails> mSipDetails; /** * The private constructor for the successful request. */ private RequestResult(long taskId) { + this(taskId, null); + } + + /** + * The private constructor for the successful request. + */ + private RequestResult(long taskId, SipDetails details) { mTaskId = taskId; mIsSuccess = true; mErrorCode = Optional.empty(); mRetryMillis = Optional.empty(); + if (details == null) { + mSipDetails = Optional.empty(); + } else { + mSipDetails = Optional.ofNullable(details); + } } /** * The private constructor for the failed request. */ private RequestResult(long taskId, int errorCode, long retryMillis) { + this(taskId, errorCode, retryMillis, null); + } + + /** + * The private constructor for the failed request. + */ + private RequestResult(long taskId, int errorCode, long retryMillis, SipDetails details) { mTaskId = taskId; mIsSuccess = false; mErrorCode = Optional.of(errorCode); mRetryMillis = Optional.of(retryMillis); + if (details == null) { + mSipDetails = Optional.empty(); + } else { + mSipDetails = Optional.ofNullable(details); + } } public long getTaskId() { @@ -184,6 +231,10 @@ public abstract class UceRequestCoordinator { public Optional<Long> getRetryMillis() { return mRetryMillis; } + + public Optional<SipDetails> getSipDetails() { + return mSipDetails; + } } // The default capability error code. diff --git a/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java index 85908f0c..8ff39c1b 100644 --- a/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java +++ b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java @@ -478,7 +478,7 @@ public class UceRequestManager { public void sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException { if (mIsDestroyed) { - callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); return; } sendRequestInternal(UceRequest.REQUEST_TYPE_CAPABILITY, uriList, skipFromCache, callback); @@ -490,7 +490,7 @@ public class UceRequestManager { public void sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback) throws RemoteException { if (mIsDestroyed) { - callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); return; } sendRequestInternal(UceRequest.REQUEST_TYPE_AVAILABILITY, @@ -511,7 +511,7 @@ public class UceRequestManager { } if (nonCachedUris.isEmpty()) { logd("sendRequestInternal: shortcut complete, sending success result"); - callback.onComplete(); + callback.onComplete(null); return; } } @@ -525,7 +525,7 @@ public class UceRequestManager { if (requestCoordinator == null) { logw("sendRequestInternal: Neither Presence nor OPTIONS are supported"); - callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L); + callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L, null); return; } diff --git a/src/java/com/android/ims/rcs/uce/util/FeatureTags.java b/src/java/com/android/ims/rcs/uce/util/FeatureTags.java index 8dbceda6..ed2eef4a 100644 --- a/src/java/com/android/ims/rcs/uce/util/FeatureTags.java +++ b/src/java/com/android/ims/rcs/uce/util/FeatureTags.java @@ -35,6 +35,18 @@ public class FeatureTags { + "service.ims.icsi.oma.cpm.largemsg,urn%3Aurn-7%3A3gpp-" + "service.ims.icsi.oma.cpm.deferred\";+g.gsma.rcs.cpm.pager-large"; + public static final String FEATURE_TAG_PAGER_MODE = + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\""; + + public static final String FEATURE_TAG_LARGE_MODE = + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\""; + + public static final String FEATURE_TAG_DEFERRED_MESSAGING = + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\""; + + public static final String FEATURE_TAG_LARGE_PAGER_MODE = + "+g.gsma.rcs.cpm.pager-large"; + public static final String FEATURE_TAG_CHAT_IM = "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\""; diff --git a/tests/Android.bp b/tests/Android.bp index 82c303d5..7ae66b56 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -40,6 +40,7 @@ android_test { ], test_suites: [ - "device-tests" - ] + "device-tests", + ], + min_sdk_version: "29", } diff --git a/tests/src/com/android/ims/ContextFixture.java b/tests/src/com/android/ims/ContextFixture.java index e987b387..21dd6348 100644 --- a/tests/src/com/android/ims/ContextFixture.java +++ b/tests/src/com/android/ims/ContextFixture.java @@ -33,6 +33,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.os.Handler; import android.os.PersistableBundle; import android.telecom.TelecomManager; import android.telephony.CarrierConfigManager; @@ -131,6 +132,18 @@ public class ContextFixture { } @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler) { + return null; + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler, int flags) { + return null; + } + + @Override public void unregisterReceiver(BroadcastReceiver receiver) { } diff --git a/tests/src/com/android/ims/ImsManagerTest.java b/tests/src/com/android/ims/ImsManagerTest.java index 0653908d..ad5051be 100644 --- a/tests/src/com/android/ims/ImsManagerTest.java +++ b/tests/src/com/android/ims/ImsManagerTest.java @@ -119,7 +119,7 @@ public class ImsManagerTest extends ImsTestBase { doReturn(true).when(mSubscriptionManagerProxy).isValidSubscriptionId(anyInt()); doReturn(mSubId).when(mSubscriptionManagerProxy).getActiveSubscriptionIdList(); - doReturn(mSubId).when(mSubscriptionManagerProxy).getSubscriptionIds(anyInt()); + doReturn(mSubId[0]).when(mSubscriptionManagerProxy).getSubscriptionId(anyInt()); doReturn(mPhoneId).when(mSubscriptionManagerProxy).getDefaultVoicePhoneId(); doReturn(-1).when(mSubscriptionManagerProxy).getIntegerSubscriptionProperty(anyInt(), anyString(), anyInt()); @@ -1012,6 +1012,14 @@ public class ImsManagerTest extends ImsTestBase { } + @Test @SmallTest + public void onMemoryAvailableTest() throws Exception{ + ImsManager imsManager = getImsManagerAndInitProvisionedValues(); + int token = 1; + imsManager.onMemoryAvailable(token); + verify(mMmTelFeatureConnection).onMemoryAvailable(eq(token)); + } + private ImsManager getImsManagerAndInitProvisionedValues() { when(mImsConfigImplBaseMock.getConfigInt(anyInt())) .thenAnswer(invocation -> { diff --git a/tests/src/com/android/ims/rcs/uce/UceControllerTest.java b/tests/src/com/android/ims/rcs/uce/UceControllerTest.java index 021a7c10..79702ed5 100644 --- a/tests/src/com/android/ims/rcs/uce/UceControllerTest.java +++ b/tests/src/com/android/ims/rcs/uce/UceControllerTest.java @@ -147,7 +147,7 @@ public class UceControllerTest extends ImsTestBase { List<Uri> uriList = new ArrayList<>(); uceController.requestCapabilities(uriList, mCapabilitiesCallback); - verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); verify(mTaskManager, never()).sendCapabilityRequest(any(), eq(false), any()); } @@ -164,7 +164,7 @@ public class UceControllerTest extends ImsTestBase { uriList.add(Uri.fromParts("sip", "test", null)); uceController.requestCapabilities(uriList, mCapabilitiesCallback); - verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_FORBIDDEN, 0L); + verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_FORBIDDEN, 0L, null); verify(mTaskManager, never()).sendCapabilityRequest(any(), eq(false), any()); } @@ -194,7 +194,7 @@ public class UceControllerTest extends ImsTestBase { Uri contact = Uri.fromParts("sip", "test", null); uceController.requestAvailability(contact, mCapabilitiesCallback); - verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); verify(mTaskManager, never()).sendAvailabilityRequest(any(), any()); } @@ -210,7 +210,7 @@ public class UceControllerTest extends ImsTestBase { Uri contact = Uri.fromParts("sip", "test", null); uceController.requestAvailability(contact, mCapabilitiesCallback); - verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_FORBIDDEN, 0L); + verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_FORBIDDEN, 0L, null); verify(mTaskManager, never()).sendCapabilityRequest(any(), eq(false), any()); } diff --git a/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfoTest.java b/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfoTest.java index c977a080..de045de4 100644 --- a/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfoTest.java +++ b/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityInfoTest.java @@ -16,6 +16,19 @@ package com.android.ims.rcs.uce.presence.publish; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doReturn; + +import android.content.Context; +import android.telephony.CarrierConfigManager; +import android.telephony.TelephonyManager; +import android.telephony.ims.RcsContactPresenceTuple; +import android.util.ArraySet; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -26,6 +39,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.ims.ImsTestBase; +import com.android.ims.rcs.uce.util.UceUtils; import org.junit.After; import org.junit.Before; @@ -33,11 +47,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + @RunWith(AndroidJUnit4.class) public class DeviceCapabilityInfoTest extends ImsTestBase { - int mSubId = 1; + @Mock PublishServiceDescTracker mPublishServiceDescTracker; + @Mock Context mMockContext; + String sipNumber = "123456789"; String telNumber = "987654321"; @@ -53,6 +74,22 @@ public class DeviceCapabilityInfoTest extends ImsTestBase { @Test @SmallTest + public void testGetPresenceCapabilityForSameDescription() throws Exception { + DeviceCapabilityInfo deviceCapInfo = createDeviceCapabilityInfo(); + + Set<ServiceDescription> mTestCapability = new ArraySet<>(); + mTestCapability.add(getChatDescription()); + mTestCapability.add(getMmtelDescription()); + mTestCapability.add(getUndefinedDescription()); + + deviceCapInfo.addLastSuccessfulServiceDescription(getMmtelDescription()); + deviceCapInfo.addLastSuccessfulServiceDescription(getChatDescription()); + deviceCapInfo.addLastSuccessfulServiceDescription(getUndefinedDescription()); + assertFalse(deviceCapInfo.isPresenceCapabilityChanged(mTestCapability)); + } + + @Test + @SmallTest public void testGetImsAssociatedUriWithoutPreferTelUri() throws Exception { DeviceCapabilityInfo deviceCapInfo = createDeviceCapabilityInfo(); @@ -90,6 +127,23 @@ public class DeviceCapabilityInfoTest extends ImsTestBase { @Test @SmallTest + public void testGetPresenceCapabilityForSameSizeOfDescription() throws Exception { + DeviceCapabilityInfo deviceCapInfo = createDeviceCapabilityInfo(); + + Set<ServiceDescription> mTestCapability = new ArraySet<>(); + mTestCapability.add(getChatDescription()); + mTestCapability.add(getMmtelDescription()); + mTestCapability.add(getUndefinedDescription()); + + deviceCapInfo.addLastSuccessfulServiceDescription(getMmtelDescription()); + deviceCapInfo.addLastSuccessfulServiceDescription(getChatDescription()); + deviceCapInfo.addLastSuccessfulServiceDescription(getUndefined2Description()); + + assertTrue(deviceCapInfo.isPresenceCapabilityChanged(mTestCapability)); + } + + @Test + @SmallTest public void testGetImsAssociatedUriWithPreferTelUri() throws Exception { DeviceCapabilityInfo deviceCapInfo = createDeviceCapabilityInfo(); @@ -138,7 +192,43 @@ public class DeviceCapabilityInfoTest extends ImsTestBase { number = numberParts[0]; assertEquals(number, telNumber); + } + @Test + @SmallTest + public void testGetLastSuccessfulPresenceTuples() throws Exception { + DeviceCapabilityInfo deviceCapInfo = createDeviceCapabilityInfo(); + + List<RcsContactPresenceTuple> tuples = + deviceCapInfo.getLastSuccessfulPresenceTuplesWithoutContactUri(); + // Verify that the presence tuples are empty when the last capabilities are empty. + assertTrue(tuples.isEmpty()); + + doReturn(null).when(mMockContext).getSystemService(CarrierConfigManager.class); + doReturn(null).when(mMockContext).getSystemService(TelephonyManager.class); + ServiceDescription mmtelDescription = getMmtelDescription(); + deviceCapInfo.addLastSuccessfulServiceDescription(mmtelDescription); + ServiceDescription chatDescription = getChatDescription(); + deviceCapInfo.addLastSuccessfulServiceDescription(chatDescription); + tuples = deviceCapInfo.getLastSuccessfulPresenceTuplesWithoutContactUri(); + assertEquals(2, tuples.size()); + + Uri[] uris = new Uri[1]; + uris[0] = Uri.fromParts(PhoneAccount.SCHEME_SIP, sipNumber, null); + deviceCapInfo.updateRcsAssociatedUri(uris); + List<RcsContactPresenceTuple> expectedTuples = new ArrayList<>(); + expectedTuples.add(mmtelDescription.getTupleBuilder().setContactUri(uris[0]).build()); + expectedTuples.add(chatDescription.getTupleBuilder().setContactUri(uris[0]).build()); + + tuples = deviceCapInfo.getLastSuccessfulPresenceTuplesWithoutContactUri(); + assertTrue(!tuples.isEmpty()); + assertEquals(expectedTuples.size(), tuples.size()); + for (int i = 0; i < tuples.size(); i++) { + assertEquals(expectedTuples.get(i).getServiceId(), tuples.get(i).getServiceId()); + assertEquals(expectedTuples.get(i).getServiceVersion(), + tuples.get(i).getServiceVersion()); + assertNull(tuples.get(i).getContactUri()); + } } private DeviceCapabilityInfo createDeviceCapabilityInfo() { @@ -146,4 +236,40 @@ public class DeviceCapabilityInfoTest extends ImsTestBase { return deviceCapInfo; } + private ServiceDescription getChatDescription() { + ServiceDescription SERVICE_DESCRIPTION_CHAT_SESSION = + new ServiceDescription( + RcsContactPresenceTuple.SERVICE_ID_CHAT_V2, + "2.0" /*version*/, + null /*description*/ + ); + return SERVICE_DESCRIPTION_CHAT_SESSION; + } + + private ServiceDescription getMmtelDescription() { + ServiceDescription SERVICE_DESCRIPTION_MMTEL_VOICE = new ServiceDescription( + RcsContactPresenceTuple.SERVICE_ID_MMTEL, + "1.0" /*version*/, + "Voice Service" /*description*/ + ); + return SERVICE_DESCRIPTION_MMTEL_VOICE; + } + + private ServiceDescription getUndefinedDescription() { + ServiceDescription SERVICE_DESCRIPTION_TEST = new ServiceDescription( + "test", + "1.0" /*version*/, + "Test_Service" /*description*/ + ); + return SERVICE_DESCRIPTION_TEST; + } + + private ServiceDescription getUndefined2Description() { + ServiceDescription SERVICE_DESCRIPTION_TEST2 = new ServiceDescription( + "test1", + "1.0" /*version*/, + "Test_Service" /*description*/ + ); + return SERVICE_DESCRIPTION_TEST2; + } }
\ No newline at end of file diff --git a/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListenerTest.java b/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListenerTest.java index 2d170ab6..11350393 100644 --- a/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListenerTest.java +++ b/tests/src/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListenerTest.java @@ -84,7 +84,6 @@ public class DeviceCapabilityListenerTest extends ImsTestBase { getProvisioningManager(anyInt()); doReturn(true).when(mDeviceCapability).updateTtyPreferredMode(anyInt()); - doReturn(true).when(mDeviceCapability).updateAirplaneMode(anyBoolean()); doReturn(true).when(mDeviceCapability).updateMobileData(anyBoolean()); doReturn(true).when(mDeviceCapability).updateVtSetting(anyBoolean()); doReturn(true).when(mDeviceCapability).updateVtSetting(anyBoolean()); @@ -107,7 +106,8 @@ public class DeviceCapabilityListenerTest extends ImsTestBase { deviceCapListener.initialize(); - verify(mContext).registerReceiver(any(), any()); + verify(mContext).registerReceiver(any(), any(), + eq(android.Manifest.permission.MODIFY_PHONE_STATE), any(), anyInt()); verify(mProvisioningManager).registerProvisioningChangedCallback(any(), any()); } @@ -143,23 +143,6 @@ public class DeviceCapabilityListenerTest extends ImsTestBase { @Test @SmallTest - public void testAirplaneModeChange() throws Exception { - DeviceCapabilityListener deviceCapListener = createDeviceCapabilityListener(); - final BroadcastReceiver receiver = deviceCapListener.mReceiver; - - Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - receiver.onReceive(mContext, intent); - - Handler handler = deviceCapListener.getHandler(); - waitForHandlerActionDelayed(handler, HANDLER_WAIT_TIMEOUT_MS, HANDLER_SENT_DELAY_MS); - - verify(mDeviceCapability).updateAirplaneMode(anyBoolean()); - verify(mCallback).requestPublishFromInternal( - PublishController.PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE); - } - - @Test - @SmallTest public void testMmtelRegistration() throws Exception { DeviceCapabilityListener deviceCapListener = createDeviceCapabilityListener(); deviceCapListener.setImsCallbackRegistered(true); @@ -171,7 +154,8 @@ public class DeviceCapabilityListenerTest extends ImsTestBase { waitForHandlerActionDelayed(handler, HANDLER_WAIT_TIMEOUT_MS, HANDLER_SENT_DELAY_MS); verify(mDeviceCapability).updateImsMmtelRegistered(1); - verify(mCallback).requestPublishFromInternal( + // update capability, but not trigger PUBLISH message when MmTel registered. + verify(mCallback, never()).requestPublishFromInternal( PublishController.PUBLISH_TRIGGER_MMTEL_REGISTERED); } @@ -266,6 +250,65 @@ public class DeviceCapabilityListenerTest extends ImsTestBase { verify(mCallback).updateImsUnregistered(); } + @Test + @SmallTest + public void testRcsAndMmtelUnregistration() throws Exception { + DeviceCapabilityListener deviceCapListener = createDeviceCapabilityListener(); + deviceCapListener.setImsCallbackRegistered(true); + + Handler handler = deviceCapListener.getHandler(); + ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, -1, ""); + // RCS unregistered + RegistrationCallback rcsRegiCallback = deviceCapListener.mRcsRegistrationCallback; + + doReturn(true).when(mDeviceCapability).updateImsRcsUnregistered(); + // RCS is unregistered but MMTEL is registered. + doReturn(true).when(mDeviceCapability).isImsRegistered(); + rcsRegiCallback.onUnregistered(info); + + // MMTEL unregistered + RegistrationCallback mmtelRegiCallback = deviceCapListener.mMmtelRegistrationCallback; + // set the Ims is unregistered + doReturn(false).when(mDeviceCapability).isImsRegistered(); + mmtelRegiCallback.onUnregistered(info); + + waitForHandlerActionDelayed(handler, HANDLER_WAIT_TIMEOUT_MS, HANDLER_SENT_DELAY_MS); + + // Do not send internal publish trigger + verify(mCallback, never()).requestPublishFromInternal(anyInt()); + // IMS is unregistered. Verify send ImsUnregistered. + verify(mCallback).updateImsUnregistered(); + } + + @Test + @SmallTest + public void testUnregisterRcsOnlyFromImsRegistration() throws Exception { + DeviceCapabilityListener deviceCapListener = createDeviceCapabilityListener(); + deviceCapListener.setImsCallbackRegistered(true); + Handler handler = deviceCapListener.getHandler(); + + // set the Ims is registered + doReturn(true).when(mDeviceCapability).isImsRegistered(); + ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, -1, ""); + // RCS unregistered + RegistrationCallback rcsRegiCallback = deviceCapListener.mRcsRegistrationCallback; + + doReturn(true).when(mDeviceCapability).updateImsRcsUnregistered(); + // RCS is unregistered but MMTEL is registered. + doReturn(true).when(mDeviceCapability).isImsRegistered(); + rcsRegiCallback.onUnregistered(info); + + waitForHandlerActionDelayed(handler, HANDLER_WAIT_TIMEOUT_MS, HANDLER_SENT_DELAY_MS); + + verify(mDeviceCapability).updateImsRcsUnregistered(); + // Only RCS unregistered. Verify the request of the modify publish is sent. + verify(mCallback).requestPublishFromInternal( + PublishController.PUBLISH_TRIGGER_RCS_URI_CHANGE); + + // Only RCS unregistered. Verify do not send ImsUnregistered. + verify(mCallback, never()).updateImsUnregistered(); + } + private DeviceCapabilityListener createDeviceCapabilityListener() { DeviceCapabilityListener deviceCapListener = new DeviceCapabilityListener(mContext, mSubId, mDeviceCapability, mCallback, mUceStatsWriter); diff --git a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishControllerImplTest.java b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishControllerImplTest.java index a7e0bbbe..aa145c7c 100644 --- a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishControllerImplTest.java +++ b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishControllerImplTest.java @@ -22,6 +22,8 @@ import static com.android.ims.rcs.uce.presence.publish.PublishController.PUBLISH import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -34,6 +36,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteCallbackList; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities; @@ -137,7 +140,7 @@ public class PublishControllerImplTest extends ImsTestBase { assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, initState); publishController.getPublishControllerCallback().updatePublishRequestResult( - RcsUceAdapter.PUBLISH_STATE_OK, Instant.now(), null); + RcsUceAdapter.PUBLISH_STATE_OK, Instant.now(), null, null); Handler handler = publishController.getPublishHandler(); waitForHandlerAction(handler, 1000); @@ -154,7 +157,7 @@ public class PublishControllerImplTest extends ImsTestBase { assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, initState); publishController.getPublishControllerCallback().updatePublishRequestResult( - RcsUceAdapter.PUBLISH_STATE_PUBLISHING, Instant.now(), null); + RcsUceAdapter.PUBLISH_STATE_PUBLISHING, Instant.now(), null, null); Handler handler = publishController.getPublishHandler(); waitForHandlerAction(handler, 1000); @@ -223,8 +226,10 @@ public class PublishControllerImplTest extends ImsTestBase { public void testPublishUpdated() throws Exception { PublishControllerImpl publishController = createPublishController(); int responseCode = 200; + String responsePhrase = "OK"; - publishController.onPublishUpdated(responseCode, "", 0, ""); + publishController.onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH) + .setSipResponseCode(responseCode, responsePhrase).build()); Handler handler = publishController.getPublishHandler(); waitForHandlerAction(handler, 1000); @@ -235,14 +240,64 @@ public class PublishControllerImplTest extends ImsTestBase { verify(mPublishProcessor).publishUpdated(captor.capture()); PublishRequestResponse response = captor.getValue(); int expectedCode = response.getNetworkRespSipCode().orElse(-1); + String expectedPhrase = response.getReasonPhrase().orElse(""); assertEquals(responseCode, expectedCode); + assertEquals(responsePhrase, expectedPhrase); + SipDetails details = response.getSipDetails().orElse(null); + assertNotNull(details); + assertEquals(responseCode, details.getResponseCode()); + assertEquals(responsePhrase, details.getResponsePhrase()); + } + + @Test + @SmallTest + public void testPublishUpdatedWithSipDetails() throws Exception { + PublishControllerImpl publishController = createPublishController(); + int responseCode = 200; + String responsePhrase = "OK"; + int reasonHdrCause = 7; + String reasonHdrText = "reasonHeaderText"; + int cseq = 10; + String callId = "TestCallId"; + + publishController.onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH) + .setCSeq(cseq).setSipResponseCode(responseCode, responsePhrase).setCallId(callId) + .setSipResponseReasonHeader(reasonHdrCause, reasonHdrText).build()); + + Handler handler = publishController.getPublishHandler(); + waitForHandlerAction(handler, 1000); + + ArgumentCaptor<PublishRequestResponse> captor = + ArgumentCaptor.forClass(PublishRequestResponse.class); + + verify(mPublishProcessor).publishUpdated(captor.capture()); + PublishRequestResponse response = captor.getValue(); + int expectedCode = response.getNetworkRespSipCode().orElse(-1); + String expectedPhrase = response.getReasonPhrase().orElse(""); + int expectedReasonCause = response.getReasonHeaderCause().orElse(-1); + String expectedReasonText = response.getReasonHeaderText().orElse(""); + + assertEquals(responseCode, expectedCode); + assertEquals(responsePhrase, expectedPhrase); + assertEquals(reasonHdrCause, expectedReasonCause); + assertEquals(reasonHdrText, expectedReasonText); + + SipDetails details = response.getSipDetails().orElse(null); + assertNotNull(details); + assertEquals(SipDetails.METHOD_PUBLISH, details.getMethod()); + assertEquals(cseq, details.getCSeq()); + assertEquals(responseCode, details.getResponseCode()); + assertEquals(responsePhrase, details.getResponsePhrase()); + assertEquals(reasonHdrCause, details.getReasonHeaderCause()); + assertEquals(reasonHdrText, details.getReasonHeaderText()); + assertEquals(callId, details.getCallId()); } @Test @SmallTest public void testPublishingStateTargetingEnable() throws Exception { doReturn(1).when(mPublishStateCallbacks).getRegisteredCallbackCount(); - Boolean boolObj = new Boolean(true); + Boolean boolObj = Boolean.TRUE; Object uid1 = (Object)boolObj; doReturn(uid1).when(mPublishStateCallbacks).getRegisteredCallbackCookie(anyInt()); @@ -282,7 +337,7 @@ public class PublishControllerImplTest extends ImsTestBase { @SmallTest public void testPublishingStateTargetingDisable() throws Exception { doReturn(1).when(mPublishStateCallbacks).getRegisteredCallbackCount(); - Boolean boolObj = new Boolean(false); + Boolean boolObj = Boolean.FALSE; Object uid1 = (Object)boolObj; doReturn(uid1).when(mPublishStateCallbacks).getRegisteredCallbackCookie(anyInt()); diff --git a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishProcessorTest.java b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishProcessorTest.java index 4e8cdfdc..2de47146 100644 --- a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishProcessorTest.java +++ b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishProcessorTest.java @@ -30,6 +30,7 @@ import android.content.Context; import android.net.Uri; import android.telephony.ims.RcsContactPresenceTuple; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.SipDetails; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -82,6 +83,7 @@ public class PublishProcessorTest extends ImsTestBase { doReturn(true).when(mDeviceCapabilities).isImsRegistered(); RcsContactUceCapability capability = getRcsContactUceCapability(); + doReturn(capability).when(mDeviceCapabilities).getChangedPresenceCapability(any()); doReturn(capability).when(mDeviceCapabilities).getDeviceCapabilities(anyInt(), any()); doReturn(mTaskId).when(mResponseCallback).getTaskId(); @@ -112,7 +114,7 @@ public class PublishProcessorTest extends ImsTestBase { PublishProcessor publishProcessor = getPublishProcessor(); publishProcessor.doPublish(PublishController.PUBLISH_TRIGGER_RETRY); - + verify(mDeviceCapabilities).getChangedPresenceCapability(any()); verify(mProcessorState, never()).resetRetryCount(); } @@ -139,7 +141,7 @@ public class PublishProcessorTest extends ImsTestBase { publishProcessor.onNetworkResponse(mResponseCallback); - verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any()); + verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any(), eq(null)); verify(mResponseCallback).onDestroy(); verify(mProcessorState).setPublishingFlag(false); verify(mPublishCtrlCallback).clearRequestCanceledTimer(); @@ -171,6 +173,7 @@ public class PublishProcessorTest extends ImsTestBase { publishProcessor.onCommandError(mResponseCallback); + verify(mDeviceCapabilities).setPresencePublishResult(false); verify(mProcessorState).increaseRetryCount(); verify(mPublishCtrlCallback).requestPublishFromInternal( eq(PublishController.PUBLISH_TRIGGER_RETRY)); @@ -188,11 +191,13 @@ public class PublishProcessorTest extends ImsTestBase { doReturn(mTaskId).when(mProcessorState).getCurrentTaskId(); doReturn(mTaskId).when(mResponseCallback).getTaskId(); doReturn(false).when(mResponseCallback).needRetry(); + doReturn(true).when(mResponseCallback).isRequestSuccess(); PublishProcessor publishProcessor = getPublishProcessor(); publishProcessor.onCommandError(mResponseCallback); - verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any()); + verify(mDeviceCapabilities).setPresencePublishResult(true); + verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any(), eq(null)); verify(mResponseCallback).onDestroy(); verify(mProcessorState).setPublishingFlag(false); verify(mPublishCtrlCallback).clearRequestCanceledTimer(); @@ -209,6 +214,7 @@ public class PublishProcessorTest extends ImsTestBase { publishProcessor.onNetworkResponse(mResponseCallback); + verify(mDeviceCapabilities).setPresencePublishResult(false); verify(mProcessorState).increaseRetryCount(); verify(mPublishCtrlCallback).requestPublishFromInternal( eq(PublishController.PUBLISH_TRIGGER_RETRY)); @@ -226,11 +232,20 @@ public class PublishProcessorTest extends ImsTestBase { doReturn(false).when(mResponseCallback).needRetry(); doReturn(true).when(mResponseCallback).isRequestSuccess(); doReturn(Optional.of(200)).when(mResponseCallback).getNetworkRespSipCode(); + + SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH) + .setCSeq(10).setSipResponseCode(200, "OK").setCallId("CallId").build(); + Optional<SipDetails> sipDetails = Optional.ofNullable(details); + doReturn(sipDetails).when(mResponseCallback).getSipDetails(); + PublishProcessor publishProcessor = getPublishProcessor(); publishProcessor.onNetworkResponse(mResponseCallback); + SipDetails actualInfo = sipDetails.orElse(null); - verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any()); + verify(mDeviceCapabilities).setPresencePublishResult(true); + verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any(), + eq(actualInfo)); verify(mResponseCallback).onDestroy(); verify(mProcessorState).setPublishingFlag(false); verify(mPublishCtrlCallback).clearRequestCanceledTimer(); @@ -260,13 +275,21 @@ public class PublishProcessorTest extends ImsTestBase { doReturn(0).when(mResponseCallback).getPublishState(); doReturn("").when(mResponseCallback).getPidfXml(); + SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH) + .setCSeq(10).setSipResponseCode(200, "").setCallId("CallId").build(); + Optional<SipDetails> sipDetails = Optional.ofNullable(details); + doReturn(sipDetails).when(mResponseCallback).getSipDetails(); + PublishProcessor publishProcessor = getPublishProcessor(); publishProcessor.publishUpdated(mResponseCallback); + SipDetails actualInfo = sipDetails.orElse(null); + verify(mDeviceCapabilities).setPresencePublishResult(true); verify(mProcessorState).setLastPublishedTime(any()); verify(mProcessorState).resetRetryCount(); - verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any()); + verify(mPublishCtrlCallback).updatePublishRequestResult(anyInt(), any(), any(), + eq(actualInfo)); } private PublishProcessor getPublishProcessor() { diff --git a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTrackerTest.java b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTrackerTest.java index 55006299..52017c7a 100644 --- a/tests/src/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTrackerTest.java +++ b/tests/src/com/android/ims/rcs/uce/presence/publish/PublishServiceDescTrackerTest.java @@ -107,7 +107,6 @@ public class PublishServiceDescTrackerTest { t1.updateImsRegistration(imsReg); assertEquals(expectedSet, t1.copyRegistrationCapabilities()); - expectedSet = Collections.singleton( ServiceDescription.SERVICE_DESCRIPTION_CHATBOT_SESSION); imsReg = createImsRegistration( @@ -139,6 +138,30 @@ public class PublishServiceDescTrackerTest { FeatureTags.FEATURE_TAG_VIDEO); t1.updateImsRegistration(imsReg); assertEquals(expectedSet, t1.copyRegistrationCapabilities()); + + expectedSet = Collections.singleton( + ServiceDescription.SERVICE_DESCRIPTION_SLM); + imsReg = createImsRegistration( + FeatureTags.FEATURE_TAG_PAGER_MODE, + FeatureTags.FEATURE_TAG_LARGE_MODE, + FeatureTags.FEATURE_TAG_DEFERRED_MESSAGING, + FeatureTags.FEATURE_TAG_LARGE_PAGER_MODE); + t1.updateImsRegistration(imsReg); + assertEquals(expectedSet, t1.copyRegistrationCapabilities()); + + + expectedSet = Collections.singleton( + ServiceDescription.SERVICE_DESCRIPTION_SLM_PAGER_LARGE); + imsReg = createImsRegistration( + FeatureTags.FEATURE_TAG_PAGER_MODE, + FeatureTags.FEATURE_TAG_LARGE_MODE); + t1.updateImsRegistration(imsReg); + assertEquals(expectedSet, t1.copyRegistrationCapabilities()); + + // delete the feature tags for Unregistered + expectedSet = new ArraySet<>(); + t1.updateImsRegistration(Collections.emptySet()); + assertEquals(expectedSet, t1.copyRegistrationCapabilities()); } @SmallTest diff --git a/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java b/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java index bcd3c98d..2c0042ea 100644 --- a/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java +++ b/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java @@ -25,14 +25,11 @@ import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDA import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDATE_RESOURCE_TERMINATED; import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDATE_TERMINATED; -import static java.lang.Boolean.TRUE; - import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -41,6 +38,7 @@ import static org.mockito.Mockito.verify; import android.net.Uri; import android.telephony.ims.RcsContactPresenceTuple; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.IRcsUceControllerCallback; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -49,21 +47,23 @@ import androidx.test.filters.SmallTest; import com.android.ims.ImsTestBase; import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; import com.android.ims.rcs.uce.UceStatsWriter; +import com.android.ims.rcs.uce.eab.EabCapabilityResult; import com.android.ims.rcs.uce.request.UceRequestCoordinator.RequestResult; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + @RunWith(AndroidJUnit4.class) public class SubscribeCoordinatorTest extends ImsTestBase { @@ -127,6 +127,17 @@ public class SubscribeCoordinatorTest extends ImsTestBase { @Test @SmallTest public void testRequestNetworkRespSuccess() throws Exception { + int responseCode = 200; + String responsePhrase = "OK"; + int reasonHdrCause = 1; + String reasonHdrText = "reasonText"; + int cSeq = 10; + String callId = "TestCallId"; + SipDetails details = new SipDetails.Builder(SipDetails.METHOD_SUBSCRIBE).setCSeq(cSeq) + .setSipResponseCode(responseCode, responsePhrase).setCallId(callId) + .setSipResponseReasonHeader(reasonHdrCause, reasonHdrText).build(); + doReturn(Optional.ofNullable(details)).when(mResponse).getSipDetails(); + SubscribeRequestCoordinator coordinator = getSubscribeCoordinator(); doReturn(true).when(mResponse).isNetworkResponseOK(); doReturn(Optional.of(200)).when(mResponse).getNetworkRespSipCode(); @@ -138,8 +149,21 @@ public class SubscribeCoordinatorTest extends ImsTestBase { assertEquals(1, requestList.size()); assertTrue(resultList.isEmpty()); - verify(mUceStatsWriter).setSubscribeResponse(eq(mSubId), eq(mTaskId), eq(200)); + Iterator<RequestResult> requestResults = resultList.iterator(); + while (requestResults.hasNext()) { + RequestResult req = requestResults.next(); + SipDetails receivedInfo = req.getSipDetails().orElse(null); + assertNotNull(receivedInfo); + assertEquals(SipDetails.METHOD_SUBSCRIBE, receivedInfo.getMethod()); + assertEquals(cSeq, receivedInfo.getCSeq()); + assertEquals(responseCode, receivedInfo.getResponseCode()); + assertEquals(responsePhrase, receivedInfo.getResponsePhrase()); + assertEquals(reasonHdrCause, receivedInfo.getReasonHeaderCause()); + assertEquals(reasonHdrText, receivedInfo.getReasonHeaderText()); + assertEquals(callId, receivedInfo.getCallId()); + } + verify(mUceStatsWriter).setSubscribeResponse(eq(mSubId), eq(mTaskId), eq(200)); verify(mRequest, never()).onFinish(); } @@ -150,10 +174,20 @@ public class SubscribeCoordinatorTest extends ImsTestBase { SubscribeRequestCoordinator coordinator = getSubscribeCoordinator(); + List<EabCapabilityResult> eabResultList = new ArrayList<>(); + + Uri contactUri = Uri.fromParts("tel", "123456789", null); + EabCapabilityResult result = new EabCapabilityResult(contactUri, + EabCapabilityResult.EAB_CONTACT_NOT_FOUND_FAILURE, null); + eabResultList.add(result); + + doReturn(eabResultList).when(mRequestMgrCallback). + getCapabilitiesFromCacheIncludingExpired(any()); + coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_NETWORK_RESPONSE); verify(mUceStatsWriter).setSubscribeResponse(eq(mSubId), eq(mTaskId), eq(400)); - + verify(mRequestMgrCallback, never()).saveCapabilities(any()); verify(mRequest).onFinish(); } diff --git a/tests/src/com/android/ims/rcs/uce/request/SubscribeRequestTest.java b/tests/src/com/android/ims/rcs/uce/request/SubscribeRequestTest.java index b4f9cca4..543ad6d2 100644 --- a/tests/src/com/android/ims/rcs/uce/request/SubscribeRequestTest.java +++ b/tests/src/com/android/ims/rcs/uce/request/SubscribeRequestTest.java @@ -19,7 +19,9 @@ package com.android.ims.rcs.uce.request; import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_NOT_SUPPORTED; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.verify; import android.net.Uri; import android.telephony.ims.RcsContactTerminatedReason; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.ISubscribeResponseCallback; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -36,10 +39,6 @@ import androidx.test.filters.SmallTest; import com.android.ims.ImsTestBase; import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; -import com.android.ims.rcs.uce.util.NetworkSipCode; - -import java.util.ArrayList; -import java.util.List; import org.junit.After; import org.junit.Before; @@ -47,6 +46,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import java.util.ArrayList; +import java.util.List; + @RunWith(AndroidJUnit4.class) public class SubscribeRequestTest extends ImsTestBase { @@ -88,6 +90,7 @@ public class SubscribeRequestTest extends ImsTestBase { subscribeRequest.requestCapabilities(uriList); verify(mRequestResponse).setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); + verify(mRequestResponse, never()).setSipDetails(any()); verify(mRequestManagerCallback).notifyRequestError(eq(mCoordId), anyLong()); verify(mSubscribeController, never()).requestCapabilities(any(), any()); } @@ -101,6 +104,7 @@ public class SubscribeRequestTest extends ImsTestBase { callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED); verify(mRequestResponse).setCommandError(COMMAND_CODE_NOT_SUPPORTED); + verify(mRequestResponse, never()).setSipDetails(any(SipDetails.class)); verify(mRequestManagerCallback).notifyCommandError(eq(mCoordId), anyLong()); } @@ -109,12 +113,16 @@ public class SubscribeRequestTest extends ImsTestBase { public void testNetworkResponse() throws Exception { SubscribeRequest subscribeRequest = getSubscribeRequest(); - int sipCode = NetworkSipCode.SIP_CODE_FORBIDDEN; - String reason = "forbidden"; + int sipCode = 200; + String reason = "OK"; + SipDetails details = new SipDetails.Builder(SipDetails.METHOD_SUBSCRIBE).setCSeq(1) + .setSipResponseCode(sipCode, reason).setCallId("callId").build(); + ISubscribeResponseCallback callback = subscribeRequest.getResponseCallback(); - callback.onNetworkResponse(sipCode, reason); + callback.onNetworkResponse(details); - verify(mRequestResponse).setNetworkResponseCode(sipCode, reason); + verify(mRequestResponse).setSipDetails(eq(details)); + verify(mRequestResponse, never()).setNetworkResponseCode(anyInt(), anyString()); verify(mRequestManagerCallback).notifyNetworkResponse(eq(mCoordId), anyLong()); } @@ -130,6 +138,7 @@ public class SubscribeRequestTest extends ImsTestBase { callback.onResourceTerminated(list); verify(mRequestResponse).addTerminatedResource(list); + verify(mRequestResponse, never()).setSipDetails(any()); verify(mRequestManagerCallback).notifyResourceTerminated(eq(mCoordId), anyLong()); } @@ -144,6 +153,7 @@ public class SubscribeRequestTest extends ImsTestBase { callback.onNotifyCapabilitiesUpdate(pidfXml); verify(mRequestResponse).addUpdatedCapabilities(any()); + verify(mRequestResponse, never()).setSipDetails(any()); verify(mRequestManagerCallback).notifyCapabilitiesUpdated(eq(mCoordId), anyLong()); } @@ -159,6 +169,7 @@ public class SubscribeRequestTest extends ImsTestBase { callback.onTerminated(reason, retryAfterMillis); verify(mRequestResponse).setTerminated(reason, retryAfterMillis); + verify(mRequestResponse, never()).setSipDetails(any()); verify(mRequestManagerCallback).notifyTerminated(eq(mCoordId), anyLong()); } diff --git a/tests/src/com/android/ims/rcs/uce/request/UceRequestManagerTest.java b/tests/src/com/android/ims/rcs/uce/request/UceRequestManagerTest.java index b380eac9..fa8214ed 100644 --- a/tests/src/com/android/ims/rcs/uce/request/UceRequestManagerTest.java +++ b/tests/src/com/android/ims/rcs/uce/request/UceRequestManagerTest.java @@ -31,6 +31,7 @@ import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDA import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -133,7 +134,7 @@ public class UceRequestManagerTest extends ImsTestBase { waitForHandlerAction(handler, 500L); verify(mUceRequest, never()).executeRequest(); - verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); + verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L, null); } /** @@ -164,7 +165,7 @@ public class UceRequestManagerTest extends ImsTestBase { verify(mCapabilitiesCallback).onCapabilitiesReceived( cachedNumbers.stream().map(EabCapabilityResult::getContactCapabilities).collect( Collectors.toList())); - verify(mCapabilitiesCallback).onComplete(); + verify(mCapabilitiesCallback).onComplete(eq(null)); // The cache should have been hit, so no network requests should have been generated. verify(mRequestRepository, never()).addRequestCoordinator(any()); } @@ -196,7 +197,7 @@ public class UceRequestManagerTest extends ImsTestBase { waitForHandlerAction(handler, 500L); // Extract caps from EabCapabilityResult and ensure the Lists match. verify(mCapabilitiesCallback, never()).onCapabilitiesReceived(any()); - verify(mCapabilitiesCallback, never()).onComplete(); + verify(mCapabilitiesCallback, never()).onComplete(any()); // A network request should have been generated for the expired contact. verify(mRequestRepository).addRequestCoordinator(any()); } @@ -240,7 +241,7 @@ public class UceRequestManagerTest extends ImsTestBase { // Extract caps from EabCapabilityResult and ensure the Lists match. verify(mCapabilitiesCallback).onCapabilitiesReceived( Collections.singletonList(cachedItem.getContactCapabilities())); - verify(mCapabilitiesCallback, never()).onComplete(); + verify(mCapabilitiesCallback, never()).onComplete(any()); // The cache should have been hit, but there was also entry that was not in the cache, so // ensure that is requested. verify(mRequestRepository).addRequestCoordinator(any()); |