Add metrics for UCE event about Publish, Subscribe, Options and metrics
for RCS registration features

Bug: http://b/174871215
Test: atest PublishControllerImplTest, PublishProcessorTest,  OptionsCoordinatorTest, RemoteOptionsCoordinatorTest, SubscribeCoordinator for UCE event metrics
atest DeviceCapabilityListenerTest for RCS registration features

Change-Id: I738f05ac69d8a6c47543025d71194238bd32c578
Merged-In: I738f05ac69d8a6c47543025d71194238bd32c578
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 e881ae0..872d35d 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
@@ -46,6 +46,7 @@
 import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType;
 import com.android.ims.rcs.uce.util.UceUtils;
@@ -53,7 +54,12 @@
 import com.android.internal.telephony.util.HandlerExecutor;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Listen to the device changes and notify the PublishController to publish the device's
@@ -65,6 +71,8 @@
 
     private static final long REGISTER_IMS_CHANGED_DELAY = 15000L;  // 15 seconds
 
+    private final UceStatsWriter mUceStatsWriter;
+
     /**
      * Used to inject ImsMmTelManager instances for testing.
      */
@@ -180,7 +188,7 @@
     private final Object mLock = new Object();
 
     public DeviceCapabilityListener(Context context, int subId, DeviceCapabilityInfo info,
-            PublishControllerCallback callback) {
+            PublishControllerCallback callback, UceStatsWriter uceStatsWriter) {
         mSubId = subId;
         logi("create");
 
@@ -188,6 +196,7 @@
         mCallback = callback;
         mCapabilityInfo = info;
         mInitialized = false;
+        mUceStatsWriter = uceStatsWriter;
 
         mHandlerThread = new HandlerThread("DeviceCapListenerThread");
         mHandlerThread.start();
@@ -447,6 +456,12 @@
                     synchronized (mLock) {
                         logi("onRcsRegistered: " + attributes);
                         if (!mIsImsCallbackRegistered) return;
+
+                        List<String> featureTagList = new ArrayList<>(attributes.getFeatureTags());
+                        int registrationTech = attributes.getRegistrationTechnology();
+
+                        mUceStatsWriter.setImsRegistrationFeatureTagStats(
+                                mSubId, featureTagList, registrationTech);
                         handleImsRcsRegistered(attributes);
                     }
                 }
@@ -456,6 +471,7 @@
                     synchronized (mLock) {
                         logi("onRcsUnregistered: " + info);
                         if (!mIsImsCallbackRegistered) return;
+                        mUceStatsWriter.setStoreCompleteImsRegistrationFeatureTagStats(mSubId);
                         handleImsRcsUnregistered();
                     }
                 }
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 83733e3..0ff4885 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
@@ -44,6 +44,7 @@
 import com.android.ims.rcs.uce.UceController.UceControllerCallback;
 import com.android.ims.rcs.uce.UceDeviceState;
 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.ims.rcs.uce.util.UceUtils;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
@@ -79,7 +80,8 @@
     @VisibleForTesting
     public interface DeviceCapListenerFactory {
         DeviceCapabilityListener createDeviceCapListener(Context context, int subId,
-                DeviceCapabilityInfo capInfo, PublishControllerCallback callback);
+                DeviceCapabilityInfo capInfo, PublishControllerCallback callback,
+                UceStatsWriter uceStatsWriter);
     }
 
     private final int mSubId;
@@ -90,6 +92,7 @@
     private volatile boolean mReceivePublishFromService;
     private volatile RcsFeatureManager mRcsFeatureManager;
     private final UceControllerCallback mUceCtrlCallback;
+    private final UceStatsWriter mUceStatsWriter;
 
     // The capability type that the device is using.
     private @RcsImsCapabilityFlag int mCapabilityType;
@@ -115,8 +118,9 @@
 
     // The listener to listen to the device's capabilities changed.
     private DeviceCapabilityListener mDeviceCapListener;
-    private DeviceCapListenerFactory mDeviceCapListenerFactory = (context, subId, capInfo, callback)
-            -> new DeviceCapabilityListener(context, subId, capInfo, callback);
+    private DeviceCapListenerFactory mDeviceCapListenerFactory = (context, subId, capInfo, callback,
+            uceStatsWriter)
+            -> new DeviceCapabilityListener(context, subId, capInfo, callback, uceStatsWriter);
 
     // Listen to the RCS availability status changed.
     private final IImsCapabilityCallback mRcsCapabilitiesCallback =
