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

Commit 99785e2c authored by Kiwon Park's avatar Kiwon Park Committed by Gerrit Code Review
Browse files

Merge "Pull metrics for phone number"

parents bfc0e33b 531c8cc3
Loading
Loading
Loading
Loading
+88 −8
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.internal.telephony.metrics;

import static android.telephony.PhoneNumberUtils.areSamePhoneNumber;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_IMS;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_UICC;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
@@ -33,6 +37,7 @@ import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_
import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION;
import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS;
import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS;
@@ -50,11 +55,14 @@ import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSIO
import android.annotation.Nullable;
import android.app.StatsManager;
import android.content.Context;
import android.telephony.SubscriptionInfo;
import android.text.TextUtils;
import android.util.StatsEvent;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
@@ -86,6 +94,7 @@ import com.android.telephony.Rlog;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -127,14 +136,24 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                    .setCoolDownMillis(MIN_COOLDOWN_MILLIS)
                    .build();

    private PersistAtomsStorage mStorage;
    private final PersistAtomsStorage mStorage;
    private final TelephonyStatsLogHelper mTelephonyStatsLog;
    private final StatsManager mStatsManager;
    private final AirplaneModeStats mAirplaneModeStats;
    private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet();
    private static final Random sRandom = new Random();

    public MetricsCollector(Context context) {
        mStorage = new PersistAtomsStorage(context);
        this(context, new PersistAtomsStorage(context), new TelephonyStatsLogHelper());
    }

    /** Allows dependency injection. Used during unit tests. */
    @VisibleForTesting
    public MetricsCollector(Context context,
                            PersistAtomsStorage storage,
                            TelephonyStatsLogHelper telephonyStatsLog) {
        mStorage = storage;
        mTelephonyStatsLog = telephonyStatsLog;
        mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
        if (mStatsManager != null) {
            registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY);
@@ -163,6 +182,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
            registerAtom(UCE_EVENT_STATS, POLICY_PULL_DAILY);
            registerAtom(PRESENCE_NOTIFY_EVENT, POLICY_PULL_DAILY);
            registerAtom(GBA_EVENT, POLICY_PULL_DAILY);
            registerAtom(PER_SIM_STATUS, null);

            Rlog.d(TAG, "registered");
        } else {
@@ -172,12 +192,6 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
        mAirplaneModeStats = new AirplaneModeStats(context);
    }

    /** Replaces the {@link PersistAtomsStorage} backing the puller. Used during unit tests. */
    @VisibleForTesting
    public void setPersistAtomsStorage(PersistAtomsStorage storage) {
        mStorage = storage;
    }

    /**
     * {@inheritDoc}
     *
@@ -240,6 +254,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                return pullPresenceNotifyEvent(data);
            case GBA_EVENT:
                return pullGbaEvent(data);
            case PER_SIM_STATUS:
                return pullPerSimStatus(data);
            default:
                Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                return StatsManager.PULL_SKIP;
@@ -639,6 +655,70 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
        }
    }

    private int pullPerSimStatus(List<StatsEvent> data) {
        SubscriptionController subscriptionController = SubscriptionController.getInstance();
        if (subscriptionController == null) {
            return StatsManager.PULL_SKIP;
        }
        for (Phone phone : getPhonesIfAny()) {
            int subId = phone.getSubId();
            String countryIso =
                    Optional.ofNullable(subscriptionController.getSubscriptionInfo(subId))
                            .map(SubscriptionInfo::getCountryIso)
                            .orElse("");
            // numbersFromAllSources[]- Phone numbers from each sources
            // numberIds[] - Array of integer ids representing phone numbers.
            // If number is empty then id will be 0. Two same numbers will have same id, and
            // different numbers will have different ids.
            // For example, [1, 0, 1] means that uicc and ims numbers are the same while carrier
            // number is empty and [1, 2, 3] means all numbers are different.
            // index 0 - PHONE_NUMBER_SOURCE_UICC
            // index 1 - PHONE_NUMBER_SOURCE_CARRIER
            // index 2 - PHONE_NUMBER_SOURCE_IMS
            String[] numbersFromAllSources =
                    new String[] {
                        subscriptionController.getPhoneNumber(
                                subId, PHONE_NUMBER_SOURCE_UICC, null, null), // 0
                        subscriptionController.getPhoneNumber(
                                subId, PHONE_NUMBER_SOURCE_CARRIER, null, null), // 1
                        subscriptionController.getPhoneNumber(
                                subId, PHONE_NUMBER_SOURCE_IMS, null, null), // 2
                    };
            int[] numberIds = new int[numbersFromAllSources.length]; // default value 0
            for (int i = 0, idForNextUniqueNumber = 1; i < numberIds.length; i++) {
                if (TextUtils.isEmpty(numbersFromAllSources[i])) {
                    // keep id 0 if number not available
                    continue;
                }
                // the number is available:
                // try to find the same number from other sources and reuse the id
                for (int j = 0; j < i; j++) {
                    if (!TextUtils.isEmpty(numbersFromAllSources[j])
                            && areSamePhoneNumber(
                                    numbersFromAllSources[i],
                                    numbersFromAllSources[j],
                                    countryIso)) {
                        numberIds[i] = numberIds[j];
                    }
                }
                // didn't find same number (otherwise should not be id 0), assign a new id
                if (numberIds[i] == 0) {
                    numberIds[i] = idForNextUniqueNumber;
                    idForNextUniqueNumber++;
                }
            }
            StatsEvent statsEvent = mTelephonyStatsLog.buildStatsEvent(
                    PER_SIM_STATUS,
                    phone.getPhoneId(), // simSlotIndex
                    phone.getCarrierId(), // carrierId
                    numberIds[0], // phoneNumberSourceUicc
                    numberIds[1], // phoneNumberSourceCarrier
                    numberIds[2]); // phoneNumberSourceIms
            data.add(statsEvent);
        }
        return StatsManager.PULL_SUCCESS;
    }

    /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
    private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
        mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.internal.telephony.metrics;

import android.util.StatsEvent;

import com.android.internal.telephony.TelephonyStatsLog;

/**
 * Wrapper of generated TelephonyStatsLog class. Can be mocked during unit test.
 *
 * <p>Methods here have the same name/arguments/return as their counterparts in TelephonyStatsLog
 * and simply call their counterparts. Do not put any involved logic here as they cannot be
 * covered by unit test.
 */
