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

Commit 4e56c7a2 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Get NetworkRegistrationInfo on background thread

To fix ANR.

Fix: 322943652
Test: manual - on Network Selection
Test: unit test
Change-Id: I9cd7137542de007e5be2830b2ba1cbfaff8b2c05
parent e5ba5da9
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.settings.network.telephony

import android.content.Context
import android.telephony.AccessNetworkConstants
import android.telephony.NetworkRegistrationInfo
import android.telephony.TelephonyManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class NetworkSelectRepository(context: Context, subId: Int) {
    private val telephonyManager =
        context.getSystemService(TelephonyManager::class.java)!!.createForSubscriptionId(subId)

    data class NetworkRegistrationAndForbiddenInfo(
        val networkList: List<NetworkRegistrationInfo>,
        val forbiddenPlmns: List<String>,
    )

    /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
    fun launchUpdateNetworkRegistrationInfo(
        lifecycleOwner: LifecycleOwner,
        action: (NetworkRegistrationAndForbiddenInfo) -> Unit,
    ) {
        lifecycleOwner.lifecycleScope.launch {
            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                withContext(Dispatchers.Default) {
                    getNetworkRegistrationInfo()
                }?.let(action)
            }
        }
    }

    fun getNetworkRegistrationInfo(): NetworkRegistrationAndForbiddenInfo? {
        if (telephonyManager.dataState != TelephonyManager.DATA_CONNECTED) return null
        // Try to get the network registration states
        val serviceState = telephonyManager.serviceState ?: return null
        val networkList = serviceState.getNetworkRegistrationInfoListForTransportType(
            AccessNetworkConstants.TRANSPORT_TYPE_WWAN
        )
        if (networkList.isEmpty()) return null
        // Due to the aggregation of cell between carriers, it's possible to get CellIdentity
        // containing forbidden PLMN.
        // Getting current network from ServiceState is no longer a good idea.
        // Add an additional rule to avoid from showing forbidden PLMN to the user.
        return NetworkRegistrationAndForbiddenInfo(networkList, getForbiddenPlmns())
    }

    /**
     * Update forbidden PLMNs from the USIM App
     */
    private fun getForbiddenPlmns(): List<String> {
        return telephonyManager.forbiddenPlmns?.toList() ?: emptyList()
    }
}
+46 −65
Original line number Diff line number Diff line
@@ -24,12 +24,10 @@ import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -52,13 +50,11 @@ import com.android.settings.network.telephony.scan.NetworkScanRepository;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanResult;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;

import kotlin.Unit;
import kotlin.jvm.functions.Function1;

import java.util.ArrayList;
import java.util.Arrays;
@@ -101,6 +97,8 @@ public class NetworkSelectSettings extends DashboardFragment {
    private NetworkScanRepository mNetworkScanRepository;
    private boolean mUpdateScanResult = false;

    private NetworkSelectRepository mNetworkSelectRepository;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
@@ -138,6 +136,7 @@ public class NetworkSelectSettings extends DashboardFragment {
        mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor,
                mCarrierConfigChangeListener);
        mNetworkScanRepository = new NetworkScanRepository(context, mSubId);
        mNetworkSelectRepository = new NetworkSelectRepository(context, mSubId);
    }

    @Keep
@@ -202,14 +201,17 @@ public class NetworkSelectSettings extends DashboardFragment {
        mProgressHeader = setPinnedHeaderView(
                com.android.settingslib.widget.progressbar.R.layout.progress_header
        ).findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
        forceUpdateConnectedPreferenceCategory();
        mNetworkSelectRepository.launchUpdateNetworkRegistrationInfo(
                getViewLifecycleOwner(),
                (info) -> {
                    forceUpdateConnectedPreferenceCategory(info);
                    return Unit.INSTANCE;
                });
        launchNetworkScan();
    }

    private void launchNetworkScan() {
        mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), new Function1<>() {
            @Override
            public Unit invoke(@NonNull NetworkScanResult networkScanResult) {
        mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> {
            if (!mUpdateScanResult) {
                // Not update UI if not in scan mode.
                return Unit.INSTANCE;
@@ -230,7 +232,6 @@ public class NetworkSelectSettings extends DashboardFragment {
            }

            return Unit.INSTANCE;
            }
        });
    }