@@ -141,6 +145,7 @@
         mSubId = subId;
         mContext = context;
         mUceCtrlCallback = callback;
+        mUceStatsWriter = UceStatsWriter.getInstance();
         logi("create");
         initPublishController(looper);
     }
@@ -148,12 +153,13 @@
     @VisibleForTesting
     public PublishControllerImpl(Context context, int subId, UceControllerCallback c,
             Looper looper, DeviceCapListenerFactory deviceCapFactory,
-            PublishProcessorFactory processorFactory) {
+            PublishProcessorFactory processorFactory, UceStatsWriter instance) {
         mSubId = subId;
         mContext = context;
         mUceCtrlCallback = c;
         mDeviceCapListenerFactory = deviceCapFactory;
         mPublishProcessorFactory = processorFactory;
+        mUceStatsWriter = instance;
         initPublishController(looper);
     }
 
@@ -200,7 +206,7 @@
 
     private void initDeviceCapabilitiesListener() {
         mDeviceCapListener = mDeviceCapListenerFactory.createDeviceCapListener(mContext, mSubId,
-                mDeviceCapabilityInfo, mPublishControllerCallback);
+                mDeviceCapabilityInfo, mPublishControllerCallback, mUceStatsWriter);
     }
 
     @Override
@@ -934,6 +940,9 @@
             if (mPublishState == newPublishState) return;
             mPublishState = newPublishState;
         }
+        if (newPublishState == RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED) {
+            mUceStatsWriter.setUnPublish(mSubId);
+        }
 
         // Trigger the publish state changed in handler thread since it may take time.
         logd("Notify publish state changed: " + mPublishState);
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 86d2136..01aa22b 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,8 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
@@ -31,6 +33,7 @@
 import com.android.ims.rcs.uce.presence.pidfparser.PidfParser;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.ims.rcs.uce.util.UceUtils;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -53,6 +56,8 @@
     private volatile boolean mIsDestroyed;
     private volatile RcsFeatureManager mRcsFeatureManager;
 
+    private final UceStatsWriter mUceStatsWriter;
+
     // Manage the state of the publish processor.
     private PublishProcessorState mProcessorState;
 
@@ -74,6 +79,18 @@
         mDeviceCapabilities = capabilityInfo;
         mPublishCtrlCallback = publishCtrlCallback;
         mProcessorState = new PublishProcessorState(subId);
