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

Commit 4f051127 authored by hhshin's avatar hhshin Committed by Automerger Merge Worker
Browse files

Create UceStatsWriter for RCS metrics am: 629a9ba9 am: 8912984c

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/net/ims/+/1906324

Change-Id: I369c938554c0c7c53efbe0dd5051ced445b343ca
parents ea42385b 8912984c
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();
    }
}