@@ -238,7 +239,6 @@ public class NetworkSelectSettings extends DashboardFragment {
    public void onStart() {
        super.onStart();

        updateForbiddenPlmns();
        setProgressBarVisible(true);
        mUpdateScanResult = true;
    }
@@ -477,33 +477,15 @@ public class NetworkSelectSettings extends DashboardFragment {
     * - If the device has no data, we will remove the connected network operators list from the
     * screen.
     */
    private void forceUpdateConnectedPreferenceCategory() {
        if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
            // Try to get the network registration states
            final ServiceState ss = mTelephonyManager.getServiceState();
            if (ss == null) {
                return;
            }
            final List<NetworkRegistrationInfo> networkList =
                    ss.getNetworkRegistrationInfoListForTransportType(
                            AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
            if (networkList == null || networkList.size() == 0) {
                return;
            }
            // Due to the aggregation of cell between carriers, it's possible to get CellIdentity
            // containing forbidden PLMN.
            // Getting current network from ServiceState is no longer a good idea.
            // Add an additional rule to avoid from showing forbidden PLMN to the user.
            if (mForbiddenPlmns == null) {
                updateForbiddenPlmns();
            }
            for (NetworkRegistrationInfo regInfo : networkList) {
    private void forceUpdateConnectedPreferenceCategory(
            NetworkSelectRepository.NetworkRegistrationAndForbiddenInfo info) {
        for (NetworkRegistrationInfo regInfo : info.getNetworkList()) {
            final CellIdentity cellIdentity = regInfo.getCellIdentity();
            if (cellIdentity == null) {
                continue;
            }
            final NetworkOperatorPreference pref = new NetworkOperatorPreference(
                        getPrefContext(), mForbiddenPlmns, mShow4GForLTE);
                    getPrefContext(), info.getForbiddenPlmns(), mShow4GForLTE);
            pref.updateCell(null, cellIdentity);
            if (pref.isForbiddenNetwork()) {
                continue;
@@ -517,7 +499,6 @@ public class NetworkSelectSettings extends DashboardFragment {
            break;
        }
    }
    }

    /**
     * Clear all of the preference summary
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.settings.network.telephony

import android.content.Context
import android.telephony.AccessNetworkConstants
import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.telephony.scan.NetworkScanRepositoryTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub

@RunWith(AndroidJUnit4::class)
class NetworkSelectRepositoryTest {

    private val mockServiceState = mock<ServiceState> {
        on {
            getNetworkRegistrationInfoListForTransportType(
                AccessNetworkConstants.TRANSPORT_TYPE_WWAN
            )
        } doReturn NetworkRegistrationInfos
    }

    private val mockTelephonyManager = mock<TelephonyManager> {
        on { createForSubscriptionId(SUB_ID) } doReturn mock
        on { dataState } doReturn TelephonyManager.DATA_CONNECTED
        on { serviceState } doReturn mockServiceState
    }

    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
        on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
    }

    private val repository = NetworkSelectRepository(context, SUB_ID)

    @Test
    fun getNetworkRegistrationInfo_notConnected_returnNull() {
        mockTelephonyManager.stub {
            on { dataState } doReturn TelephonyManager.DATA_DISCONNECTED
        }

        val info = repository.getNetworkRegistrationInfo()

        assertThat(info).isNull()
    }

    @Test
    fun getNetworkRegistrationInfo_nullServiceState_returnNull() {
        mockTelephonyManager.stub {
            on { serviceState } doReturn null
        }

        val info = repository.getNetworkRegistrationInfo()

        assertThat(info).isNull()
    }

    @Test
    fun getNetworkRegistrationInfo_emptyNetworkList_returnNull() {
        mockServiceState.stub {
            on {
                getNetworkRegistrationInfoListForTransportType(
                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN
                )
            } doReturn emptyList()
        }

        val info = repository.getNetworkRegistrationInfo()

        assertThat(info).isNull()
    }

    @Test
    fun getNetworkRegistrationInfo_hasNetworkList_returnInfo() {
        mockServiceState.stub {
            on {
                getNetworkRegistrationInfoListForTransportType(
                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN
                )
            } doReturn NetworkRegistrationInfos
        }
        mockTelephonyManager.stub {
            on { forbiddenPlmns } doReturn arrayOf(FORBIDDEN_PLMN)
        }

        val info = repository.getNetworkRegistrationInfo()

        assertThat(info).isEqualTo(
            NetworkSelectRepository.NetworkRegistrationAndForbiddenInfo(
                networkList = NetworkRegistrationInfos,
                forbiddenPlmns = listOf(FORBIDDEN_PLMN),
            )
        )
    }

    private companion object {
        const val SUB_ID = 1
        val NetworkRegistrationInfos = listOf(NetworkRegistrationInfo.Builder().build())
        const val FORBIDDEN_PLMN = "Forbidden PLMN"
    }
}