Snap for 10453563 from f61dbc79584e2bd99c6588abed0d176b368a20d5 to mainline-art-release

Change-Id: I7ec44d47ea7592899cfe1c59e2d30a0945fb94d4
diff --git a/OWNERS b/OWNERS
index 8a0d4ed..fa4ba42 100644
--- a/OWNERS
+++ b/OWNERS
@@ -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 06230a1..29f6acd 100755
--- a/src/java/com/android/ims/ImsCall.java
+++ b/src/java/com/android/ims/ImsCall.java
@@ -537,6 +537,22 @@
         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 @@
     }
 
     /**
-     * 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 @@
                 }
             }
         }
+
+        @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 c41426d..b5a1168 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.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.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 @@
     @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 @@
         }
 
         @Override
-        public int[] getSubscriptionIds(int slotIndex) {
-            return getSubscriptionManager().getSubscriptionIds(slotIndex);
+        public int getSubscriptionId(int slotIndex) {
+            return SubscriptionManager.getSubscriptionId(slotIndex);
         }
 
         @Override
@@ -1433,12 +1437,7 @@
     }
 
     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 @@
         }
     }
 
+    /**
+     * 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 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 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 d02ffaa..17e3415 100644
--- a/src/java/com/android/ims/ImsUt.java
+++ b/src/java/com/android/ims/ImsUt.java
@@ -382,9 +382,9 @@
             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 3170d41..c09fd94 100644
--- a/src/java/com/android/ims/MmTelFeatureConnection.java
+++ b/src/java/com/android/ims/MmTelFeatureConnection.java
@@ -22,8 +22,11 @@
 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.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.IImsUt;
 
 import java.util.ArrayList;
-import java.util.Optional;
+import java.util.HashMap;
 import java.util.Set;
 
 /**
@@ -100,7 +106,6 @@
     }
 
     private class CapabilityCallbackManager extends ImsCallbackAdapterManager<IImsCapabilityCallback> {
-
         public CapabilityCallbackManager(Context context, Object lock) {
             super(context, lock, mSlotId, mSubId);
         }
@@ -376,6 +381,22 @@
         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 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 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 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 @@
         }
     }
 
+    /**
+     * 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 e034a68..e3f50c3 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.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.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 @@
          * 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 @@
                 }
 
                 @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 @@
         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 6fb27b0..aeab061 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 @@
 
 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.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 @@
     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 @@
         /**
          * 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 @@
         }
 
         /** @Return the cached pubilsh update event */
