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

Commit 629a9ba9 authored by hhshin's avatar hhshin Committed by Jinyoung Jeong
Browse files

Create UceStatsWriter for RCS metrics

Define interface for metrics data related Uce store

Bug: http://b/174871215
Test: build atest UceStatsWriterTest
Change-Id: I4ae53a927cf308d58b25fec0eca5efa16f1aca74
Merged-In: I4ae53a927cf308d58b25fec0eca5efa16f1aca74
parent b37be333
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;
    }
}
+292 −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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import android.telephony.ims.RcsContactPresenceTuple;
import android.telephony.ims.RcsContactUceCapability;

import com.android.ims.rcs.uce.UceStatsWriter;
import com.android.ims.rcs.uce.UceStatsWriter.UceStatsCallback;

import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;

@RunWith(AndroidJUnit4.class)
public class UceStatsWriterTest {
    private int mSubId = 3;
    private long mTaskId = 5;
    private int mRegistrationTech = 3;
    private int mType = 2;
    private boolean mSuccessful = true;
    private int mCommandCode = 4;
    private int mNetworkResponse = 200;
    private String mReason = "noresource";

    private Callback mCallback;
    private TestableUceStatsWriter mWrite;

    class Callback implements UceStatsCallback {
        int subId;
        List<String> featureTagList;
        List<String> serviceIdList;
        List<String> versions;
        int registrationTech;
        int type;
        boolean successful;
        int commandCode;
        int networkResponse;
        long taskId;
        List<RcsContactUceCapability> updatedCapList;
        String reason;

        Callback() {
        }

        public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
            int registrationTech) {
            this.subId = subId;
            this.featureTagList = featureTagList;
            this.registrationTech = registrationTech;
        }

        public void onStoreCompleteImsRegistrationFeatureTagStats(int subId) {}

