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

Commit c97af0d6 authored by Meng Wang's avatar Meng Wang Committed by Kiwon Park
Browse files

DO NOT MERGE Pull metrics for phone number

Bug: 193539271
Test: atest MetricsCollectorTest
Change-Id: I110c0172c4af5ff29dafafba07ae92e6b288883f
(cherry picked from commit 449238e2)
Merged-In: I110c0172c4af5ff29dafafba07ae92e6b288883f
parent d6aaa027
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -4586,9 +4586,12 @@ public class SubscriptionController extends ISub.Stub {
        }
    }

    // Internal helper method for implementing getPhoneNumber() API.
    /**
     * Implements getPhoneNumber() APIs, w/o permission check.
     * Can be used by other phone internal components.
     */
    @Nullable
    private String getPhoneNumber(int subId, int source) {
    public String getPhoneNumber(int subId, int source) {
        if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_UICC) {
            Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
            return phone != null ? phone.getLine1Number() : null;
+77 −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,59 @@ 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("");
            // number[]- hone numbers from each sources:
            // numberState[] - int representation for each number source used in the atom
            // index 0 - PHONE_NUMBER_SOURCE_UICC
            // index 1 - PHONE_NUMBER_SOURCE_CARRIER
            // index 2 - PHONE_NUMBER_SOURCE_IMS
            String[] number = new String[] {
                subscriptionController.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_UICC),  // 0
                subscriptionController.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_CARRIER),  // 1
                subscriptionController.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_IMS),  // 2
            };
            int[] numberState = new int[number.length];  // default value 0
            for (int i = 0, stateForNextUniqueNumber = 1; i < numberState.length; i++) {
                if (TextUtils.isEmpty(number[i])) {
                    // keep state 0 if number not available
                    continue;
                }
                // the number is available:
                // try to find the same number from other sources and reuse the state
                for (int j = 0; j < i; j++) {
                    if (!TextUtils.isEmpty(number[j])
                            && areSamePhoneNumber(number[i], number[j], countryIso)) {
                        numberState[i] = numberState[j];
                    }
                }
                // didn't find same number (otherwise should not be state 0), assign a new state
                if (numberState[i] == 0) {
                    numberState[i] = stateForNextUniqueNumber;
                    stateForNextUniqueNumber++;
                }
            }
            StatsEvent statsEvent = mTelephonyStatsLog.buildStatsEvent(
                    PER_SIM_STATUS,
                    phone.getPhoneId(), // simSlotIndex
                    phone.getCarrierId(), // carrierId
                    numberState[0], // phoneNumberSourceUicc
                    numberState[1], // phoneNumberSourceCarrier
                    numberState[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) 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.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;
@@ -28,17 +33,20 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
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;
@@ -83,6 +91,7 @@ public class MetricsCollectorTest extends TelephonyTest {
    // b/153195691: we cannot verify the contents of StatsEvent as its getters are marked with @hide

    @Mock private Phone mSecondPhone;
    @Mock private TelephonyStatsLogHelper mTelephonyStatsLog;
    @Mock private UiccSlot mPhysicalSlot;
    @Mock private UiccSlot mEsimSlot;
    @Mock private UiccCard mActiveCard;
@@ -95,8 +104,8 @@ public class MetricsCollectorTest extends TelephonyTest {
    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        mMetricsCollector = new MetricsCollector(mContext);
        mMetricsCollector.setPersistAtomsStorage(mPersistAtomsStorage);
        mMetricsCollector =
                new MetricsCollector(mContext, mPersistAtomsStorage, mTelephonyStatsLog);
        doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
        doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
    }
@@ -405,4 +414,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);
        doReturn("")
                .when(mSubscriptionController)
                .getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER);
        doReturn("+16506953210")
                .when(mSubscriptionController)
                .getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS);
        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);
        doReturn("16506950123")
                .when(mSubscriptionController)
                .getPhoneNumber(2, PHONE_NUMBER_SOURCE_CARRIER);
        doReturn("+16506950123")
                .when(mSubscriptionController)
                .getPhoneNumber(2, PHONE_NUMBER_SOURCE_IMS);
        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);
    }
}