Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af94eeb0 authored by Hakjun Choi's avatar Hakjun Choi Committed by Android (Google) Code Review
Browse files

Merge changes from topic "RCS single registration metrics" into sc-v2-dev

* changes:
  Add metrics for UCE event about Publish, Subscribe, Options and metrics for RCS registration features
  [UCE] Reset the retry count and times related publish when the publish status changed to not_published
  Create UceStatsWriter for RCS metrics
parents b6cf2533 be5550dc
Loading
Loading
Loading
Loading
+263 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ims.rcs.uce;

import android.annotation.IntDef;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
import android.telephony.ims.RcsContactPresenceTuple;
import android.telephony.ims.RcsContactUceCapability;
import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * The UceStatsWriter should be a singleton class for storing atoms in RcsStats.
 * ims-common provides an interface for setting atoms to telephony-common.
 **/
public class UceStatsWriter {
    private static UceStatsWriter sInstance = null;
    private UceStatsCallback mCallBack;

    /**
     * @hide
     */
    // Defines which UCE event occurred.
    @IntDef(value = {
        PUBLISH_EVENT,
        SUBSCRIBE_EVENT,
        INCOMING_OPTION_EVENT,
        OUTGOING_OPTION_EVENT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface UceEventType {}
    /**
     * UCE events related to Publish Method.
     */
    public static final int PUBLISH_EVENT = 0;
    /**
     * UCE events related to Subscribe Method.
     */
    public static final int SUBSCRIBE_EVENT = 1;
    /**
     * UCE events related to incoming Options Method.
     */
    public static final int INCOMING_OPTION_EVENT = 2;
    /**
     * UCE events related to outgoing Options Method.
     */
    public static final int OUTGOING_OPTION_EVENT = 3;

    /**
     * The callback interface is called by the Metrics data creator to receive information from
     * others controllers.
     */
    public interface UceStatsCallback {
        /**
         * Notify the callback listener that the feature tag associated with
         * IMS registration of this device have changed.
         */
        public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
            int registrationTech);

        /**
         * Notify that the active IMS registration to the carrier network has been terminated.
         */
        public void onStoreCompleteImsRegistrationFeatureTagStats(int subId);

        /**
         * Notify the callback listener that the PIDF ServiceDescriptions associated with
         * the UCE presence of this device have changed.
         */
        void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
            List<String> serviceIdVersionList, int registrationTech);

        /**
         * Notify the callback listener that a subscribe response received.
         */
        void onSubscribeResponse(int subId, long taskId, int networkResponse);

        /**
         * Notify the callback listener that a UCE Network Event has occurred.
         */
        void onUceEvent(int subId, int type, boolean successful, int commandCode,
            int networkResponse);

        /**
         * Notify the callback listener that a subscribe has ended.
         */
        void onSubscribeTerminated(int subId, long taskId, String reason);

        /**
         * Notify that the Presence Notify Event has changed.
         */
        void onPresenceNotifyEvent(int subId, long taskId,
            List<RcsContactUceCapability> updatedCapList);

        /**
         * Notify that the active UCE PUBLISH to the carrier network has been terminated.
         */
        void onStoreCompleteImsRegistrationServiceDescStats(int subId);
    }

    /**
     * create an instance of UceStatsWriter
     */
    public static UceStatsWriter init(UceStatsCallback callback) {
        synchronized (UceStatsWriter.class) {
            if (sInstance == null) {
                sInstance = new UceStatsWriter(callback);
            }
            return sInstance;
        }
    }

    /**
     * get the current instance of UceStatsWriter
     */
    public static UceStatsWriter getInstance() {
        synchronized (UceStatsWriter.class) {
            return sInstance;
        }
    }