-        public synchronized Optional<SomeArgs> getPublishUpdatedEvent() {
+        public synchronized Optional<SipDetails> getPublishUpdatedEvent() {
             return mPublishUpdatedEvent;
         }
 
@@ -285,7 +281,6 @@
         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 @@
         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 @@
                 }
 
                 @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 @@
         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 @@
             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 @@
         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 @@
             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 @@
      * 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 b4406fd..738a4fc 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.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 @@
         }
 
         @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 dc79433..4929148 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 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 @@
 
     // 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 @@
         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 @@
     /**
      * 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 @@
             mRcsRegistered = false;
             changed = true;
         }
+
+        mLastRegistrationFeatureTags = Collections.emptySet();
+        updateRegistration(mLastRegistrationFeatureTags);
         mRcsNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        mLastSuccessfulCapabilities.clear();
+        mPendingPublishCapabilities = null;
         return changed;
     }
 
@@ -373,19 +391,6 @@
     }
 
     /**
-     * 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 @@
         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 @@
             @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 442cf7d..b58f7ec 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 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 @@
     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 @@
                             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 void onSubscriberAssociatedUriChanged(Uri[] uris) {
                     synchronized (mLock) {
                         logi("onRcsSubscriberAssociatedUriChanged");
-                        handleRcsSubscriberAssociatedUriChanged(uris, true);
+                        handleRcsSubscriberAssociatedUriChanged(uris, false);
                     }
                 }
     };
@@ -523,7 +520,7 @@
                 public void onSubscriberAssociatedUriChanged(Uri[] uris) {
                     synchronized (mLock) {
                         logi("onMmTelSubscriberAssociatedUriChanged");
-                        handleMmTelSubscriberAssociatedUriChanged(uris, true);
+                        handleMmTelSubscriberAssociatedUriChanged(uris, false);
                     }
                 }
             };
@@ -571,15 +568,6 @@
         }
     }
 
-    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 @@
      * 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 @@
     /*
      * 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 @@
     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 @@
     /*
      * 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));
 
-        if (triggerPublish && hasChanged) {
+        logi("handleRcsSubscriberAssociatedUriChanged: 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_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 b20f5ce..732a558 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 @@
 
 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 @@
     /** 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 @@
         /**
          * 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 @@
     /**
      * 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 034d9ac..101aa81 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.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.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 @@
         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 @@
             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 @@
      * 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 @@
 
                 @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 @@
                     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 @@
                     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 @@
          * 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 @@
             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 @@
         /**
          * 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 @@
         // 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 @@
             // 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 @@
      * 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 @@
         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 @@
         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 @@
             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 @@
                     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 @@
         }
     }
 
-    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 d87eea9..c239dd5 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.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 @@
         logi("onRcsDisconnected");
         mRcsFeatureManager = null;
         mProcessorState.onRcsDisconnected();
+        // reset the publish capabilities.
+        mDeviceCapabilities.resetPresenceCapability();
     }
 
     /**
@@ -152,10 +155,15 @@
         }
 
         // 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 @@
         // 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 @@
         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 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 47aa37c..aa1b27c 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 @@
     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 @@
         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 @@
         }
 
         @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 @@
     }
 
     /**
+     * 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 @@
         }
     }
 
-    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);
+        mNetworkRespSipCode = Optional.of(details.getResponseCode());
+        mReasonPhrase = Optional.ofNullable(details.getResponsePhrase());
+        if (details.getReasonHeaderCause() != 0) {
+            mReasonHeaderCause = Optional.of(details.getReasonHeaderCause());
+        }
+        if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+            mReasonHeaderText = Optional.empty();
+        } else {
+            mReasonHeaderText = Optional.ofNullable(details.getReasonHeaderText());
+        }
+        mSipDetails = Optional.ofNullable(details);
         updateRetryFlagByNetworkResponse();
 
         PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
         if (ctrlCallback != null) {
             ctrlCallback.onRequestNetworkResp(this);
         } else {
-            Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sip code=" + sipCode);
-        }
-    }
-
-    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;
-        }
-        mResponseTimestamp = Instant.now();
-        mNetworkRespSipCode = Optional.of(sipCode);
-        mReasonPhrase = Optional.ofNullable(reasonPhrase);
-        mReasonHeaderCause = Optional.of(reasonHeaderCause);
-        mReasonHeaderText = Optional.ofNullable(reasonHeaderText);
-        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 a107b1a..7f7ea3e 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 @@
      */
     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 @@
                         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 0f271e1..e175f2a 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 @@
                     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 97371b8..6da3130 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 @@
     // 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 @@
         mUpdatedCapabilityList = new ArrayList<>();
         mRemoteCaps = new HashSet<>();
         mContactCapsReceived = new HashMap<>();
