Merge "Get NetworkRegistrationInfo on background thread" into main
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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()
|
||||||
|
}
|
||||||
|
}
|
@@ -24,12 +24,10 @@ import android.os.Handler;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.AccessNetworkConstants;
|
|
||||||
import android.telephony.CarrierConfigManager;
|
import android.telephony.CarrierConfigManager;
|
||||||
import android.telephony.CellIdentity;
|
import android.telephony.CellIdentity;
|
||||||
import android.telephony.CellInfo;
|
import android.telephony.CellInfo;
|
||||||
import android.telephony.NetworkRegistrationInfo;
|
import android.telephony.NetworkRegistrationInfo;
|
||||||
import android.telephony.ServiceState;
|
|
||||||
import android.telephony.SignalStrength;
|
import android.telephony.SignalStrength;
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
import android.telephony.TelephonyManager;
|
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.NetworkScanCellInfos;
|
||||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete;
|
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.NetworkScanError;
|
||||||
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanResult;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function1;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -101,6 +97,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
|||||||
private NetworkScanRepository mNetworkScanRepository;
|
private NetworkScanRepository mNetworkScanRepository;
|
||||||
private boolean mUpdateScanResult = false;
|
private boolean mUpdateScanResult = false;
|
||||||
|
|
||||||
|
private NetworkSelectRepository mNetworkSelectRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
@@ -138,6 +136,7 @@ public class NetworkSelectSettings extends DashboardFragment {
|
|||||||
mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor,
|
mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor,
|
||||||
mCarrierConfigChangeListener);
|
mCarrierConfigChangeListener);
|
||||||
mNetworkScanRepository = new NetworkScanRepository(context, mSubId);
|
mNetworkScanRepository = new NetworkScanRepository(context, mSubId);
|
||||||
|
mNetworkSelectRepository = new NetworkSelectRepository(context, mSubId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@@ -202,35 +201,37 @@ public class NetworkSelectSettings extends DashboardFragment {
|
|||||||
mProgressHeader = setPinnedHeaderView(
|
mProgressHeader = setPinnedHeaderView(
|
||||||
com.android.settingslib.widget.progressbar.R.layout.progress_header
|
com.android.settingslib.widget.progressbar.R.layout.progress_header
|
||||||
).findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
|
).findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
|
||||||
forceUpdateConnectedPreferenceCategory();
|
mNetworkSelectRepository.launchUpdateNetworkRegistrationInfo(
|
||||||
|
getViewLifecycleOwner(),
|
||||||
|
(info) -> {
|
||||||
|
forceUpdateConnectedPreferenceCategory(info);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
launchNetworkScan();
|
launchNetworkScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchNetworkScan() {
|
private void launchNetworkScan() {
|
||||||
mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), new Function1<>() {
|
mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> {
|
||||||
@Override
|
if (!mUpdateScanResult) {
|
||||||
public Unit invoke(@NonNull NetworkScanResult networkScanResult) {
|
// Not update UI if not in scan mode.
|
||||||
if (!mUpdateScanResult) {
|
|
||||||
// Not update UI if not in scan mode.
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
if (networkScanResult instanceof NetworkScanCellInfos networkScanCellInfos) {
|
|
||||||
scanResultHandler(networkScanCellInfos.getCellInfos());
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
if (!isPreferenceScreenEnabled()) {
|
|
||||||
clearPreferenceSummary();
|
|
||||||
enablePreferenceScreen(true);
|
|
||||||
} else if (networkScanResult instanceof NetworkScanComplete
|
|
||||||
&& mCellInfoList == null) {
|
|
||||||
// In case the scan timeout before getting any results
|
|
||||||
addMessagePreference(R.string.empty_networks_list);
|
|
||||||
} else if (networkScanResult instanceof NetworkScanError) {
|
|
||||||
addMessagePreference(R.string.network_query_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
}
|
}
|
||||||
|
if (networkScanResult instanceof NetworkScanCellInfos networkScanCellInfos) {
|
||||||
|
scanResultHandler(networkScanCellInfos.getCellInfos());
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
}
|
||||||
|
if (!isPreferenceScreenEnabled()) {
|
||||||
|
clearPreferenceSummary();
|
||||||
|
enablePreferenceScreen(true);
|
||||||
|
} else if (networkScanResult instanceof NetworkScanComplete
|
||||||
|
&& mCellInfoList == null) {
|
||||||
|
// In case the scan timeout before getting any results
|
||||||
|
addMessagePreference(R.string.empty_networks_list);
|
||||||
|
} else if (networkScanResult instanceof NetworkScanError) {
|
||||||
|
addMessagePreference(R.string.network_query_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +239,6 @@ public class NetworkSelectSettings extends DashboardFragment {
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
updateForbiddenPlmns();
|
|
||||||
setProgressBarVisible(true);
|
setProgressBarVisible(true);
|
||||||
mUpdateScanResult = true;
|
mUpdateScanResult = true;
|
||||||
}
|
}
|
||||||
@@ -477,45 +477,26 @@ public class NetworkSelectSettings extends DashboardFragment {
|
|||||||
* - If the device has no data, we will remove the connected network operators list from the
|
* - If the device has no data, we will remove the connected network operators list from the
|
||||||
* screen.
|
* screen.
|
||||||
*/
|
*/
|
||||||
private void forceUpdateConnectedPreferenceCategory() {
|
private void forceUpdateConnectedPreferenceCategory(
|
||||||
if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
|
NetworkSelectRepository.NetworkRegistrationAndForbiddenInfo info) {
|
||||||
// Try to get the network registration states
|
for (NetworkRegistrationInfo regInfo : info.getNetworkList()) {
|
||||||
final ServiceState ss = mTelephonyManager.getServiceState();
|
final CellIdentity cellIdentity = regInfo.getCellIdentity();
|
||||||
if (ss == null) {
|
if (cellIdentity == null) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
final List<NetworkRegistrationInfo> networkList =
|
final NetworkOperatorPreference pref = new NetworkOperatorPreference(
|
||||||
ss.getNetworkRegistrationInfoListForTransportType(
|
getPrefContext(), info.getForbiddenPlmns(), mShow4GForLTE);
|
||||||
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
|
pref.updateCell(null, cellIdentity);
|
||||||
if (networkList == null || networkList.size() == 0) {
|
if (pref.isForbiddenNetwork()) {
|
||||||
return;
|
continue;
|
||||||
}
|
|
||||||
// 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) {
|
|
||||||
final CellIdentity cellIdentity = regInfo.getCellIdentity();
|
|
||||||
if (cellIdentity == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final NetworkOperatorPreference pref = new NetworkOperatorPreference(
|
|
||||||
getPrefContext(), mForbiddenPlmns, mShow4GForLTE);
|
|
||||||
pref.updateCell(null, cellIdentity);
|
|
||||||
if (pref.isForbiddenNetwork()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pref.setSummary(R.string.network_connected);
|
|
||||||
// Update the signal strength icon, since the default signalStrength value
|
|
||||||
// would be zero
|
|
||||||
// (it would be quite confusing why the connected network has no signal)
|
|
||||||
pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1);
|
|
||||||
mPreferenceCategory.addPreference(pref);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
pref.setSummary(R.string.network_connected);
|
||||||
|
// Update the signal strength icon, since the default signalStrength value
|
||||||
|
// would be zero
|
||||||
|
// (it would be quite confusing why the connected network has no signal)
|
||||||
|
pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1);
|
||||||
|
mPreferenceCategory.addPreference(pref);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user