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

Commit 4d97a2e9 authored by Kiwon Park's avatar Kiwon Park Committed by Gerrit Code Review
Browse files

Merge "Collect metrics for WFC settings."

parents 318c22f9 5a168df5
Loading
Loading
Loading
Loading
+23 −68
Original line number Diff line number Diff line
@@ -16,10 +16,6 @@

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;
@@ -55,14 +51,11 @@ 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;
@@ -94,7 +87,6 @@ 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;
@@ -137,23 +129,20 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                    .build();

    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) {
        this(context, new PersistAtomsStorage(context), new TelephonyStatsLogHelper());
        this(context, new PersistAtomsStorage(context));
    }

    /** Allows dependency injection. Used during unit tests. */
    @VisibleForTesting
    public MetricsCollector(Context context,
                            PersistAtomsStorage storage,
                            TelephonyStatsLogHelper telephonyStatsLog) {
                            PersistAtomsStorage storage) {
        mStorage = storage;
        mTelephonyStatsLog = telephonyStatsLog;
        mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
        if (mStatsManager != null) {
            registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY);
@@ -656,67 +645,33 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
    }

    private int pullPerSimStatus(List<StatsEvent> data) {
        SubscriptionController subscriptionController = SubscriptionController.getInstance();
        if (subscriptionController == null) {
            return StatsManager.PULL_SKIP;
        }
        int result = 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
            PerSimStatus perSimStatus = PerSimStatus.getCurrentState(phone);
            if (perSimStatus == null) {
                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(
            StatsEvent statsEvent = TelephonyStatsLog.buildStatsEvent(
                    PER_SIM_STATUS,
                    phone.getPhoneId(), // simSlotIndex
                    phone.getCarrierId(), // carrierId
                    numberIds[0], // phoneNumberSourceUicc
                    numberIds[1], // phoneNumberSourceCarrier
                    numberIds[2]); // phoneNumberSourceIms
                    perSimStatus.carrierId, // carrierId
                    perSimStatus.phoneNumberSourceUicc, // phoneNumberSourceUicc
                    perSimStatus.phoneNumberSourceCarrier, // phoneNumberSourceCarrier
                    perSimStatus.phoneNumberSourceIms, // phoneNumberSourceIms
                    perSimStatus.advancedCallingSettingEnabled, // volteEnabled
                    perSimStatus.voWiFiSettingEnabled, // wfcEnabled
                    perSimStatus.voWiFiModeSetting, // wfcMode
                    perSimStatus.voWiFiRoamingModeSetting, // wfcRoamingMode
                    perSimStatus.vtSettingEnabled, // videoCallingEnabled
                    perSimStatus.dataRoamingEnabled, // dataRoamingEnabled
                    perSimStatus.preferredNetworkType, // allowedNetworksByUser
                    perSimStatus.disabled2g, // is2gDisabled
                    false, // TODO(b/215758472): isPin1Enabled
                    0); // TODO(b/215758472): simVoltageClass
            data.add(statsEvent);
            result = StatsManager.PULL_SUCCESS;
        }
        return StatsManager.PULL_SUCCESS;
        return result;
    }

    /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
+207 −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 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 com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__CELLULAR_PREFERRED;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__WIFI_ONLY;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__WIFI_PREFERRED;

import android.annotation.Nullable;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.text.TextUtils;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.SubscriptionController;

import java.util.Optional;

/** Stores the per SIM status. */
public class PerSimStatus {
    private static final long BITMASK_2G =
            TelephonyManager.NETWORK_TYPE_BITMASK_GSM
                    | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
                    | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
                    | TelephonyManager.NETWORK_TYPE_BITMASK_CDMA
                    | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;

    public final int carrierId;
    public final int phoneNumberSourceUicc;
    public final int phoneNumberSourceCarrier;
    public final int phoneNumberSourceIms;
    public final boolean advancedCallingSettingEnabled;
    public final boolean voWiFiSettingEnabled;
    public final int voWiFiModeSetting;
    public final int voWiFiRoamingModeSetting;
    public final boolean vtSettingEnabled;
    public final boolean dataRoamingEnabled;
    public final long preferredNetworkType;
    public final boolean disabled2g;

    /** Returns the current sim status of the given {@link Phone}. */
    @Nullable
    public static PerSimStatus getCurrentState(Phone phone) {
        int[] numberIds = getNumberIds(phone);
        if (numberIds == null) return null;
        ImsMmTelManager imsMmTelManager = getImsMmTelManager(phone);
        return new PerSimStatus(
                phone.getCarrierId(),
                numberIds[0],
                numberIds[1],
                numberIds[2],
                imsMmTelManager == null ? false : imsMmTelManager.isAdvancedCallingSettingEnabled(),
                imsMmTelManager == null ? false : imsMmTelManager.isVoWiFiSettingEnabled(),
                imsMmTelManager == null
                        ? PER_SIM_STATUS__WFC_MODE__UNKNOWN
                        : wifiCallingModeToProtoEnum(imsMmTelManager.getVoWiFiModeSetting()),
                imsMmTelManager == null
                        ? PER_SIM_STATUS__WFC_MODE__UNKNOWN
                        : wifiCallingModeToProtoEnum(imsMmTelManager.getVoWiFiRoamingModeSetting()),
                imsMmTelManager == null ? false : imsMmTelManager.isVtSettingEnabled(),
                phone.getDataRoamingEnabled(),
                phone.getAllowedNetworkTypes(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER),
                is2gDisabled(phone));
    }

