diff --git a/src/com/android/settings/network/telephony/CellInfoUtil.kt b/src/com/android/settings/network/telephony/CellInfoUtil.kt index c7b6b24b521..51f60e796ff 100644 --- a/src/com/android/settings/network/telephony/CellInfoUtil.kt +++ b/src/com/android/settings/network/telephony/CellInfoUtil.kt @@ -82,7 +82,7 @@ object CellInfoUtil { */ @JvmStatic fun cellInfoListToString(cellInfos: List): String = - cellInfos.joinToString { cellInfo -> cellInfo.readableString() } + cellInfos.joinToString(System.lineSeparator()) { cellInfo -> cellInfo.readableString() } /** * Convert [CellInfo] to a readable string without sensitive info. diff --git a/src/com/android/settings/network/telephony/NetworkScanHelper.java b/src/com/android/settings/network/telephony/NetworkScanHelper.java deleted file mode 100644 index 19613291231..00000000000 --- a/src/com/android/settings/network/telephony/NetworkScanHelper.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (C) 2018 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.annotation.IntDef; -import android.content.Context; -import android.telephony.AccessNetworkConstants.AccessNetworkType; -import android.telephony.CellInfo; -import android.telephony.NetworkScan; -import android.telephony.NetworkScanRequest; -import android.telephony.PhoneCapability; -import android.telephony.RadioAccessSpecifier; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyScanManager; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; - -import com.android.internal.telephony.CellNetworkScanResult; - -import com.android.settings.R; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; - -/** - * A helper class that builds the common interface and performs the network scan for two different - * network scan APIs. - */ -public class NetworkScanHelper { - public static final String TAG = "NetworkScanHelper"; - - /** - * Callbacks interface to inform the network scan results. - */ - public interface NetworkScanCallback { - /** - * Called when the results is returned from {@link TelephonyManager}. This method will be - * called at least one time if there is no error occurred during the network scan. - * - *

This method can be called multiple times in one network scan, until - * {@link #onComplete()} or {@link #onError(int)} is called. - * - * @param results - */ - void onResults(List results); - - /** - * Called when the current network scan process is finished. No more - * {@link #onResults(List)} will be called for the current network scan after this method is - * called. - */ - void onComplete(); - - /** - * Called when an error occurred during the network scan process. - * - *

There is no more result returned from {@link TelephonyManager} if an error occurred. - * - *

{@link #onComplete()} will not be called if an error occurred. - * - * @see {@link NetworkScan.ScanErrorCode} - */ - void onError(int errorCode); - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS}) - public @interface NetworkQueryType {} - - /** - * Performs the network scan using {@link TelephonyManager#getAvailableNetworks()}. The network - * scan results won't be returned to the caller until the network scan is completed. - * - *

This is typically used when the modem doesn't support the new network scan api - * {@link TelephonyManager#requestNetworkScan( - * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. - */ - public static final int NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS = 1; - - /** - * Performs the network scan using {@link TelephonyManager#requestNetworkScan( - * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} The network scan - * results will be returned to the caller periodically in a small time window until the network - * scan is completed. The complete results should be returned in the last called of - * {@link NetworkScanCallback#onResults(List)}. - * - *

This is recommended to be used if modem supports the new network scan api - * {@link TelephonyManager#requestNetworkScan( - * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} - */ - public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2; - - /** The constants below are used in the async network scan. */ - @VisibleForTesting - static final boolean INCREMENTAL_RESULTS = true; - @VisibleForTesting - static final int SEARCH_PERIODICITY_SEC = 5; - @VisibleForTesting - static final int MAX_SEARCH_TIME_SEC = 300; - @VisibleForTesting - static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3; - - private final NetworkScanCallback mNetworkScanCallback; - private final TelephonyManager mTelephonyManager; - private final TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback; - private final Executor mExecutor; - - private int mMaxSearchTimeSec = MAX_SEARCH_TIME_SEC; - private NetworkScan mNetworkScanRequester; - - /** Callbacks for sync network scan */ - private ListenableFuture> mNetworkScanFuture; - - public NetworkScanHelper(TelephonyManager tm, NetworkScanCallback callback, Executor executor) { - mTelephonyManager = tm; - mNetworkScanCallback = callback; - mInternalNetworkScanCallback = new NetworkScanCallbackImpl(); - mExecutor = executor; - } - - public NetworkScanHelper(Context context, TelephonyManager tm, NetworkScanCallback callback, - Executor executor) { - this(tm, callback, executor); - mMaxSearchTimeSec = context.getResources().getInteger( - R.integer.config_network_scan_helper_max_search_time_sec); - } - - @VisibleForTesting - NetworkScanRequest createNetworkScanForPreferredAccessNetworks() { - long networkTypeBitmap3gpp = mTelephonyManager.getPreferredNetworkTypeBitmask() - & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP; - - List radioAccessSpecifiers = new ArrayList<>(); - // If the allowed network types are unknown or if they are of the right class, scan for - // them; otherwise, skip them to save scan time and prevent users from being shown networks - // that they can't connect to. - if (networkTypeBitmap3gpp == 0 - || (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_2G) != 0) { - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)); - } - if (networkTypeBitmap3gpp == 0 - || (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_3G) != 0) { - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkType.UTRAN, null, null)); - } - if (networkTypeBitmap3gpp == 0 - || (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_4G) != 0) { - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkType.EUTRAN, null, null)); - } - // If a device supports 5G stand-alone then the code below should be re-enabled; however - // a device supporting only non-standalone mode cannot perform PLMN selection and camp on - // a 5G network, which means that it shouldn't scan for 5G at the expense of battery as - // part of the manual network selection process. - // - if (networkTypeBitmap3gpp == 0 - || (hasNrSaCapability() - && (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_5G) != 0)) { - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkType.NGRAN, null, null)); - Log.d(TAG, "radioAccessSpecifiers add NGRAN."); - } - - return new NetworkScanRequest( - NetworkScanRequest.SCAN_TYPE_ONE_SHOT, - radioAccessSpecifiers.toArray( - new RadioAccessSpecifier[radioAccessSpecifiers.size()]), - SEARCH_PERIODICITY_SEC, - mMaxSearchTimeSec, - INCREMENTAL_RESULTS, - INCREMENTAL_RESULTS_PERIODICITY_SEC, - null /* List of PLMN ids (MCC-MNC) */); - } - - /** - * Performs a network scan for the given type {@code type}. - * {@link #NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS} is recommended if modem supports - * {@link TelephonyManager#requestNetworkScan( - * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. - * - * @param type used to tell which network scan API should be used. - */ - public void startNetworkScan(@NetworkQueryType int type) { - if (type == NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS) { - mNetworkScanFuture = SettableFuture.create(); - Futures.addCallback(mNetworkScanFuture, new FutureCallback>() { - @Override - public void onSuccess(List result) { - onResults(result); - onComplete(); - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof CancellationException) { - return; - } - int errCode = Integer.parseInt(t.getMessage()); - onError(errCode); - } - }, MoreExecutors.directExecutor()); - mExecutor.execute(new NetworkScanSyncTask( - mTelephonyManager, (SettableFuture) mNetworkScanFuture)); - } else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) { - if (mNetworkScanRequester != null) { - return; - } - mNetworkScanRequester = mTelephonyManager.requestNetworkScan( - createNetworkScanForPreferredAccessNetworks(), - mExecutor, - mInternalNetworkScanCallback); - if (mNetworkScanRequester == null) { - onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR); - } - } - } - - /** - * The network scan of type {@link #NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS} can't be stopped, - * however, the result of the current network scan won't be returned to the callback after - * calling this method. - */ - public void stopNetworkQuery() { - if (mNetworkScanRequester != null) { - mNetworkScanRequester.stopScan(); - mNetworkScanRequester = null; - } - - if (mNetworkScanFuture != null) { - mNetworkScanFuture.cancel(true /* mayInterruptIfRunning */); - mNetworkScanFuture = null; - } - } - - private void onResults(List cellInfos) { - mNetworkScanCallback.onResults(cellInfos); - } - - private void onComplete() { - mNetworkScanCallback.onComplete(); - } - - private void onError(int errCode) { - mNetworkScanCallback.onError(errCode); - } - - private boolean hasNrSaCapability() { - return Arrays.stream( - mTelephonyManager.getPhoneCapability().getDeviceNrCapabilities()) - .anyMatch(i -> i == PhoneCapability.DEVICE_NR_CAPABILITY_SA); - } - - /** - * Converts the status code of {@link CellNetworkScanResult} to one of the - * {@link NetworkScan.ScanErrorCode}. - * @param errCode status code from {@link CellNetworkScanResult}. - * - * @return one of the scan error code from {@link NetworkScan.ScanErrorCode}. - */ - private static int convertToScanErrorCode(int errCode) { - switch (errCode) { - case CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE: - return NetworkScan.ERROR_RADIO_INTERFACE_ERROR; - case CellNetworkScanResult.STATUS_RADIO_GENERIC_FAILURE: - default: - return NetworkScan.ERROR_MODEM_ERROR; - } - } - - private final class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback { - public void onResults(List results) { - Log.d(TAG, "Async scan onResults() results = " - + CellInfoUtil.cellInfoListToString(results)); - NetworkScanHelper.this.onResults(results); - } - - public void onComplete() { - Log.d(TAG, "async scan onComplete()"); - NetworkScanHelper.this.onComplete(); - } - - public void onError(@NetworkScan.ScanErrorCode int errCode) { - Log.d(TAG, "async scan onError() errorCode = " + errCode); - NetworkScanHelper.this.onError(errCode); - } - } - - private static final class NetworkScanSyncTask implements Runnable { - private final SettableFuture> mCallback; - private final TelephonyManager mTelephonyManager; - - NetworkScanSyncTask( - TelephonyManager telephonyManager, SettableFuture> callback) { - mTelephonyManager = telephonyManager; - mCallback = callback; - } - - @Override - public void run() { - final CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks(); - if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) { - final List cellInfos = result.getOperators() - .stream() - .map(operatorInfo - -> CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo)) - .collect(Collectors.toList()); - Log.d(TAG, "Sync network scan completed, cellInfos = " - + CellInfoUtil.cellInfoListToString(cellInfos)); - mCallback.set(cellInfos); - } else { - final Throwable error = new Throwable( - Integer.toString(convertToScanErrorCode(result.getStatus()))); - mCallback.setException(error); - Log.d(TAG, "Sync network scan error, ex = " + error); - } - } - } -} diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index c8617afb38c..33ece6be6fe 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -16,7 +16,6 @@ package com.android.settings.network.telephony; -import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; @@ -39,6 +38,7 @@ import android.util.Log; import android.view.View; import androidx.annotation.Keep; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -49,14 +49,21 @@ import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.flags.Flags; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; +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; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; @@ -71,12 +78,8 @@ public class NetworkSelectSettings extends DashboardFragment { private static final String TAG = "NetworkSelectSettings"; private static final int EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE = 1; - private static final int EVENT_NETWORK_SCAN_RESULTS = 2; - private static final int EVENT_NETWORK_SCAN_ERROR = 3; - private static final int EVENT_NETWORK_SCAN_COMPLETED = 4; private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference"; - private static final int MIN_NUMBER_OF_SCAN_REQUIRED = 2; private PreferenceCategory mPreferenceCategory; @VisibleForTesting @@ -91,18 +94,14 @@ public class NetworkSelectSettings extends DashboardFragment { private CarrierConfigManager mCarrierConfigManager; private List mForbiddenPlmns; private boolean mShow4GForLTE = false; - private NetworkScanHelper mNetworkScanHelper; private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1); private MetricsFeatureProvider mMetricsFeatureProvider; - private boolean mUseNewApi; - private long mRequestIdManualNetworkSelect; - private long mRequestIdManualNetworkScan; - private long mWaitingForNumberOfScanResults; - @VisibleForTesting - boolean mIsAggregationEnabled = false; private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener; private AtomicBoolean mShouldFilterOutSatellitePlmn = new AtomicBoolean(); + private NetworkScanRepository mNetworkScanRepository; + private boolean mUpdateScanResult = false; + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -114,7 +113,6 @@ public class NetworkSelectSettings extends DashboardFragment { @Initializer protected void onCreateInitialization() { Context context = getContext(); - mUseNewApi = enableNewAutoSelectNetworkUI(context); mSubId = getSubId(); mPreferenceCategory = getPreferenceCategory(PREF_KEY_NETWORK_OPERATORS); @@ -124,8 +122,6 @@ public class NetworkSelectSettings extends DashboardFragment { mTelephonyManager = getTelephonyManager(context, mSubId); mSatelliteManager = getSatelliteManager(context); mCarrierConfigManager = getCarrierConfigManager(context); - mNetworkScanHelper = new NetworkScanHelper( - mTelephonyManager, mCallback, mNetworkScanExecutor); PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mSubId, CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL); @@ -136,30 +132,13 @@ public class NetworkSelectSettings extends DashboardFragment { true)); mMetricsFeatureProvider = getMetricsFeatureProvider(context); - mIsAggregationEnabled = enableAggregation(context); - Log.d(TAG, "init: mUseNewApi:" + mUseNewApi - + " ,mIsAggregationEnabled:" + mIsAggregationEnabled + " ,mSubId:" + mSubId); mCarrierConfigChangeListener = (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigChanged( subId); mCarrierConfigManager.registerCarrierConfigChangeListener(mNetworkScanExecutor, mCarrierConfigChangeListener); - - } - - @Keep - @VisibleForTesting - protected boolean enableNewAutoSelectNetworkUI(Context context) { - return context.getResources().getBoolean( - com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI); - } - - @Keep - @VisibleForTesting - protected boolean enableAggregation(Context context) { - return context.getResources().getBoolean( - R.bool.config_network_selection_list_aggregation_enabled); + mNetworkScanRepository = new NetworkScanRepository(context, mSubId); } @Keep @@ -218,17 +197,42 @@ public class NetworkSelectSettings extends DashboardFragment { } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - final Activity activity = getActivity(); - if (activity != null) { - mProgressHeader = setPinnedHeaderView( - com.android.settingslib.widget.progressbar.R.layout.progress_header) - .findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation); - setProgressBarVisible(false); - } + mProgressHeader = setPinnedHeaderView( + com.android.settingslib.widget.progressbar.R.layout.progress_header + ).findViewById(com.android.settingslib.widget.progressbar.R.id.progress_bar_animation); forceUpdateConnectedPreferenceCategory(); + launchNetworkScan(); + } + + private void launchNetworkScan() { + mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), new Function1<>() { + @Override + public Unit invoke(@NonNull NetworkScanResult networkScanResult) { + 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; + } + }); } @Override @@ -236,12 +240,8 @@ public class NetworkSelectSettings extends DashboardFragment { super.onStart(); updateForbiddenPlmns(); - if (isProgressBarVisible()) { - return; - } - if (mWaitingForNumberOfScanResults <= 0) { - startNetworkQuery(); - } + setProgressBarVisible(true); + mUpdateScanResult = true; } /** @@ -256,14 +256,6 @@ public class NetworkSelectSettings extends DashboardFragment { : new ArrayList<>(); } - @Override - public void onStop() { - if (mWaitingForNumberOfScanResults <= 0) { - stopNetworkQuery(); - } - super.onStop(); - } - @Override public boolean onPreferenceTreeClick(Preference preference) { if (preference == mSelectedPreference) { @@ -275,7 +267,7 @@ public class NetworkSelectSettings extends DashboardFragment { return false; } - stopNetworkQuery(); + mUpdateScanResult = false; // Refresh the last selected item in case users reselect network. clearPreferenceSummary(); @@ -294,8 +286,6 @@ public class NetworkSelectSettings extends DashboardFragment { // Disable the screen until network is manually set enablePreferenceScreen(false); - mRequestIdManualNetworkSelect = getNewRequestId(); - mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED; final OperatorInfo operator = mSelectedPreference.getOperatorInfo(); ThreadUtils.postOnBackgroundThread(() -> { final Message msg = mHandler.obtainMessage( @@ -329,7 +319,6 @@ public class NetworkSelectSettings extends DashboardFragment { switch (msg.what) { case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE: final boolean isSucceed = (boolean) msg.obj; - stopNetworkQuery(); setProgressBarVisible(false); enablePreferenceScreen(true); @@ -341,86 +330,15 @@ public class NetworkSelectSettings extends DashboardFragment { Log.e(TAG, "No preference to update!"); } break; - case EVENT_NETWORK_SCAN_RESULTS: - scanResultHandler((List) msg.obj); - break; - - case EVENT_NETWORK_SCAN_ERROR: - stopNetworkQuery(); - Log.i(TAG, "Network scan failure " + msg.arg1 + ":" - + " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan) - + ", waiting for scan results = " + mWaitingForNumberOfScanResults - + ", select request 0x" - + Long.toHexString(mRequestIdManualNetworkSelect)); - if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { - break; - } - if (!isPreferenceScreenEnabled()) { - clearPreferenceSummary(); - enablePreferenceScreen(true); - } else { - addMessagePreference(R.string.network_query_error); - } - break; - - case EVENT_NETWORK_SCAN_COMPLETED: - stopNetworkQuery(); - Log.d(TAG, "Network scan complete:" - + " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan) - + ", waiting for scan results = " + mWaitingForNumberOfScanResults - + ", select request 0x" - + Long.toHexString(mRequestIdManualNetworkSelect)); - if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { - break; - } - if (!isPreferenceScreenEnabled()) { - clearPreferenceSummary(); - enablePreferenceScreen(true); - } else if (mCellInfoList == null) { - // In case the scan timeout before getting any results - addMessagePreference(R.string.empty_networks_list); - } - break; } - return; } }; - @VisibleForTesting - List doAggregation(List cellInfoListInput) { - if (!mIsAggregationEnabled) { - Log.d(TAG, "no aggregation"); - return new ArrayList<>(cellInfoListInput); - } - ArrayList aggregatedList = new ArrayList<>(); - for (CellInfo cellInfo : cellInfoListInput) { - String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity()); - Class className = cellInfo.getClass(); - - Optional itemInTheList = aggregatedList.stream().filter( - item -> { - String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity()); - return itemPlmn.equals(plmn) && item.getClass().equals(className); - }) - .findFirst(); - if (itemInTheList.isPresent()) { - if (cellInfo.isRegistered() && !itemInTheList.get().isRegistered()) { - // Adding the registered cellinfo item into list. If there are two registered - // cellinfo items, then select first one from source list. - aggregatedList.set(aggregatedList.indexOf(itemInTheList.get()), cellInfo); - } - continue; - } - aggregatedList.add(cellInfo); - } - - return filterOutSatellitePlmn(aggregatedList); - } - /* We do not want to expose carrier satellite plmns to the user when manually scan the cellular network. Therefore, it is needed to filter out satellite plmns from current cell info list */ - private List filterOutSatellitePlmn(List cellInfoList) { + @VisibleForTesting + List filterOutSatellitePlmn(List cellInfoList) { List aggregatedSatellitePlmn = getSatellitePlmnsForCarrierWrapper(); if (!mShouldFilterOutSatellitePlmn.get() || aggregatedSatellitePlmn.isEmpty()) { return cellInfoList; @@ -455,39 +373,10 @@ public class NetworkSelectSettings extends DashboardFragment { } } - private final NetworkScanHelper.NetworkScanCallback mCallback = - new NetworkScanHelper.NetworkScanCallback() { - public void onResults(List results) { - final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results); - msg.sendToTarget(); - } - - public void onComplete() { - final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED); - msg.sendToTarget(); - } - - public void onError(int error) { - final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, - 0 /* arg2 */); - msg.sendToTarget(); - } - }; - @Keep @VisibleForTesting protected void scanResultHandler(List results) { - if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { - Log.d(TAG, "CellInfoList (drop): " - + CellInfoUtil.cellInfoListToString(new ArrayList<>(results))); - return; - } - mWaitingForNumberOfScanResults--; - if ((mWaitingForNumberOfScanResults <= 0) && (!isResumed())) { - stopNetworkQuery(); - } - - mCellInfoList = doAggregation(results); + mCellInfoList = filterOutSatellitePlmn(results); Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); if (mCellInfoList != null && mCellInfoList.size() != 0) { final NetworkOperatorPreference connectedPref = updateAllPreferenceCategory(); @@ -646,11 +535,6 @@ public class NetworkSelectSettings extends DashboardFragment { } } - private long getNewRequestId() { - return Math.max(mRequestIdManualNetworkSelect, - mRequestIdManualNetworkScan) + 1; - } - private boolean isProgressBarVisible() { if (mProgressHeader == null) { return false; @@ -671,29 +555,8 @@ public class NetworkSelectSettings extends DashboardFragment { mPreferenceCategory.addPreference(mStatusMessagePreference); } - private void startNetworkQuery() { - setProgressBarVisible(true); - if (mNetworkScanHelper != null) { - mRequestIdManualNetworkScan = getNewRequestId(); - mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED; - mNetworkScanHelper.startNetworkScan( - mUseNewApi - ? NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS - : NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS); - } - } - - private void stopNetworkQuery() { - setProgressBarVisible(false); - if (mNetworkScanHelper != null) { - mWaitingForNumberOfScanResults = 0; - mNetworkScanHelper.stopNetworkQuery(); - } - } - @Override public void onDestroy() { - stopNetworkQuery(); mNetworkScanExecutor.shutdown(); super.onDestroy(); } diff --git a/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt new file mode 100644 index 00000000000..2067b8c3be5 --- /dev/null +++ b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt @@ -0,0 +1,167 @@ +/* + * 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.scan + +import android.content.Context +import android.telephony.AccessNetworkConstants.AccessNetworkType +import android.telephony.CellInfo +import android.telephony.NetworkScanRequest +import android.telephony.PhoneCapability +import android.telephony.RadioAccessSpecifier +import android.telephony.TelephonyManager +import android.telephony.TelephonyScanManager +import android.util.Log +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LifecycleOwner +import com.android.settings.network.telephony.CellInfoUtil +import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle +import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flowOn + +class NetworkScanRepository(context: Context, subId: Int) { + sealed interface NetworkScanResult + + data class NetworkScanCellInfos(val cellInfos: List) : NetworkScanResult + data object NetworkScanComplete : NetworkScanResult + data class NetworkScanError(val error: Int) : NetworkScanResult + + private val telephonyManager = + context.getSystemService(TelephonyManager::class.java)!!.createForSubscriptionId(subId) + + /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */ + fun launchNetworkScan(lifecycleOwner: LifecycleOwner, onResult: (NetworkScanResult) -> Unit) { + networkScanFlow().collectLatestWithLifecycle(lifecycleOwner, action = onResult) + } + + data class CellInfoScanKey( + val title: String?, + val className: String, + val isRegistered: Boolean, + ) { + constructor(cellInfo: CellInfo) : this( + title = cellInfo.cellIdentity.getNetworkTitle(), + className = cellInfo.javaClass.name, + isRegistered = cellInfo.isRegistered, + ) + } + + fun networkScanFlow(): Flow = callbackFlow { + val callback = object : TelephonyScanManager.NetworkScanCallback() { + override fun onResults(results: List) { + val cellInfos = results.distinctBy { CellInfoScanKey(it) } + trySend(NetworkScanCellInfos(cellInfos)) + Log.d(TAG, "CellInfoList: ${CellInfoUtil.cellInfoListToString(cellInfos)}") + } + + override fun onComplete() { + trySend(NetworkScanComplete) + close() + Log.d(TAG, "onComplete") + } + + override fun onError(error: Int) { + trySend(NetworkScanError(error)) + close() + Log.d(TAG, "onError: $error") + } + } + + val networkScan = telephonyManager.requestNetworkScan( + createNetworkScan(), + Dispatchers.Default.asExecutor(), + callback, + ) + + awaitClose { networkScan.stopScan() } + }.flowOn(Dispatchers.Default) + + /** Create network scan for allowed network types. */ + private fun createNetworkScan(): NetworkScanRequest { + val allowedNetworkTypes = getAllowedNetworkTypes() + Log.d(TAG, "createNetworkScan: allowedNetworkTypes = $allowedNetworkTypes") + val radioAccessSpecifiers = allowedNetworkTypes + .map { RadioAccessSpecifier(it, null, null) } + .toTypedArray() + return NetworkScanRequest( + NetworkScanRequest.SCAN_TYPE_ONE_SHOT, + radioAccessSpecifiers, + NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC, // one shot, not used + MAX_SEARCH_TIME_SEC, + true, + INCREMENTAL_RESULTS_PERIODICITY_SEC, + null, + ) + } + + private fun getAllowedNetworkTypes(): List { + val networkTypeBitmap3gpp: Long = + telephonyManager.getAllowedNetworkTypesBitmask() and + TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP + return buildList { + // If the allowed network types are unknown or if they are of the right class, scan for + // them; otherwise, skip them to save scan time and prevent users from being shown + // networks that they can't connect to. + if (networkTypeBitmap3gpp == 0L + || networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_2G != 0L + ) { + add(AccessNetworkType.GERAN) + } + if (networkTypeBitmap3gpp == 0L + || networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_3G != 0L + ) { + add(AccessNetworkType.UTRAN) + } + if (networkTypeBitmap3gpp == 0L + || networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_4G != 0L + ) { + add(AccessNetworkType.EUTRAN) + } + // If a device supports 5G stand-alone then the code below should be re-enabled; however + // a device supporting only non-standalone mode cannot perform PLMN selection and camp + // on a 5G network, which means that it shouldn't scan for 5G at the expense of battery + // as part of the manual network selection process. + // + if (networkTypeBitmap3gpp == 0L + || (networkTypeBitmap3gpp and TelephonyManager.NETWORK_CLASS_BITMASK_5G != 0L && + hasNrSaCapability()) + ) { + add(AccessNetworkType.NGRAN) + Log.d(TAG, "radioAccessSpecifiers add NGRAN.") + } + } + } + + private fun hasNrSaCapability(): Boolean { + val phoneCapability = telephonyManager.getPhoneCapability() + return PhoneCapability.DEVICE_NR_CAPABILITY_SA in phoneCapability.deviceNrCapabilities + } + + companion object { + private const val TAG = "NetworkScanRepository" + + @VisibleForTesting + val MAX_SEARCH_TIME_SEC = 300 + + @VisibleForTesting + val INCREMENTAL_RESULTS_PERIODICITY_SEC = 3 + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt new file mode 100644 index 00000000000..070c779b5ea --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt @@ -0,0 +1,265 @@ +/* + * 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.scan + +import android.content.Context +import android.telephony.AccessNetworkConstants.AccessNetworkType +import android.telephony.CellIdentityCdma +import android.telephony.CellIdentityGsm +import android.telephony.CellIdentityLte +import android.telephony.CellInfoCdma +import android.telephony.CellInfoGsm +import android.telephony.CellInfoLte +import android.telephony.NetworkScan +import android.telephony.NetworkScanRequest +import android.telephony.PhoneCapability +import android.telephony.TelephonyManager +import android.telephony.TelephonyManager.NETWORK_CLASS_BITMASK_5G +import android.telephony.TelephonyScanManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +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.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argThat +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class NetworkScanRepositoryTest { + + private var callback: TelephonyScanManager.NetworkScanCallback? = null + + private val mockTelephonyManager = mock { + on { createForSubscriptionId(SUB_ID) } doReturn mock + on { requestNetworkScan(any(), any(), any()) } doAnswer { + callback = it.arguments[2] as TelephonyScanManager.NetworkScanCallback + mock() + } + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager + } + + private val repository = NetworkScanRepository(context, SUB_ID) + + @Test + fun networkScanFlow_initial() = runBlocking { + val result = repository.networkScanFlow().firstWithTimeoutOrNull() + + assertThat(result).isNull() + } + + @Test + fun networkScanFlow_onResults(): Unit = runBlocking { + val cellInfos = listOf(CellInfoCdma().apply { cellIdentity = CELL_IDENTITY_CDMA }) + val listDeferred = async { + repository.networkScanFlow().toListWithTimeout() + } + delay(100) + + callback?.onResults(cellInfos) + + assertThat(listDeferred.await()).containsExactly(NetworkScanCellInfos(cellInfos)) + } + + @Test + fun networkScanFlow_onComplete(): Unit = runBlocking { + val listDeferred = async { + repository.networkScanFlow().toListWithTimeout() + } + delay(100) + + callback?.onComplete() + + assertThat(listDeferred.await()).containsExactly(NetworkScanComplete) + } + + @Test + fun networkScanFlow_onError(): Unit = runBlocking { + val listDeferred = async { + repository.networkScanFlow().toListWithTimeout() + } + delay(100) + + callback?.onError(1) + + assertThat(listDeferred.await()).containsExactly(NetworkScanError(1)) + } + + @Test + fun networkScanFlow_hasDuplicateItems(): Unit = runBlocking { + val cellInfos = listOf( + createCellInfoLte("123", false), + createCellInfoLte("123", false), + createCellInfoLte("124", true), + createCellInfoLte("124", true), + createCellInfoGsm("123", false), + createCellInfoGsm("123", false), + ) + val listDeferred = async { + repository.networkScanFlow().toListWithTimeout() + } + delay(100) + + callback?.onResults(cellInfos) + + assertThat(listDeferred.await()).containsExactly( + NetworkScanCellInfos( + listOf( + createCellInfoLte("123", false), + createCellInfoLte("124", true), + createCellInfoGsm("123", false), + ) + ) + ) + } + + + @Test + fun networkScanFlow_noDuplicateItems(): Unit = runBlocking { + val cellInfos = listOf( + createCellInfoLte("123", false), + createCellInfoLte("123", true), + createCellInfoLte("124", false), + createCellInfoLte("124", true), + createCellInfoGsm("456", false), + createCellInfoGsm("456", true), + ) + val listDeferred = async { + repository.networkScanFlow().toListWithTimeout() + } + delay(100) + + callback?.onResults(cellInfos) + + assertThat(listDeferred.await()).containsExactly( + NetworkScanCellInfos( + listOf( + createCellInfoLte("123", false), + createCellInfoLte("123", true), + createCellInfoLte("124", false), + createCellInfoLte("124", true), + createCellInfoGsm("456", false), + createCellInfoGsm("456", true), + ) + ) + ) + } + + @Test + fun createNetworkScan_deviceHasNrSa_requestNgran(): Unit = runBlocking { + mockTelephonyManager.stub { + on { getAllowedNetworkTypesBitmask() } doReturn NETWORK_CLASS_BITMASK_5G + on { getPhoneCapability() } doReturn + createPhoneCapability(intArrayOf(PhoneCapability.DEVICE_NR_CAPABILITY_SA)) + } + + repository.networkScanFlow().firstWithTimeoutOrNull() + + verify(mockTelephonyManager).requestNetworkScan(argThat { + specifiers.any { it.radioAccessNetwork == AccessNetworkType.NGRAN } + }, any(), any()) + } + + @Test + fun createNetworkScan_deviceNoNrSa_noNgran(): Unit = runBlocking { + mockTelephonyManager.stub { + on { getAllowedNetworkTypesBitmask() } doReturn NETWORK_CLASS_BITMASK_5G + on { getPhoneCapability() } doReturn + createPhoneCapability(intArrayOf(PhoneCapability.DEVICE_NR_CAPABILITY_NSA)) + } + + repository.networkScanFlow().firstWithTimeoutOrNull() + + verify(mockTelephonyManager).requestNetworkScan(argThat { + specifiers.none { it.radioAccessNetwork == AccessNetworkType.NGRAN } + }, any(), any()) + } + + private companion object { + const val SUB_ID = 1 + const val LONG = "Long" + const val SHORT = "Short" + + val CELL_IDENTITY_CDMA = CellIdentityCdma( + /* nid = */ 1, + /* sid = */ 2, + /* bid = */ 3, + /* lon = */ 4, + /* lat = */ 5, + /* alphal = */ LONG, + /* alphas = */ SHORT, + ) + + private fun createCellInfoLte(alphaLong: String, registered: Boolean): CellInfoLte { + val cellIdentityLte = CellIdentityLte( + /* ci = */ 1, + /* pci = */ 2, + /* tac = */ 3, + /* earfcn = */ 4, + /* bands = */ intArrayOf(1, 2), + /* bandwidth = */ 10000, + /* mccStr = */ null, + /* mncStr = */ null, + /* alphal = */ alphaLong, + /* alphas = */ null, + /* additionalPlmns = */ emptyList(), + /* csgInfo = */ null, + ) + return CellInfoLte().apply { + cellIdentity = cellIdentityLte + isRegistered = registered + } + } + + private fun createCellInfoGsm(alphaLong: String, registered: Boolean): CellInfoGsm { + val cellIdentityGsm = CellIdentityGsm( + /* lac = */ 1, + /* cid = */ 2, + /* arfcn = */ 3, + /* bsic = */ 4, + /* mccStr = */ "123", + /* mncStr = */ "01", + /* alphal = */ alphaLong, + /* alphas = */ null, + /* additionalPlmns = */ emptyList(), + ) + return CellInfoGsm().apply { + cellIdentity = cellIdentityGsm + isRegistered = registered + } + } + + private fun createPhoneCapability(deviceNrCapabilities: IntArray) = + PhoneCapability.Builder().setDeviceNrCapabilities(deviceNrCapabilities).build() + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java deleted file mode 100644 index f046c9a6f22..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * 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.settings.network.telephony; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.telephony.AccessNetworkConstants; -import android.telephony.CellInfo; -import android.telephony.ModemInfo; -import android.telephony.NetworkScan; -import android.telephony.NetworkScanRequest; -import android.telephony.PhoneCapability; -import android.telephony.RadioAccessSpecifier; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyScanManager; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -@RunWith(AndroidJUnit4.class) -public class NetworkScanHelperTest { - - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private List mCellInfos; - @Mock - private NetworkScanHelper.NetworkScanCallback mNetworkScanCallback; - - private static final long THREAD_EXECUTION_TIMEOUT_MS = 3000L; - - private ExecutorService mNetworkScanExecutor; - private NetworkScanHelper mNetworkScanHelper; - - private static final int SCAN_ID = 1234; - private static final int SUB_ID = 1; - - private NetworkScan mNetworkScan; - - public class NetworkScanMock extends NetworkScan { - NetworkScanMock(int scanId, int subId) { - super(scanId, subId); - } - - @Override - public void stopScan() { - return; - } - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mNetworkScanExecutor = Executors.newFixedThreadPool(1); - - mNetworkScanHelper = new NetworkScanHelper(mTelephonyManager, - mNetworkScanCallback, mNetworkScanExecutor); - - mNetworkScan = spy(new NetworkScanMock(SCAN_ID, SUB_ID)); - } - - @Test - public void startNetworkScan_incrementalAndSuccess_completionWithResult() { - when(mCellInfos.size()).thenReturn(1); - - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - TelephonyScanManager.NetworkScanCallback callback = - (TelephonyScanManager.NetworkScanCallback) - (invocation.getArguments()[2]); - callback.onResults(mCellInfos); - callback.onComplete(); - return mNetworkScan; - } - }).when(mTelephonyManager).requestNetworkScan( - any(NetworkScanRequest.class), any(Executor.class), - any(TelephonyScanManager.NetworkScanCallback.class)); - - ArgumentCaptor> argument = ArgumentCaptor.forClass(List.class); - - startNetworkScan_incremental(true); - - verify(mNetworkScanCallback, times(1)).onResults(argument.capture()); - List actualResult = argument.getValue(); - assertThat(actualResult.size()).isEqualTo(mCellInfos.size()); - verify(mNetworkScanCallback, times(1)).onComplete(); - } - - @Test - public void startNetworkScan_incrementalAndImmediateFailure_failureWithErrorCode() { - doReturn(null).when(mTelephonyManager).requestNetworkScan( - any(NetworkScanRequest.class), any(Executor.class), - any(TelephonyScanManager.NetworkScanCallback.class)); - - startNetworkScan_incremental(true); - - verify(mNetworkScanCallback, times(1)).onError(anyInt()); - } - - @Test - public void startNetworkScan_incrementalAndFailure_failureWithErrorCode() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - TelephonyScanManager.NetworkScanCallback callback = - (TelephonyScanManager.NetworkScanCallback) - (invocation.getArguments()[2]); - callback.onError(NetworkScan.ERROR_MODEM_ERROR); - return mNetworkScan; - } - }).when(mTelephonyManager).requestNetworkScan( - any(NetworkScanRequest.class), any(Executor.class), - any(TelephonyScanManager.NetworkScanCallback.class)); - - startNetworkScan_incremental(true); - - verify(mNetworkScanCallback, times(1)).onError(anyInt()); - } - - @Test - public void startNetworkScan_incrementalAndAbort_doStop() { - doReturn(mNetworkScan).when(mTelephonyManager).requestNetworkScan( - any(NetworkScanRequest.class), any(Executor.class), - any(TelephonyScanManager.NetworkScanCallback.class)); - - startNetworkScan_incremental(false); - - verify(mNetworkScan, times(1)).stopScan(); - } - - @Test - public void createNetworkScanForPreferredAccessNetworks_deviceNoNrSa_noNgran() { - int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_NSA}; - PhoneCapability phoneCapability = createPhoneCapability(deviceNrCapabilities); - doReturn(TelephonyManager.NETWORK_CLASS_BITMASK_2G - | TelephonyManager.NETWORK_CLASS_BITMASK_3G - | TelephonyManager.NETWORK_CLASS_BITMASK_4G - | TelephonyManager.NETWORK_CLASS_BITMASK_5G).when( - mTelephonyManager).getPreferredNetworkTypeBitmask(); - doReturn(phoneCapability).when(mTelephonyManager).getPhoneCapability(); - List radioAccessSpecifiers = new ArrayList<>(); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN, null, - null)); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.UTRAN, null, - null)); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null, - null)); - NetworkScanRequest expectedNetworkScanRequest = createNetworkScanRequest( - radioAccessSpecifiers); - - assertEquals(expectedNetworkScanRequest, - mNetworkScanHelper.createNetworkScanForPreferredAccessNetworks()); - } - - @Test - public void createNetworkScanForPreferredAccessNetworks_deviceHasNrSa_hasNgran() { - int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_NSA, - PhoneCapability.DEVICE_NR_CAPABILITY_SA}; - PhoneCapability phoneCapability = createPhoneCapability(deviceNrCapabilities); - doReturn(TelephonyManager.NETWORK_CLASS_BITMASK_2G - | TelephonyManager.NETWORK_CLASS_BITMASK_3G - | TelephonyManager.NETWORK_CLASS_BITMASK_4G - | TelephonyManager.NETWORK_CLASS_BITMASK_5G).when( - mTelephonyManager).getPreferredNetworkTypeBitmask(); - doReturn(phoneCapability).when(mTelephonyManager).getPhoneCapability(); - List radioAccessSpecifiers = new ArrayList<>(); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN, null, - null)); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.UTRAN, null, - null)); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null, - null)); - radioAccessSpecifiers.add( - new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.NGRAN, null, - null)); - NetworkScanRequest expectedNetworkScanRequest = createNetworkScanRequest( - radioAccessSpecifiers); - - assertEquals(expectedNetworkScanRequest, - mNetworkScanHelper.createNetworkScanForPreferredAccessNetworks()); - } - - private PhoneCapability createPhoneCapability(int[] deviceNrCapabilities) { - int maxActiveVoiceCalls = 1; - int maxActiveData = 2; - ModemInfo modemInfo = new ModemInfo(1, 2, true, false); - List logicalModemList = new ArrayList<>(); - logicalModemList.add(modemInfo); - return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, - logicalModemList, false, deviceNrCapabilities); - } - - private NetworkScanRequest createNetworkScanRequest( - List radioAccessSpecifiers) { - return new NetworkScanRequest( - NetworkScanRequest.SCAN_TYPE_ONE_SHOT, - radioAccessSpecifiers.toArray( - new RadioAccessSpecifier[radioAccessSpecifiers.size()]), - mNetworkScanHelper.SEARCH_PERIODICITY_SEC, - mNetworkScanHelper.MAX_SEARCH_TIME_SEC, - mNetworkScanHelper.INCREMENTAL_RESULTS, - mNetworkScanHelper.INCREMENTAL_RESULTS_PERIODICITY_SEC, - null /* List of PLMN ids (MCC-MNC) */); - } - - private void startNetworkScan_incremental(boolean waitForCompletion) { - mNetworkScanHelper.startNetworkScan( - NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS); - if (!waitForCompletion) { - mNetworkScanHelper.stopNetworkQuery(); - } - } - -} diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java index 080534eeb7b..a4657cee8a0 100644 --- a/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java @@ -83,7 +83,6 @@ public class NetworkSelectSettingsTest { public Context mContext; public PreferenceCategory mPreferenceCategory; - public boolean mIsAggregationEnabled = true; private TargetClass mNetworkSelectSettings; @@ -104,12 +103,13 @@ public class NetworkSelectSettingsTest { doReturn(mCellId2).when(mCellInfo2).getCellIdentity(); doReturn(mock(CellSignalStrength.class)).when(mCellInfo2).getCellSignalStrength(); doReturn(CARRIER_NAME2).when(mCellId2).getOperatorAlphaLong(); - mIsAggregationEnabled = true; mNetworkSelectSettings = spy(new TargetClass(this)); PersistableBundle config = new PersistableBundle(); config.putBoolean(CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true); - doReturn(config).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); + doReturn(config).when(mCarrierConfigManager).getConfigForSubId(SUB_ID, + CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, + CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL); doReturn(TelephonyManager.DATA_CONNECTED).when(mTelephonyManager).getDataState(); } @@ -174,11 +174,6 @@ public class NetworkSelectSettingsTest { return pref; } - @Override - protected boolean enableAggregation(Context context) { - return mTestEnv.mIsAggregationEnabled; - } - @Override protected int getSubId() { return SUB_ID; @@ -210,77 +205,7 @@ public class NetworkSelectSettingsTest { } @Test - public void doAggregation_hasDuplicateItemsDiffCellIdCase1_removeSamePlmnRatItem() { - mNetworkSelectSettings.onCreateInitialization(); - List testList = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createLteCellInfo(true, 1234, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB")); - List expected = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); - } - - @Test - public void doAggregation_hasDuplicateItemsDiffCellIdCase2_removeSamePlmnRatItem() { - mNetworkSelectSettings.onCreateInitialization(); - List testList = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB"), - createLteCellInfo(false, 1234, "123", "232", "CarrierB"), - createGsmCellInfo(false, 1234, "123", "232", "CarrierB")); - List expected = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB"), - createLteCellInfo(false, 1234, "123", "232", "CarrierB")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); - } - - @Test - public void doAggregation_hasDuplicateItemsDiffMccMncCase1_removeSamePlmnRatItem() { - mNetworkSelectSettings.onCreateInitialization(); - List testList = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createLteCellInfo(true, 123, "456", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB")); - List expected = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); - } - - @Test - public void doAggregation_hasDuplicateItemsDiffMccMncCase2_removeSamePlmnRatItem() { - mNetworkSelectSettings.onCreateInitialization(); - List testList = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB"), - createLteCellInfo(false, 1234, "123", "232", "CarrierB"), - createGsmCellInfo(false, 123, "456", "232", "CarrierB")); - List expected = Arrays.asList( - createLteCellInfo(true, 123, "123", "232", "CarrierA"), - createGsmCellInfo(false, 123, "123", "232", "CarrierB"), - createLteCellInfo(false, 1234, "123", "232", "CarrierB")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); - } - - @Test - public void doAggregation_hasDuplicateItemsDiffMccMncCase3_removeSamePlmnRatItem() { - mNetworkSelectSettings.onCreateInitialization(); - List testList = Arrays.asList( - createLteCellInfo(false, 123, "123", "232", "CarrierA"), - createLteCellInfo(false, 124, "123", "233", "CarrierA"), - createLteCellInfo(true, 125, "123", "234", "CarrierA"), - createGsmCellInfo(false, 126, "456", "232", "CarrierA")); - List expected = Arrays.asList( - createLteCellInfo(true, 125, "123", "234", "CarrierA"), - createGsmCellInfo(false, 126, "456", "232", "CarrierA")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); - } - - @Test - public void doAggregation_filterOutSatellitePlmn_whenKeyIsTrue() { + public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenKeyIsTrue() { PersistableBundle config = new PersistableBundle(); config.putBoolean( CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true); @@ -304,11 +229,11 @@ public class NetworkSelectSettingsTest { List expected = Arrays.asList( createGsmCellInfo(false, 123, "123", "233", "CarrierB"), createLteCellInfo(false, 1234, "123", "234", "CarrierC")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); + assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected); } @Test - public void doAggregation_filterOutSatellitePlmn_whenNoSatellitePlmnIsAvailable() { + public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenNoSatellitePlmnIsAvailable() { PersistableBundle config = new PersistableBundle(); config.putBoolean( CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true); @@ -336,17 +261,17 @@ public class NetworkSelectSettingsTest { createGsmCellInfo(false, 123, "123", "233", "CarrierB"), createLteCellInfo(false, 1234, "123", "234", "CarrierC"), createGsmCellInfo(false, 12345, "123", "235", "CarrierD")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); + assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected); // Expect no filter out when KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL is false. config.putBoolean( CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, false); mNetworkSelectSettings.onCreateInitialization(); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); + assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected); } @Test - public void doAggregation_filterOutSatellitePlmn_whenKeyIsFalse() { + public void filterOutSatellitePlmn_filterOutSatellitePlmn_whenKeyIsFalse() { PersistableBundle config = new PersistableBundle(); config.putBoolean( CarrierConfigManager.KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true); @@ -372,7 +297,7 @@ public class NetworkSelectSettingsTest { createGsmCellInfo(false, 123, "123", "233", "CarrierB"), createLteCellInfo(false, 1234, "123", "234", "CarrierC"), createGsmCellInfo(false, 12345, "123", "235", "CarrierD")); - assertThat(mNetworkSelectSettings.doAggregation(testList)).isEqualTo(expected); + assertThat(mNetworkSelectSettings.filterOutSatellitePlmn(testList)).isEqualTo(expected); } private CellInfoLte createLteCellInfo(boolean registered, int cellId, String mcc, String mnc,