Loading src/com/android/settings/network/telephony/NetworkSelectRepository.kt 0 → 100644 +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() } } src/com/android/settings/network/telephony/NetworkSelectSettings.java +46 −65 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -138,6 +136,7 @@ public class NetworkSelectSettings extends DashboardFragment { mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor, mCarrierConfigChangeListener); mNetworkScanRepository = new NetworkScanRepository(context, mSubId); mNetworkSelectRepository = new NetworkSelectRepository(context, mSubId); } @Keep Loading Loading @@ -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; Loading @@ -230,7 +232,6 @@ public class NetworkSelectSettings extends DashboardFragment { } return Unit.INSTANCE; } }); } Loading @@ -238,7 +239,6 @@ public class NetworkSelectSettings extends DashboardFragment { public void onStart() { super.onStart(); updateForbiddenPlmns(); setProgressBarVisible(true); mUpdateScanResult = true; } Loading Loading @@ -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; Loading @@ -517,7 +499,6 @@ public class NetworkSelectSettings extends DashboardFragment { break; } } } /** * Clear all of the preference summary Loading tests/spa_unit/src/com/android/settings/network/telephony/NetworkSelectRepositoryTest.kt 0 → 100644 +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" } } Loading
src/com/android/settings/network/telephony/NetworkSelectRepository.kt 0 → 100644 +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() } }
src/com/android/settings/network/telephony/NetworkSelectSettings.java +46 −65 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -138,6 +136,7 @@ public class NetworkSelectSettings extends DashboardFragment { mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor, mCarrierConfigChangeListener); mNetworkScanRepository = new NetworkScanRepository(context, mSubId); mNetworkSelectRepository = new NetworkSelectRepository(context, mSubId); } @Keep Loading Loading @@ -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; Loading @@ -230,7 +232,6 @@ public class NetworkSelectSettings extends DashboardFragment { } return Unit.INSTANCE; } }); } Loading @@ -238,7 +239,6 @@ public class NetworkSelectSettings extends DashboardFragment { public void onStart() { super.onStart(); updateForbiddenPlmns(); setProgressBarVisible(true); mUpdateScanResult = true; } Loading Loading @@ -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; Loading @@ -517,7 +499,6 @@ public class NetworkSelectSettings extends DashboardFragment { break; } } } /** * Clear all of the preference summary Loading
tests/spa_unit/src/com/android/settings/network/telephony/NetworkSelectRepositoryTest.kt 0 → 100644 +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" } }