+        mUceStatsWriter = UceStatsWriter.getInstance();
+    }
+
+    @VisibleForTesting
+    public PublishProcessor(Context context, int subId, DeviceCapabilityInfo capabilityInfo,
+            PublishControllerCallback publishCtrlCallback, UceStatsWriter instance) {
+        mSubId = subId;
+        mContext = context;
+        mDeviceCapabilities = capabilityInfo;
+        mPublishCtrlCallback = publishCtrlCallback;
+        mProcessorState = new PublishProcessorState(subId);
+        mUceStatsWriter = instance;
     }
 
     /**
@@ -158,6 +175,13 @@
             return false;
         }
 
+        featureManager.getImsRegistrationTech((tech) -> {
+            int registrationTech = (tech == null)
+                    ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : tech;
+            mUceStatsWriter.setImsRegistrationServiceDescStats(mSubId,
+                    deviceCapability.getCapabilityTuples(), registrationTech);
+        });
+
         // Publish to the Presence server.
         return publishCapabilities(featureManager, pidfXml);
     }
@@ -244,6 +268,13 @@
         mLocalLog.log("Receive command error code=" + requestResponse.getCmdErrorCode());
         logd("onCommandError: " + requestResponse.toString());
 
+        int cmdError = requestResponse.getCmdErrorCode().orElse(0);
+        boolean successful = false;
+        if (cmdError == RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE) {
+            successful = true;
+        }
+        mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.PUBLISH_EVENT, successful, cmdError, 0);
+
         if (requestResponse.needRetry() && !mProcessorState.isReachMaximumRetries()) {
             handleRequestRespWithRetry(requestResponse);
         } else {
@@ -267,6 +298,10 @@
         mLocalLog.log("Receive network response code=" + requestResponse.getNetworkRespSipCode());
         logd("onNetworkResponse: " + requestResponse.toString());
 
+        int responseCode = requestResponse.getNetworkRespSipCode().orElse(0);
+        mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.PUBLISH_EVENT, true, 0,
+            responseCode);
+
         if (requestResponse.needRetry() && !mProcessorState.isReachMaximumRetries()) {
             handleRequestRespWithRetry(requestResponse);
         } else {
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 a150dd6..a266093 100644
--- a/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java
+++ b/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java
@@ -24,6 +24,7 @@
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Collection;
@@ -44,7 +45,14 @@
 
         public Builder(int subId, Collection<UceRequest> requests,
                 RequestManagerCallback callback) {
-            mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback);
+            mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback,
+                    UceStatsWriter.getInstance());
+        }
+        @VisibleForTesting
+        public Builder(int subId, Collection<UceRequest> requests,
+                RequestManagerCallback callback, UceStatsWriter instance) {
+            mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback,
+                    instance);
         }
 
         public Builder setCapabilitiesCallback(IRcsUceControllerCallback callback) {
@@ -105,9 +113,12 @@
     // The callback to notify the result of the capabilities request.
     private IRcsUceControllerCallback mCapabilitiesCallback;
 
+    private final UceStatsWriter mUceStatsWriter;
+
     private OptionsRequestCoordinator(int subId, Collection<UceRequest> requests,
-            RequestManagerCallback requestMgrCallback) {
+            RequestManagerCallback requestMgrCallback, UceStatsWriter instance) {
         super(subId, requests, requestMgrCallback);
+        mUceStatsWriter = instance;
         logd("OptionsRequestCoordinator: created");
     }
 
@@ -189,6 +200,11 @@
         // Finish this request.
         request.onFinish();
 
+        int commandErrorCode = response.getCommandError().orElse(0);
+        mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.OUTGOING_OPTION_EVENT,
+            false, commandErrorCode, 0);
+
+
         // Remove this request from the activated collection and notify RequestManager.
         Long taskId = request.getTaskId();
         RequestResult requestResult = sCommandErrorCreator.createRequestResult(taskId, response);
@@ -203,6 +219,11 @@
         CapabilityRequestResponse response = request.getRequestResponse();
         logd("handleNetworkResponse: " + response.toString());
 
+        int responseCode = response.getNetworkRespSipCode().orElse(0);
+        mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.OUTGOING_OPTION_EVENT, true,
+            0, responseCode);
+
+
         List<RcsContactUceCapability> updatedCapList = response.getUpdatedContactCapability();
         if (!updatedCapList.isEmpty()) {
             // Save the capabilities and trigger the capabilities callback
diff --git a/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java b/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java
index c8aa3f7..5a3e33b 100644
--- a/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java
+++ b/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java
@@ -25,6 +25,7 @@
 
 import com.android.ims.rcs.uce.request.RemoteOptionsRequest.RemoteOptResponse;
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Collection;
@@ -41,7 +42,13 @@
         RemoteOptionsCoordinator mRemoteOptionsCoordinator;
 
         public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c) {
-            mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c);
+            mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c,
+                    UceStatsWriter.getInstance());
+        }
+        @VisibleForTesting
+        public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c,
+                UceStatsWriter instance) {
+            mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c, instance);
         }
 
         public Builder setOptionsRequestCallback(IOptionsRequestCallback callback) {
@@ -78,9 +85,12 @@
     // The callback to notify the result of the remote options request.
     private IOptionsRequestCallback mOptionsReqCallback;
 
+    private final UceStatsWriter mUceStatsWriter;
+
     private RemoteOptionsCoordinator(int subId, Collection<UceRequest> requests,
-            RequestManagerCallback requestMgrCallback) {
+            RequestManagerCallback requestMgrCallback, UceStatsWriter instance) {
         super(subId, requests, requestMgrCallback);
+        mUceStatsWriter = instance;
         logd("RemoteOptionsCoordinator: created");
     }
 
@@ -144,6 +154,9 @@
             boolean isRemoteNumberBlocked) {
         try {
             logd("triggerOptionsReqCallback: start");
+            mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0,
+                200);
+
             mOptionsReqCallback.respondToCapabilityRequest(deviceCaps, isRemoteNumberBlocked);
         } catch (RemoteException e) {
             logw("triggerOptionsReqCallback exception: " + e);
@@ -155,6 +168,9 @@
     private void triggerOptionsReqWithErrorCallback(int errorCode, String reason) {
         try {
             logd("triggerOptionsReqWithErrorCallback: start");
+            mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0,
+                errorCode);
+
             mOptionsReqCallback.respondToCapabilityRequestWithError(errorCode, reason);
         } catch (RemoteException e) {
             logw("triggerOptionsReqWithErrorCallback exception: " + e);
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 ee6bd35..8913340 100644
--- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java
+++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java
@@ -28,6 +28,7 @@
 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
 import com.android.ims.rcs.uce.request.SubscriptionTerminatedHelper.TerminatedResult;
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Collection;
@@ -51,7 +52,13 @@
          * The builder of the SubscribeRequestCoordinator class.
          */
         public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c) {
-            mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c);
+            mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c,
+                    UceStatsWriter.getInstance());
+        }
+        @VisibleForTesting
+        public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c,
+                UceStatsWriter instance) {
+            mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c, instance);
         }
 
         /**
@@ -152,9 +159,12 @@
     // The callback to notify the result of the capabilities request.
     private volatile IRcsUceControllerCallback mCapabilitiesCallback;
 
+    private final UceStatsWriter mUceStatsWriter;
+
     private SubscribeRequestCoordinator(int subId, Collection<UceRequest> requests,
-            RequestManagerCallback requestMgrCallback) {
+            RequestManagerCallback requestMgrCallback, UceStatsWriter instance) {
         super(subId, requests, requestMgrCallback);
+        mUceStatsWriter = instance;
         logd("SubscribeRequestCoordinator: created");
     }
 
@@ -246,6 +256,10 @@
         // Finish this request.
         request.onFinish();
 
+        int commandErrorCode = response.getCommandError().orElse(0);
+        mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.SUBSCRIBE_EVENT,
+            false, commandErrorCode, 0);
+
         // Remove this request from the activated collection and notify RequestManager.
         Long taskId = request.getTaskId();
         RequestResult requestResult = sCommandErrorCreator.createRequestResult(taskId, response,
@@ -261,6 +275,9 @@
         CapabilityRequestResponse response = request.getRequestResponse();
         logd("handleNetworkResponse: " + response.toString());
 
+        int respCode = response.getNetworkRespSipCode().orElse(0);
+        mUceStatsWriter.setSubscribeResponse(mSubId, request.getTaskId(), respCode);
+
         // Refresh the device state with the request result.
         response.getResponseSipCode().ifPresent(sipCode -> {
             String reason = response.getResponseReason().orElse("");
@@ -333,6 +350,7 @@
             return;
         }
 
+        mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, updatedCapList);
         // Save the updated capabilities to the cache.
         mRequestManagerCallback.saveCapabilities(updatedCapList);
 
@@ -356,6 +374,8 @@
             return;
         }
 
+        mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, terminatedResources);
+
         // Save the terminated capabilities to the cache.
         mRequestManagerCallback.saveCapabilities(terminatedResources);
 
@@ -396,6 +416,7 @@
 
         // Remove this request from the activated collection and notify RequestManager.
         Long taskId = request.getTaskId();
+        mUceStatsWriter.setSubscribeTerminated(mSubId, taskId, response.getTerminatedReason());
         RequestResult requestResult = sTerminatedCreator.createRequestResult(taskId, response,
                 mRequestManagerCallback);
         moveRequestToFinishedCollection(taskId, requestResult);
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 bf33103..1959dd0 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
@@ -19,7 +19,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.refEq;
 import static org.mockito.Mockito.verify;
 
 import android.content.BroadcastReceiver;
@@ -39,12 +43,18 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.ims.ImsTestBase;
+import com.android.ims.rcs.uce.UceStatsWriter;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 @RunWith(AndroidJUnit4.class)
 public class DeviceCapabilityListenerTest extends ImsTestBase {
@@ -60,6 +70,7 @@
     @Mock DeviceCapabilityListener.ImsMmTelManagerFactory mImsMmTelMgrFactory;
     @Mock DeviceCapabilityListener.ImsRcsManagerFactory mImsRcsMgrFactory;
     @Mock DeviceCapabilityListener.ProvisioningManagerFactory mProvisioningMgrFactory;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     int mSubId = 1;
 
@@ -77,6 +88,10 @@
         doReturn(true).when(mDeviceCapability).updateVtSetting(anyBoolean());
         doReturn(true).when(mDeviceCapability).updateVtSetting(anyBoolean());
         doReturn(true).when(mDeviceCapability).updateMmtelCapabilitiesChanged(any());
+
+        doNothing().when(mUceStatsWriter).setImsRegistrationFeatureTagStats(
+                anyInt(), anyList(), anyInt());
+        doNothing().when(mUceStatsWriter).setStoreCompleteImsRegistrationFeatureTagStats(anyInt());
     }
 
     @After
@@ -183,8 +198,18 @@
         DeviceCapabilityListener deviceCapListener = createDeviceCapabilityListener();
         deviceCapListener.setImsCallbackRegistered(true);
         RegistrationCallback registrationCallback = deviceCapListener.mRcsRegistrationCallback;
+
+        List<String> list = new ArrayList<>();
+        list.add("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"");
+        list.add("+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"");
+        list.add("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"");
+        Set<String> featureTags = new HashSet<String>(list);
+
         ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build();
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE)
+                .setFeatureTags(featureTags)
+                .build();
+
         // Notify DeviceCapabilityListener that registered has caused a change and requires publish
         doReturn(true).when(mDeviceCapability).updateImsRcsRegistered(attr);
 
@@ -195,6 +220,8 @@
         verify(mDeviceCapability).updateImsRcsRegistered(attr);
         verify(mCallback).requestPublishFromInternal(
                 PublishController.PUBLISH_TRIGGER_RCS_REGISTERED);
+        verify(mUceStatsWriter).setImsRegistrationFeatureTagStats(anyInt(),
+            refEq(list), eq(ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
     }
 
     @Test
@@ -216,6 +243,7 @@
         verify(mDeviceCapability).updateImsRcsUnregistered();
         verify(mCallback).requestPublishFromInternal(
                 PublishController.PUBLISH_TRIGGER_RCS_UNREGISTERED);
+        verify(mUceStatsWriter).setStoreCompleteImsRegistrationFeatureTagStats(anyInt());
     }
 
     @Test
@@ -237,7 +265,7 @@
 
     private DeviceCapabilityListener createDeviceCapabilityListener() {
         DeviceCapabilityListener deviceCapListener = new DeviceCapabilityListener(mContext,
-                mSubId, mDeviceCapability, mCallback);
+                mSubId, mDeviceCapability, mCallback, mUceStatsWriter);
         deviceCapListener.setImsMmTelManagerFactory(mImsMmTelMgrFactory);
         deviceCapListener.setImsRcsManagerFactory(mImsRcsMgrFactory);
         deviceCapListener.setProvisioningMgrFactory(mProvisioningMgrFactory);
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 d78ec42..804702c 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
@@ -43,6 +43,7 @@
 import com.android.ims.RcsFeatureManager;
 import com.android.ims.rcs.uce.UceController;
 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
 import com.android.ims.rcs.uce.presence.publish.PublishControllerImpl.DeviceCapListenerFactory;
 import com.android.ims.rcs.uce.presence.publish.PublishControllerImpl.PublishProcessorFactory;
@@ -68,6 +69,7 @@
     @Mock UceController.UceControllerCallback mUceCtrlCallback;
     @Mock RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
     @Mock DeviceStateResult mDeviceStateResult;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     private int mSubId = 1;
 
@@ -77,7 +79,7 @@
         doReturn(mPublishProcessor).when(mPublishProcessorFactory).createPublishProcessor(any(),
                 eq(mSubId), any(), any());
         doReturn(mDeviceCapListener).when(mDeviceCapListenerFactory).createDeviceCapListener(any(),
-                eq(mSubId), any(), any());
+                eq(mSubId), any(), any(), any());
         doReturn(mDeviceStateResult).when(mUceCtrlCallback).getDeviceState();
         doReturn(false).when(mDeviceStateResult).isRequestForbidden();
     }
@@ -172,6 +174,7 @@
         int publishState = publishController.getUcePublishState();
         assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, publishState);
         verify(mPublishProcessor).resetState();
+        verify(mUceStatsWriter).setUnPublish(eq(mSubId));
     }
 
     @Test
@@ -352,7 +355,7 @@
     private PublishControllerImpl createPublishController() {
         PublishControllerImpl publishController = new PublishControllerImpl(mContext, mSubId,
                 mUceCtrlCallback, Looper.getMainLooper(), mDeviceCapListenerFactory,
-                mPublishProcessorFactory);
+                mPublishProcessorFactory, mUceStatsWriter);
         publishController.setPublishStateCallback(mPublishStateCallbacks);
         publishController.setCapabilityType(RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
         return publishController;
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 d83158f..d894c33 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
@@ -34,6 +34,7 @@
 
 import com.android.ims.ImsTestBase;
 import com.android.ims.RcsFeatureManager;
+import com.android.ims.rcs.uce.UceStatsWriter;
 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
 
 import org.junit.After;
@@ -42,6 +43,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
+import java.util.Optional;
 @RunWith(AndroidJUnit4.class)
 public class PublishProcessorTest extends ImsTestBase {
 
@@ -50,6 +52,7 @@
     @Mock PublishControllerCallback mPublishCtrlCallback;
     @Mock PublishProcessorState mProcessorState;
     @Mock PublishRequestResponse mResponseCallback;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     private int mSub = 1;
     private long mTaskId = 1L;
@@ -144,6 +147,8 @@
         doReturn(mTaskId).when(mProcessorState).getCurrentTaskId();
         doReturn(mTaskId).when(mResponseCallback).getTaskId();
         doReturn(true).when(mResponseCallback).needRetry();
+        doReturn(Optional.of(10)).when(mResponseCallback).getCmdErrorCode();
+
         PublishProcessor publishProcessor = getPublishProcessor();
 
         publishProcessor.onCommandError(mResponseCallback);
@@ -154,6 +159,8 @@
         verify(mResponseCallback).onDestroy();
         verify(mProcessorState).setPublishingFlag(false);
         verify(mPublishCtrlCallback).clearRequestCanceledTimer();
+        verify(mUceStatsWriter).setUceEvent(eq(mSub), eq(UceStatsWriter.PUBLISH_EVENT), eq(true),
+                eq(10), eq(0));
     }
 
     @Test
@@ -200,6 +207,7 @@
         doReturn(mTaskId).when(mResponseCallback).getTaskId();
         doReturn(false).when(mResponseCallback).needRetry();
         doReturn(true).when(mResponseCallback).isRequestSuccess();
+        doReturn(Optional.of(200)).when(mResponseCallback).getNetworkRespSipCode();
         PublishProcessor publishProcessor = getPublishProcessor();
 
         publishProcessor.onNetworkResponse(mResponseCallback);
@@ -208,6 +216,9 @@
         verify(mResponseCallback).onDestroy();
         verify(mProcessorState).setPublishingFlag(false);
         verify(mPublishCtrlCallback).clearRequestCanceledTimer();
+
+        verify(mUceStatsWriter).setUceEvent(eq(mSub), eq(UceStatsWriter.PUBLISH_EVENT), eq(true),
+                eq(0), eq(200));
     }
 
     @Test
@@ -223,7 +234,7 @@
 
     private PublishProcessor getPublishProcessor() {
         PublishProcessor publishProcessor = new PublishProcessor(mContext, mSub,
-                mDeviceCapabilities, mPublishCtrlCallback);
+                mDeviceCapabilities, mPublishCtrlCallback, mUceStatsWriter);
         publishProcessor.setProcessorState(mProcessorState);
         publishProcessor.onRcsConnected(mRcsFeatureManager);
         return publishProcessor;
diff --git a/tests/src/com/android/ims/rcs/uce/request/OptionsCoordinatorTest.java b/tests/src/com/android/ims/rcs/uce/request/OptionsCoordinatorTest.java
index 9c270fb..1759de5 100644
--- a/tests/src/com/android/ims/rcs/uce/request/OptionsCoordinatorTest.java
+++ b/tests/src/com/android/ims/rcs/uce/request/OptionsCoordinatorTest.java
@@ -38,11 +38,13 @@
 import com.android.ims.ImsTestBase;
 import com.android.ims.rcs.uce.request.UceRequestCoordinator.RequestResult;
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
+import com.android.ims.rcs.uce.UceStatsWriter;
 
 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;
@@ -57,6 +59,7 @@
     @Mock CapabilityRequestResponse mResponse;
     @Mock RequestManagerCallback mRequestMgrCallback;
     @Mock IRcsUceControllerCallback mUceCallback;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     private int mSubId = 1;
     private long mTaskId = 1L;
@@ -93,11 +96,14 @@
     @Test
     @SmallTest
     public void testRequestCommandError() throws Exception {
+        doReturn(Optional.of(3)).when(mResponse).getCommandError();
         OptionsRequestCoordinator coordinator = getOptionsCoordinator();
 
         coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_COMMAND_ERROR);
 
         verify(mRequest).onFinish();
+        verify(mUceStatsWriter).setUceEvent(eq(mSubId), eq(UceStatsWriter.OUTGOING_OPTION_EVENT),
+            eq(false), eq(3), eq(0));
 
         Collection<UceRequest> requestList = coordinator.getActivatedRequest();
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
@@ -111,6 +117,7 @@
     public void testRequestNetworkResponse() throws Exception {
         OptionsRequestCoordinator coordinator = getOptionsCoordinator();
         doReturn(true).when(mResponse).isNetworkResponseOK();
+        doReturn(Optional.of(200)).when(mResponse).getNetworkRespSipCode();
 
         final List<RcsContactUceCapability> updatedCapList = new ArrayList<>();
         RcsContactUceCapability updatedCapability = getContactUceCapability();
@@ -124,6 +131,8 @@
         verify(mResponse).removeUpdatedCapabilities(updatedCapList);
 
         verify(mRequest).onFinish();
+        verify(mUceStatsWriter).setUceEvent(eq(mSubId), eq(UceStatsWriter.OUTGOING_OPTION_EVENT),
+            eq(true), eq(0), eq(200));
 
         Collection<UceRequest> requestList = coordinator.getActivatedRequest();
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
@@ -134,9 +143,10 @@
 
     private OptionsRequestCoordinator getOptionsCoordinator() {
         OptionsRequestCoordinator.Builder builder = new OptionsRequestCoordinator.Builder(
-                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback);
+                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback, mUceStatsWriter);
         builder.setCapabilitiesCallback(mUceCallback);
-        return builder.build();
+        OptionsRequestCoordinator coordinator = builder.build();
+        return coordinator;
     }
 
     private RcsContactUceCapability getContactUceCapability() {
diff --git a/tests/src/com/android/ims/rcs/uce/request/RemoteOptionsCoordinatorTest.java b/tests/src/com/android/ims/rcs/uce/request/RemoteOptionsCoordinatorTest.java
index 1a6ed4a..8007711 100644
--- a/tests/src/com/android/ims/rcs/uce/request/RemoteOptionsCoordinatorTest.java
+++ b/tests/src/com/android/ims/rcs/uce/request/RemoteOptionsCoordinatorTest.java
@@ -37,6 +37,7 @@
 import com.android.ims.rcs.uce.request.RemoteOptionsRequest.RemoteOptResponse;
 import com.android.ims.rcs.uce.request.UceRequestCoordinator.RequestResult;
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
+import com.android.ims.rcs.uce.UceStatsWriter;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -54,6 +55,7 @@
     @Mock RemoteOptResponse mResponse;
     @Mock RequestManagerCallback mRequestMgrCallback;
     @Mock IOptionsRequestCallback mOptRequestCallback;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     private int mSubId = 1;
     private long mTaskId = 1L;
@@ -84,6 +86,8 @@
         verify(mOptRequestCallback).respondToCapabilityRequest(updatedCapability, true);
 
         verify(mRequest).onFinish();
+        verify(mUceStatsWriter).setUceEvent(eq(mSubId), eq(UceStatsWriter.INCOMING_OPTION_EVENT),
+            eq(true), eq(0), eq(200));
 
         Collection<UceRequest> requestList = coordinator.getActivatedRequest();
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
@@ -94,7 +98,7 @@
 
     private RemoteOptionsCoordinator getRemoteOptCoordinator() {
         RemoteOptionsCoordinator.Builder builder = new RemoteOptionsCoordinator.Builder(
-                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback);
+                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback, mUceStatsWriter);
         builder.setOptionsRequestCallback(mOptRequestCallback);
         return builder.build();
     }
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 137b4ac..bcd3c98 100644
--- a/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java
+++ b/tests/src/com/android/ims/rcs/uce/request/SubscribeCoordinatorTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.net.Uri;
+import android.telephony.ims.RcsContactPresenceTuple;
 import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 
@@ -47,6 +48,7 @@
 
 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.request.UceRequestCoordinator.RequestResult;
 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
 
@@ -70,6 +72,7 @@
     @Mock RequestManagerCallback mRequestMgrCallback;
     @Mock IRcsUceControllerCallback mUceCallback;
     @Mock DeviceStateResult mDeviceStateResult;
+    @Mock UceStatsWriter mUceStatsWriter;
 
     private int mSubId = 1;
     private long mTaskId = 1L;
@@ -106,6 +109,7 @@
     @Test
     @SmallTest
     public void testRequestCommandError() throws Exception {
+        doReturn(Optional.of(3)).when(mResponse).getCommandError();
         SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
 
         coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_COMMAND_ERROR);
@@ -114,6 +118,9 @@
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
         assertTrue(requestList.isEmpty());
         assertEquals(1, resultList.size());
+
+        verify(mUceStatsWriter).setUceEvent(eq(mSubId), eq(UceStatsWriter.SUBSCRIBE_EVENT),
+            eq(false), eq(3), eq(0));
         verify(mRequest).onFinish();
     }
 
@@ -122,6 +129,7 @@
     public void testRequestNetworkRespSuccess() throws Exception {
         SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
         doReturn(true).when(mResponse).isNetworkResponseOK();
+        doReturn(Optional.of(200)).when(mResponse).getNetworkRespSipCode();
 
         coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_NETWORK_RESPONSE);
 
@@ -129,11 +137,28 @@
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
         assertEquals(1, requestList.size());
         assertTrue(resultList.isEmpty());
+
+        verify(mUceStatsWriter).setSubscribeResponse(eq(mSubId), eq(mTaskId), eq(200));
+
         verify(mRequest, never()).onFinish();
     }
 
     @Test
     @SmallTest
+    public void testRequestNetworkRespFailure() throws Exception {
+        doReturn(Optional.of(400)).when(mResponse).getNetworkRespSipCode();
+
+        SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
+
+        coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_NETWORK_RESPONSE);
+
+        verify(mUceStatsWriter).setSubscribeResponse(eq(mSubId), eq(mTaskId), eq(400));
+
+        verify(mRequest).onFinish();
+    }
+
+    @Test
+    @SmallTest
     public void testRequestNetworkRespError() throws Exception {
         SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
         doReturn(false).when(mResponse).isNetworkResponseOK();
@@ -162,7 +187,8 @@
         SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
 
         final List<RcsContactUceCapability> updatedCapList = new ArrayList<>();
-        RcsContactUceCapability updatedCapability = getContactUceCapability();
+        RcsContactPresenceTuple tuple = getContactPresenceTuple();
+        RcsContactUceCapability updatedCapability = getContactUceCapability(tuple);
         updatedCapList.add(updatedCapability);
         doReturn(updatedCapList).when(mResponse).getUpdatedContactCapability();
 
@@ -171,6 +197,8 @@
         verify(mRequestMgrCallback).saveCapabilities(updatedCapList);
         verify(mUceCallback).onCapabilitiesReceived(updatedCapList);
         verify(mResponse).removeUpdatedCapabilities(updatedCapList);
+
+        verify(mUceStatsWriter).setPresenceNotifyEvent(eq(mSubId), eq(mTaskId), any());
     }
 
     @Test
@@ -188,6 +216,8 @@
         verify(mRequestMgrCallback).saveCapabilities(updatedCapList);
         verify(mUceCallback).onCapabilitiesReceived(updatedCapList);
         verify(mResponse).removeTerminatedResources(updatedCapList);
+
+        verify(mUceStatsWriter).setPresenceNotifyEvent(eq(mSubId), eq(mTaskId), any());
     }
 
     @Test
@@ -211,12 +241,16 @@
     public void testRequestTerminated() throws Exception {
         SubscribeRequestCoordinator coordinator = getSubscribeCoordinator();
 
+        doReturn("noresource").when(mResponse).getTerminatedReason();
+
         coordinator.onRequestUpdated(mTaskId, REQUEST_UPDATE_TERMINATED);
 
         Collection<UceRequest> requestList = coordinator.getActivatedRequest();
         Collection<RequestResult> resultList = coordinator.getFinishedRequest();
         assertTrue(requestList.isEmpty());
         assertEquals(1, resultList.size());
+
+        verify(mUceStatsWriter).setSubscribeTerminated(eq(mSubId), eq(mTaskId), eq("noresource"));
     }
 
     @Test
@@ -234,8 +268,18 @@
 
     private SubscribeRequestCoordinator getSubscribeCoordinator() {
         SubscribeRequestCoordinator.Builder builder = new SubscribeRequestCoordinator.Builder(
-                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback);
+                mSubId, Collections.singletonList(mRequest), mRequestMgrCallback, mUceStatsWriter);
         builder.setCapabilitiesCallback(mUceCallback);
+        SubscribeRequestCoordinator subCoor = builder.build();
+        return subCoor;
+    }
+
+    private RcsContactUceCapability getContactUceCapability(RcsContactPresenceTuple tuple) {
+        int requestResult = RcsContactUceCapability.REQUEST_RESULT_FOUND;
+        RcsContactUceCapability.PresenceBuilder builder =
+                new RcsContactUceCapability.PresenceBuilder(
+                        mContact, RcsContactUceCapability.SOURCE_TYPE_NETWORK, requestResult);
+        builder.addCapabilityTuple(tuple);
         return builder.build();
     }
 
@@ -246,4 +290,12 @@
                         mContact, RcsContactUceCapability.SOURCE_TYPE_NETWORK, requestResult);
         return builder.build();
     }
+
+    private RcsContactPresenceTuple getContactPresenceTuple() {
+        RcsContactPresenceTuple.Builder builder =
+            new RcsContactPresenceTuple.Builder("open", RcsContactPresenceTuple.SERVICE_ID_CHAT_V1,
+                "1.0");
+        return builder.build();
+
+    }
 }