    /**
     * Stats about each Feature tag that was included in IMS registration received from
     * the network during register.
     * @param subId The subId associated with the event.
     * @param featureTagList Ims Feature tag list.
     * @param registrationTech The registration tech associated with the feature tag.
     */
    public void setImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
        @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
        if (mCallBack == null) {
            return;
        }
        mCallBack.onImsRegistrationFeatureTagStats(subId, featureTagList, registrationTech);
    }

    /**
     * Update time of stats for each stored feature tag.
     * @param subId The subId associated with the event.
     */
    public void setStoreCompleteImsRegistrationFeatureTagStats(int subId) {
        if (mCallBack == null) {
            return;
        }
        mCallBack.onStoreCompleteImsRegistrationFeatureTagStats(subId);
    }

    /**
     * Stats about each ServiceDescription that was included in the PIDF XML sent to
     * the network during publish
     * @param subId The subId associated with the event.
     * @param tupleList Tuple information set in PIDF.
     * @param registrationTech The registration tech associated with the feature tag.
     */
    public void setImsRegistrationServiceDescStats(int subId,
        List<RcsContactPresenceTuple> tupleList,
        @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
        if (mCallBack == null) {
            return;
        }
        ArrayList<String> svcId = new ArrayList<>();
        ArrayList<String> svcVersion = new ArrayList<>();

        for (RcsContactPresenceTuple tuple : tupleList) {
            svcId.add(tuple.getServiceId());
            svcVersion.add(tuple.getServiceVersion());
        }
        mCallBack.onImsRegistrationServiceDescStats(subId, svcId, svcVersion, registrationTech);
    }

    /**
     * Stats related to UCE queries to the network
     * @param subId The subId associated with the event.
     * @param taskId The taskId associate with the event.
     * @param networkResponse The network response code for the Uce event
     */
    public void setSubscribeResponse(int subId, long taskId, int networkResponse) {
        if (mCallBack != null) {
            mCallBack.onSubscribeResponse(subId, taskId, networkResponse);
        }
    }

    /**
     * Stats related to UCE queries to the network
     * @param subId The subId associated with the event.
     * @param type Used to identify the message type.
     * @param successful Whether the UCE event is successfully finished.
     * @param commandCode The command error code for the Uce event
     * @param networkResponse The network response code for the Uce event
     */
    public void setUceEvent(int subId, @UceEventType int type, boolean successful,
        @RcsCapabilityExchangeImplBase.CommandCode int commandCode, int networkResponse) {
        if (mCallBack != null) {
            mCallBack.onUceEvent(subId, type, successful, commandCode, networkResponse);
        }
    }

    /**
     * The result of processing received notify messages.
     * @param subId The subId associated with the event.
     * @param taskId The taskId associate with the event.
     * @param updatedCapList Capability information of the user contained in Presence Notify.
     */
    public void setPresenceNotifyEvent(int subId, long taskId,
        List<RcsContactUceCapability> updatedCapList) {
        if (mCallBack == null || updatedCapList == null || updatedCapList.isEmpty()) {
            return;
        }
        mCallBack.onPresenceNotifyEvent(subId, taskId, updatedCapList);
    }

    /**
     * Indicates that the subscription request has become a terminated state.
     * @param subId The subId associated with the event.
     * @param taskId The taskId associate with the event.
     * @param reason The terminated reason associated with the subscription state.
     */
    public void setSubscribeTerminated(int subId, long taskId, String reason) {
        if (mCallBack != null) {
            mCallBack.onSubscribeTerminated(subId, taskId, reason);
        }
    }

    /**
     * indicates that the device has removed an existing PUBLISH from the carrier's network
     * containing the device's RCS capabilities state.
     * The registered time of publication must be set in ImsRegistrationServiceDescStats,
     * which is the life time of publication, so it can be set only when publication is over.
     * @param subId The subId associated with the event.
     */
    public void setUnPublish(int subId) {
        if (mCallBack != null) {
            mCallBack.onStoreCompleteImsRegistrationServiceDescStats(subId);
        }
    }

    @VisibleForTesting
    protected UceStatsWriter(UceStatsCallback callback) {
        mCallBack = callback;
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.util.IndentingPrintWriter;
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.annotations.VisibleForTesting;
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 @@ public class DeviceCapabilityListener {

    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 @@ public class DeviceCapabilityListener {
    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 @@ public class DeviceCapabilityListener {
        mCallback = callback;
        mCapabilityInfo = info;
        mInitialized = false;
        mUceStatsWriter = uceStatsWriter;

        mHandlerThread = new HandlerThread("DeviceCapListenerThread");
        mHandlerThread.start();
@@ -447,6 +456,12 @@ public class DeviceCapabilityListener {
                    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 @@ public class DeviceCapabilityListener {
                    synchronized (mLock) {
                        logi("onRcsUnregistered: " + info);
                        if (!mIsImsCallbackRegistered) return;
                        mUceStatsWriter.setStoreCompleteImsRegistrationFeatureTagStats(mSubId);
                        handleImsRcsUnregistered();
                    }
                }
+48 −7
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.ims.rcs.uce.UceController;
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 @@ public class PublishControllerImpl implements PublishController {
    @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 @@ public class PublishControllerImpl implements PublishController {
    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 @@ public class PublishControllerImpl implements PublishController {

    // 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 @@ public class PublishControllerImpl implements PublishController {
        mSubId = subId;
        mContext = context;
        mUceCtrlCallback = callback;
        mUceStatsWriter = UceStatsWriter.getInstance();
        logi("create");
        initPublishController(looper);
    }
@@ -148,12 +153,13 @@ public class PublishControllerImpl implements PublishController {
    @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 @@ public class PublishControllerImpl implements PublishController {

    private void initDeviceCapabilitiesListener() {
        mDeviceCapListener = mDeviceCapListenerFactory.createDeviceCapListener(mContext, mSubId,
                mDeviceCapabilityInfo, mPublishControllerCallback);
                mDeviceCapabilityInfo, mPublishControllerCallback, mUceStatsWriter);
    }

    @Override
@@ -330,8 +336,7 @@ public class PublishControllerImpl implements PublishController {
    public void onUnpublish() {
        logd("onUnpublish");
        if (mIsDestroyedFlag) return;
        mPublishHandler.sendPublishStateChangedMessage(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
                Instant.now(), null /*pidfXml*/);
        mPublishHandler.sendUnpublishedMessage(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED);
    }

    @Override
@@ -416,6 +421,7 @@ public class PublishControllerImpl implements PublishController {
        private static final int MSG_REQUEST_NETWORK_RESPONSE = 10;
        private static final int MSG_REQUEST_CANCELED = 11;
        private static final int MSG_RESET_DEVICE_STATE = 12;
        private static final int MSG_UNPUBLISHED = 13;

        private final WeakReference<PublishControllerImpl> mPublishControllerRef;

@@ -496,6 +502,14 @@ public class PublishControllerImpl implements PublishController {
                    publishCtrl.handleResetDeviceStateMessage();
                    break;

                case MSG_UNPUBLISHED: {
                    SomeArgs args = (SomeArgs) message.obj;
                    int newPublishState = (Integer) args.arg1;
                    Instant updatedTimestamp = (Instant) args.arg2;
                    args.recycle();
                    publishCtrl.handleUnpublishedMessage(newPublishState, updatedTimestamp);
                    break;
                }
                default:
                    publishCtrl.logd("invalid message: " + message.what);
                    break;
@@ -583,6 +597,22 @@ public class PublishControllerImpl implements PublishController {
            sendMessage(message);
        }

        /**
         * Send the message to notify the publish state is changed.
         */
        public void sendUnpublishedMessage(@PublishState int publishState) {
            PublishControllerImpl publishCtrl = mPublishControllerRef.get();
            if (publishCtrl == null) return;
            if (publishCtrl.mIsDestroyedFlag) return;

            SomeArgs args = SomeArgs.obtain();
            args.arg1 = publishState;
            args.arg2 = Instant.now();
            Message message = obtainMessage();
            message.what = MSG_UNPUBLISHED;
            message.obj = args;
            sendMessage(message);
        }
        /**
         * Send the message to notify the new added callback of the latest publish state.
         */
@@ -703,6 +733,7 @@ public class PublishControllerImpl implements PublishController {
            EVENT_DESCRIPTION.put(MSG_REQUEST_NETWORK_RESPONSE, "REQUEST_NETWORK_RESPONSE");
            EVENT_DESCRIPTION.put(MSG_REQUEST_CANCELED, "REQUEST_CANCELED");
            EVENT_DESCRIPTION.put(MSG_RESET_DEVICE_STATE, "RESET_DEVICE_STATE");
            EVENT_DESCRIPTION.put(MSG_UNPUBLISHED, "MSG_UNPUBLISHED");
        }
    }

@@ -909,6 +940,9 @@ public class PublishControllerImpl implements PublishController {
            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);
@@ -988,6 +1022,13 @@ public class PublishControllerImpl implements PublishController {
        mUceCtrlCallback.resetDeviceState();
    }

    private void handleUnpublishedMessage(@PublishState int newPublishState,
            Instant updatedTimestamp) {
        if (mIsDestroyedFlag) return;
        mPublishProcessor.resetState();
        handlePublishStateChangedMessage(newPublishState, updatedTimestamp, null);
    }

    @VisibleForTesting
    public void setCapabilityType(int type) {
        mCapabilityType = type;
+41 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.annotation.NonNull;
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.RcsFeatureManager;
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 @@ public class PublishProcessor {
    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 @@ public class PublishProcessor {
        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 @@ public class PublishProcessor {
            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 @@ public class PublishProcessor {
        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 @@ public class PublishProcessor {
        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 {
@@ -445,6 +480,12 @@ public class PublishProcessor {
        return mProcessorState.isPublishingNow();
    }

    /**
     * Reset the retry count and time related publish.
     */
    public void resetState() {
        mProcessorState.resetState();
    }
    @VisibleForTesting
    public void setProcessorState(PublishProcessorState processorState) {
        mProcessorState = processorState;
+10 −0
Original line number Diff line number Diff line
@@ -348,6 +348,16 @@ public class PublishProcessorState {
        }
    }

    /**
     * Reset the retry count and related time when the publication status has
     * changed to not_published.
     */
    public void resetState() {
        synchronized (mLock) {
            mPublishThrottle.resetState();
        }
    }

    /*
     * Check if it has reached the maximum retry count.
     */
Loading