        public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
            List<String> serviceIdVersionList, int registrationTech) {
            this.subId = subId;
            this.serviceIdList = serviceIdList;
            this.versions = serviceIdVersionList;
            this.registrationTech = registrationTech;
        }

        public void onSubscribeResponse(int subId, long taskId, int networkResponse) {
            this.subId = subId;
            this.taskId = taskId;
            this.successful = true;
            this.commandCode = 0;
            this.networkResponse = networkResponse;
        }

        public void onUceEvent(int subId, int type, boolean successful, int commandCode,
            int networkResponse) {
            this.subId = subId;
            this.type = type;
            this.successful = successful;
            this.commandCode = commandCode;
            this.networkResponse = networkResponse;
        }

        public void onSubscribeTerminated(int subId, long taskId, String reason) {
            this.subId = subId;
            this.taskId = taskId;
            this.reason = reason;
        }

        public void onPresenceNotifyEvent(int subId, long taskId,
            List<RcsContactUceCapability> updatedCapList) {
            this.subId = subId;
            this.taskId = taskId;
            this.updatedCapList = updatedCapList;
        }

        public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
            this.subId = subId;
        }
    }

    private class TestableUceStatsWriter extends UceStatsWriter {
        public TestableUceStatsWriter(UceStatsCallback callback) {
            super(callback);
        }
    }

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mCallback = new Callback();
        mWrite = new TestableUceStatsWriter(mCallback);
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    @SmallTest
    public void setImsRegistrationFeatureTagStats() throws Exception {
        List<String> featureTags = new ArrayList<>();
        featureTags.add("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"");
        featureTags.add("+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"");
        featureTags.add("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"");
        mWrite.setImsRegistrationFeatureTagStats(mSubId, featureTags, mRegistrationTech);
        assertEquals(mSubId, mCallback.subId);
        for (int index = 0; index < featureTags.size(); index++) {
            assertEquals(featureTags.get(index), mCallback.featureTagList.get(index));
        }
        assertEquals(mRegistrationTech, mCallback.registrationTech);
    }

    @Test
    @SmallTest
    public void setImsRegistrationServiceDescStats() throws Exception {
        List<RcsContactPresenceTuple> tupleList = new ArrayList<>();
        tupleList.add(getContactChatTuple());
        tupleList.add(getContactFtTuple());
        tupleList.add(getContactUnknown1Tuple());
        tupleList.add(getContactUnknown2Tuple());
        mWrite.setImsRegistrationServiceDescStats(mSubId, tupleList, mRegistrationTech);
        assertEquals(mSubId, mCallback.subId);
        for (int index = 0; index < tupleList.size(); index++) {
            assertEquals(tupleList.get(index).getServiceId(), mCallback.serviceIdList.get(index));
            assertEquals(tupleList.get(index).getServiceVersion(), mCallback.versions.get(index));
        }
        assertEquals(mRegistrationTech, mCallback.registrationTech);
    }

    @Test
    @SmallTest
    public void setSubscribeEvent() throws Exception {
        mWrite.setSubscribeResponse(mSubId, mTaskId, mNetworkResponse);
        assertEquals(mSubId, mCallback.subId);
        assertEquals(mTaskId, mCallback.taskId);
        assertTrue(mCallback.successful);
        assertEquals(0, mCallback.commandCode);
        assertEquals(mNetworkResponse, mCallback.networkResponse);
    }

    @Test
    @SmallTest
    public void setSubscribeTerminated() throws Exception {
        mWrite.setSubscribeResponse(mSubId, mTaskId, mNetworkResponse);
        mWrite.setSubscribeTerminated(mSubId, mTaskId, mReason);
        assertEquals(mSubId, mCallback.subId);
        assertEquals(mTaskId, mCallback.taskId);
        assertEquals(mReason, mCallback.reason);
    }

    @Test
    @SmallTest
    public void setUceEvent() throws Exception {
        mWrite.setUceEvent(mSubId, mType, mSuccessful, mCommandCode, mNetworkResponse);
        assertEquals(mSubId, mCallback.subId);
        assertEquals(mType, mCallback.type);
        assertEquals(mSuccessful, mCallback.successful);
        assertEquals(mCommandCode, mCallback.commandCode);
        assertEquals(mNetworkResponse, mCallback.networkResponse);
    }

    @Test
    @SmallTest
    public void setPresenceNotifyEvent() throws Exception {
        List<RcsContactUceCapability> updatedCapList = new ArrayList<>();
        RcsContactUceCapability.PresenceBuilder builder =
            new RcsContactUceCapability.PresenceBuilder(null, 0, 2);
        builder.addCapabilityTuple(getContactChatTuple());
        builder.addCapabilityTuple(getContactCallComposer2Tuple());
        builder.addCapabilityTuple(getContactUnknown1Tuple());
        updatedCapList.add(builder.build());

        mWrite.setPresenceNotifyEvent(mSubId, mTaskId, updatedCapList);
        assertEquals(mSubId, mCallback.subId);
        assertEquals(mTaskId, mCallback.taskId);
        assertEquals(updatedCapList.size(), mCallback.updatedCapList.size());
        for (int index = 0; index < updatedCapList.size(); index++) {
            RcsContactUceCapability input = updatedCapList.get(index);
            RcsContactUceCapability output = mCallback.updatedCapList.get(index);
            assertEquals(input.getCapabilityTuples().size(), output.getCapabilityTuples().size());
        }
    }

    @Test
    @SmallTest
    public void setPresenceNotifyEvent_withCallComposer2Caps() throws Exception {
        RcsContactPresenceTuple tuple = getContactCallComposer2Tuple();
        List<RcsContactUceCapability> updatedCapList = new ArrayList<>();
        RcsContactUceCapability.PresenceBuilder builder =
            new RcsContactUceCapability.PresenceBuilder(null, 0, 2);
        builder.addCapabilityTuple(getContactCallComposer2Tuple());
        updatedCapList.add(builder.build());

        mWrite.setPresenceNotifyEvent(mSubId, mTaskId, updatedCapList);
        assertEquals(mSubId, mCallback.subId);
        assertEquals(mTaskId, mCallback.taskId);
        assertEquals(updatedCapList.size(), mCallback.updatedCapList.size());
    }

    @Test
    @SmallTest
    public void setUnPublish() throws Exception {
        mWrite.setUnPublish(mSubId);
        assertEquals(mSubId, mCallback.subId);
    }

    private RcsContactPresenceTuple getContactChatTuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open", RcsContactPresenceTuple.SERVICE_ID_CHAT_V1,
                "1.0");
        return builder.build();
    }

    private RcsContactPresenceTuple getContactMmtelTuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open", RcsContactPresenceTuple.SERVICE_ID_MMTEL,
                "1.0");
        return builder.build();
    }

    private RcsContactPresenceTuple getContactFtTuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open", RcsContactPresenceTuple.SERVICE_ID_FT,
                "1.0");
        return builder.build();
    }

    private RcsContactPresenceTuple getContactCallComposer2Tuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open",
                RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER,
                "2.0");
        return builder.build();
    }

    private RcsContactPresenceTuple getContactUnknown1Tuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open", "Unknown1",
                "8.0");
        return builder.build();
    }

    private RcsContactPresenceTuple getContactUnknown2Tuple() {
        RcsContactPresenceTuple.Builder builder =
            new RcsContactPresenceTuple.Builder("open", "Unknown2",
                "9.0");
        return builder.build();
    }
}