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

Commit 105ce92e authored by Kiwon Park's avatar Kiwon Park Committed by Gerrit Code Review
Browse files

Merge "DO NOT MERGE Pull metrics for phone number"

parents 7e9f7be3 c97af0d6
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);
    }
}