+        mSipDetails = Optional.empty();
     }
 
     /**
@@ -169,12 +176,20 @@
     /**
      * 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 @@
     }
 
     /**
+     * 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 a266093..b2db178 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 @@
     private void triggerCompletedCallback() {
         try {
             logd("triggerCompletedCallback");
-            mCapabilitiesCallback.onComplete();
+            mCapabilitiesCallback.onComplete(null);
         } catch (RemoteException e) {
             logw("triggerCompletedCallback exception: " + e);
         } finally {
@@ -329,7 +329,7 @@
     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 bee7177..a306eb4 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 @@
                     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 @@
     }
 
     // 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 f44686a..26143f9 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 @@
 
 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 @@
     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 @@
         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 @@
             // 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 @@
             // 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 @@
                         .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 @@
     /**
      * 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 @@
     /**
      * 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 eea4fbe..5d3a35d 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.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 @@
         }
 
         /**
+         * 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 @@
             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 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 85908f0..8ff39c1 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 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 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 @@
             }
             if (nonCachedUris.isEmpty()) {
                 logd("sendRequestInternal: shortcut complete, sending success result");
-                callback.onComplete();
+                callback.onComplete(null);
                 return;
             }
         }
@@ -525,7 +525,7 @@
 
         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 8dbceda..ed2eef4 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 @@
                     + "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 82c303d..7ae66b5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -40,6 +40,7 @@
     ],
 
     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 e987b38..21dd634 100644
--- a/tests/src/com/android/ims/ContextFixture.java
+++ b/tests/src/com/android/ims/ContextFixture.java
@@ -33,6 +33,7 @@
 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 @@
         }
 
         @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 0653908..ad5051b 100644
--- a/tests/src/com/android/ims/ImsManagerTest.java
+++ b/tests/src/com/android/ims/ImsManagerTest.java
@@ -119,7 +119,7 @@
 
         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 @@
 
     }
 
+    @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 021a7c1..79702ed 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 @@
         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 @@
         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 @@
         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 @@
         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 c977a08..de045de 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.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.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 @@
 
     @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 @@
 
     @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 @@
         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 @@
         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 2d170ab..1135039 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 @@
                 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 @@
 
         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 @@
 
     @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 @@
         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 @@
         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 a7e0bbb..aa145c7 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 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.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 @@
         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 @@
         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 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 @@
         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 @@
     @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 4e8cdfd..2de4714 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.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 @@
 
         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 @@
         PublishProcessor publishProcessor = getPublishProcessor();
 
         publishProcessor.doPublish(PublishController.PUBLISH_TRIGGER_RETRY);
-
+        verify(mDeviceCapabilities).getChangedPresenceCapability(any());
         verify(mProcessorState, never()).resetRetryCount();
     }
 
@@ -139,7 +141,7 @@
 
         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 @@
 
         publishProcessor.onCommandError(mResponseCallback);
 
+        verify(mDeviceCapabilities).setPresencePublishResult(false);
         verify(mProcessorState).increaseRetryCount();
         verify(mPublishCtrlCallback).requestPublishFromInternal(
                 eq(PublishController.PUBLISH_TRIGGER_RETRY));
@@ -188,11 +191,13 @@
         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 @@
 
         publishProcessor.onNetworkResponse(mResponseCallback);
 
+        verify(mDeviceCapabilities).setPresencePublishResult(false);
         verify(mProcessorState).increaseRetryCount();
         verify(mPublishCtrlCallback).requestPublishFromInternal(
                 eq(PublishController.PUBLISH_TRIGGER_RETRY));
@@ -226,11 +232,20 @@
         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 @@
         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 5500629..52017c7 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 @@
         t1.updateImsRegistration(imsReg);
         assertEquals(expectedSet, t1.copyRegistrationCapabilities());
 
-
         expectedSet = Collections.singleton(
                 ServiceDescription.SERVICE_DESCRIPTION_CHATBOT_SESSION);
         imsReg = createImsRegistration(
@@ -139,6 +138,30 @@
                 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 bcd3c98..2c0042e 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_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 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 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 @@
     @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 @@
         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 @@
 
         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 b4f9cca..543ad6d 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 @@
 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 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 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.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 @@
         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 @@
         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 void testNetworkResponse() throws Exception {
         SubscribeRequest subscribeRequest = getSubscribeRequest();
 
-        int sipCode = NetworkSipCode.SIP_CODE_FORBIDDEN;
-        String reason = "forbidden";
-        ISubscribeResponseCallback callback = subscribeRequest.getResponseCallback();
-        callback.onNetworkResponse(sipCode, reason);
+        int sipCode = 200;
+        String reason = "OK";
+        SipDetails details = new SipDetails.Builder(SipDetails.METHOD_SUBSCRIBE).setCSeq(1)
+                .setSipResponseCode(sipCode, reason).setCallId("callId").build();
 
-        verify(mRequestResponse).setNetworkResponseCode(sipCode, reason);
+        ISubscribeResponseCallback callback = subscribeRequest.getResponseCallback();
+        callback.onNetworkResponse(details);
+
+        verify(mRequestResponse).setSipDetails(eq(details));
+        verify(mRequestResponse, never()).setNetworkResponseCode(anyInt(), anyString());
         verify(mRequestManagerCallback).notifyNetworkResponse(eq(mCoordId), anyLong());
     }
 
@@ -130,6 +138,7 @@
         callback.onResourceTerminated(list);
 
         verify(mRequestResponse).addTerminatedResource(list);
+        verify(mRequestResponse, never()).setSipDetails(any());
         verify(mRequestManagerCallback).notifyResourceTerminated(eq(mCoordId), anyLong());
     }
 
@@ -144,6 +153,7 @@
         callback.onNotifyCapabilitiesUpdate(pidfXml);
 
         verify(mRequestResponse).addUpdatedCapabilities(any());
+        verify(mRequestResponse, never()).setSipDetails(any());
         verify(mRequestManagerCallback).notifyCapabilitiesUpdated(eq(mCoordId), anyLong());
     }
 
@@ -159,6 +169,7 @@
         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 b380eac..fa8214e 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 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 @@
         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 @@
         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 @@
         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 @@
         // 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());