    private PerSimStatus(
            int carrierId,
            int phoneNumberSourceUicc,
            int phoneNumberSourceCarrier,
            int phoneNumberSourceIms,
            boolean advancedCallingSettingEnabled,
            boolean voWiFiSettingEnabled,
            int voWiFiModeSetting,
            int voWiFiRoamingModeSetting,
            boolean vtSettingEnabled,
            boolean dataRoamingEnabled,
            long preferredNetworkType,
            boolean disabled2g) {
        this.carrierId = carrierId;
        this.phoneNumberSourceUicc = phoneNumberSourceUicc;
        this.phoneNumberSourceCarrier = phoneNumberSourceCarrier;
        this.phoneNumberSourceIms = phoneNumberSourceIms;
        this.advancedCallingSettingEnabled = advancedCallingSettingEnabled;
        this.voWiFiSettingEnabled = voWiFiSettingEnabled;
        this.voWiFiModeSetting = voWiFiModeSetting;
        this.voWiFiRoamingModeSetting = voWiFiRoamingModeSetting;
        this.vtSettingEnabled = vtSettingEnabled;
        this.dataRoamingEnabled = dataRoamingEnabled;
        this.preferredNetworkType = preferredNetworkType;
        this.disabled2g = disabled2g;
    }

    @Nullable
    private static ImsMmTelManager getImsMmTelManager(Phone phone) {
        ImsManager imsManager = phone.getContext().getSystemService(ImsManager.class);
        if (imsManager == null) {
            return null;
        }
        try {
            return imsManager.getImsMmTelManager(phone.getSubId());
        } catch (IllegalArgumentException e) {
            return null; // Invalid subId
        }
    }

    /**
     * Returns an 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.
     *
     * <ul>
     *   <li>Index 0: id associated with {@code PHONE_NUMBER_SOURCE_UICC}.</li>
     *   <li>Index 1: id associated with {@code PHONE_NUMBER_SOURCE_CARRIER}.</li>
     *   <li>Index 2: id associated with {@code PHONE_NUMBER_SOURCE_IMS}.</li>
     * </ul>
     */
    @Nullable
    private static int[] getNumberIds(Phone phone) {
        SubscriptionController subscriptionController = SubscriptionController.getInstance();
        if (subscriptionController == null) {
            return null;
        }
        int subId = phone.getSubId();
        String countryIso =
                Optional.ofNullable(subscriptionController.getSubscriptionInfo(subId))
                        .map(SubscriptionInfo::getCountryIso)
                        .orElse("");
        // numbersFromAllSources[] - phone numbers from each sources:
        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++;
            }
        }
        return numberIds;
    }

    /**
     * Returns {@code true} if 2G cellular network is disabled (Allow 2G toggle in the settings).
     */
    private static boolean is2gDisabled(Phone phone) {
        return (phone.getAllowedNetworkTypes(
                                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G)
                        & BITMASK_2G)
                == 0;
    }

    /** Converts {@link ImsMmTelManager.WifiCallingMode} to the value of PerSimStatus WfcMode. */
    private static int wifiCallingModeToProtoEnum(@ImsMmTelManager.WiFiCallingMode int mode) {
        switch (mode) {
            case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
                return PER_SIM_STATUS__WFC_MODE__WIFI_ONLY;
            case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
                return PER_SIM_STATUS__WFC_MODE__CELLULAR_PREFERRED;
            case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
                return PER_SIM_STATUS__WFC_MODE__WIFI_PREFERRED;
            default:
                return PER_SIM_STATUS__WFC_MODE__UNKNOWN;
        }
    }
}
+0 −47
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);
    }
}
+1 −72
Original line number Diff line number Diff line
@@ -16,13 +16,8 @@

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;
@@ -39,14 +34,12 @@ 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;
@@ -91,7 +84,6 @@ public class MetricsCollectorTest extends TelephonyTest {

    // Mocked classes
    private Phone mSecondPhone;
    private TelephonyStatsLogHelper mTelephonyStatsLog;
    private UiccSlot mPhysicalSlot;
    private UiccSlot mEsimSlot;
    private UiccCard mActiveCard;
@@ -104,14 +96,13 @@ 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, mPersistAtomsStorage, mTelephonyStatsLog);
                new MetricsCollector(mContext, mPersistAtomsStorage);
        doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
        doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
    }
@@ -421,66 +412,4 @@ 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);
    }
}
+270 −0

File added.

Preview size limit exceeded, changes collapsed.