public class TelephonyStatsLogHelper {
    /** Wraps TelephonyStatsLog.buildStatsEvent(...) for atom PER_SIM_STATUS. */
    public StatsEvent buildStatsEvent(
            int atomId,
            int simSlotIndex,
            int carrierId,
            int phoneNumberSourceUicc,
            int phoneNumberSourceCarrier,
            int phoneNumberSourceIms) {
        return TelephonyStatsLog.buildStatsEvent(
                atomId,
                simSlotIndex,
                carrierId,
                phoneNumberSourceUicc,
                phoneNumberSourceCarrier,
                phoneNumberSourceIms);
    }
}
+73 −2
Original line number Diff line number Diff line
@@ -16,8 +16,13 @@

package com.android.internal.telephony.metrics;

import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_IMS;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_UICC;

import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -34,12 +39,14 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.app.StatsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.StatsEvent;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
@@ -84,6 +91,7 @@ public class MetricsCollectorTest extends TelephonyTest {

    // Mocked classes
    private Phone mSecondPhone;
    private TelephonyStatsLogHelper mTelephonyStatsLog;
    private UiccSlot mPhysicalSlot;
    private UiccSlot mEsimSlot;
    private UiccCard mActiveCard;
@@ -96,13 +104,14 @@ public class MetricsCollectorTest extends TelephonyTest {
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        mSecondPhone = mock(Phone.class);
        mTelephonyStatsLog = mock(TelephonyStatsLogHelper.class);
        mPhysicalSlot = mock(UiccSlot.class);
        mEsimSlot = mock(UiccSlot.class);
        mActiveCard = mock(UiccCard.class);
        mActivePort = mock(UiccPort.class);
        mServiceStateStats = mock(ServiceStateStats.class);
        mMetricsCollector = new MetricsCollector(mContext);
        mMetricsCollector.setPersistAtomsStorage(mPersistAtomsStorage);
        mMetricsCollector =
                new MetricsCollector(mContext, mPersistAtomsStorage, mTelephonyStatsLog);
        doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
        doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
    }
@@ -412,4 +421,66 @@ public class MetricsCollectorTest extends TelephonyTest {
        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
        // TODO(b/153196254): verify atom contents
    }

    @Test
    @SmallTest
    public void onPullAtom_perSimStatus() throws Exception {
        // Make PhoneFactory.getPhones() return an array of two
        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mSecondPhone});
        // phone 0 setup
        doReturn(0).when(mPhone).getPhoneId();
        doReturn(1).when(mPhone).getSubId();
        doReturn(100).when(mPhone).getCarrierId();
        doReturn("6506953210")
                .when(mSubscriptionController)
                .getPhoneNumber(1, PHONE_NUMBER_SOURCE_UICC, null, null);
        doReturn("")
                .when(mSubscriptionController)
                .getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER, null, null);
        doReturn("+16506953210")
                .when(mSubscriptionController)
                .getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS, null, null);
        SubscriptionInfo subscriptionInfo1 = mock(SubscriptionInfo.class);
        doReturn("us").when(subscriptionInfo1).getCountryIso();
        doReturn(subscriptionInfo1).when(mSubscriptionController).getSubscriptionInfo(1);
        // phone 1 setup
        doReturn(1).when(mSecondPhone).getPhoneId();
        doReturn(2).when(mSecondPhone).getSubId();
        doReturn(101).when(mSecondPhone).getCarrierId();
        doReturn("0123")
                .when(mSubscriptionController)
                .getPhoneNumber(2, PHONE_NUMBER_SOURCE_UICC, null, null);
        doReturn("16506950123")
                .when(mSubscriptionController)
                .getPhoneNumber(2, PHONE_NUMBER_SOURCE_CARRIER, null, null);
        doReturn("+16506950123")
                .when(mSubscriptionController)
                .getPhoneNumber(2, PHONE_NUMBER_SOURCE_IMS, null, null);
        SubscriptionInfo subscriptionInfo2 = mock(SubscriptionInfo.class);
        doReturn("us").when(subscriptionInfo2).getCountryIso();
        doReturn(subscriptionInfo2).when(mSubscriptionController).getSubscriptionInfo(2);
        List<StatsEvent> actualAtoms = new ArrayList<>();

        int result = mMetricsCollector.onPullAtom(PER_SIM_STATUS, actualAtoms);

        verify(mTelephonyStatsLog).buildStatsEvent(
                PER_SIM_STATUS, 0, 100, 1, 0, 1);
        verify(mTelephonyStatsLog).buildStatsEvent(
                PER_SIM_STATUS, 1, 101, 1, 2, 2);
        assertThat(actualAtoms).hasSize(2);
        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
    }

    @Test
    @SmallTest
    public void onPullAtom_perSimStatus_noSubscriptionController_skip() throws Exception {
        // Make SubscriptionController.getInstance() return null
        replaceInstance(SubscriptionController.class, "sInstance", null, null);
        List<StatsEvent> actualAtoms = new ArrayList<>();

        int result = mMetricsCollector.onPullAtom(PER_SIM_STATUS, actualAtoms);

        assertThat(actualAtoms).isEmpty();
        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
    }
}