Merge "Fix NPE in AudioSwitchPreferenceController" into 24D1-dev
diff --git a/res/layout/face_enroll_button.xml b/res/layout/face_enroll_button.xml
index 6266650..73fbd77 100644
--- a/res/layout/face_enroll_button.xml
+++ b/res/layout/face_enroll_button.xml
@@ -27,6 +27,7 @@
         style="@style/SudGlifButton.Primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginStart="0dp"
         android:layout_gravity="start"
         android:text="@string/security_settings_face_settings_enroll"/>
 
diff --git a/res/layout/face_remove_button.xml b/res/layout/face_remove_button.xml
index 2c2497a..f281961 100644
--- a/res/layout/face_remove_button.xml
+++ b/res/layout/face_remove_button.xml
@@ -27,6 +27,7 @@
         style="@style/SudGlifButton.Primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginStart="0dp"
         android:layout_gravity="start"
         android:text="@string/security_settings_face_settings_remove_face_model"/>
 
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index ec63c9a..42379b4 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -59,9 +59,8 @@
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mBatteryUsageProgressBarPref = screen.findPreference(getPreferenceKey());
-        // Set up loading text first to prevent layout flaky before info loaded.
-        mBatteryUsageProgressBarPref.setBottomSummary(
-                mContext.getString(R.string.settings_license_activity_loading));
+        // Set up empty space text first to prevent layout flaky before info loaded.
+        mBatteryUsageProgressBarPref.setBottomSummary(" ");
 
         if (com.android.settings.Utils.isBatteryPresent(mContext)) {
             quickUpdateHeaderPreference();
diff --git a/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java
index eb833b1..b56c1b9 100644
--- a/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java
+++ b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java
@@ -31,6 +31,10 @@
         super(context, preferenceKey, lifecycle, lifecycleOwner);
     }
 
+    public CallsDefaultSubscriptionController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
     @Override
     protected int getDefaultSubscriptionId() {
         int defaultCallSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
diff --git a/src/com/android/settings/network/telephony/CellInfoUtil.kt b/src/com/android/settings/network/telephony/CellInfoUtil.kt
index c7b6b24..51f60e7 100644
--- a/src/com/android/settings/network/telephony/CellInfoUtil.kt
+++ b/src/com/android/settings/network/telephony/CellInfoUtil.kt
@@ -82,7 +82,7 @@
      */
     @JvmStatic
     fun cellInfoListToString(cellInfos: List<CellInfo>): 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/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
index 08e993b..21cceb5 100644
--- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
@@ -69,15 +69,19 @@
 
     public ConvertToEsimPreferenceController(Context context, String key, Lifecycle lifecycle,
             LifecycleOwner lifecycleOwner, int subId) {
-        super(context, key);
+        this(context, key);
         mSubId = subId;
-        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
         mLifecycleOwner = lifecycleOwner;
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
     }
 
+    public ConvertToEsimPreferenceController(Context context, String key) {
+        super(context, key);
+        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
+    }
+
     public void init(int subId, SubscriptionInfoEntity subInfoEntity) {
         mSubId = subId;
         mSubscriptionInfoEntity = subInfoEntity;
diff --git a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
index 96c39f2..e5a4b46 100644
--- a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
+++ b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
@@ -64,16 +64,20 @@
 
     public DefaultSubscriptionController(Context context, String preferenceKey, Lifecycle lifecycle,
             LifecycleOwner lifecycleOwner) {
+        this(context, preferenceKey);
+        mLifecycleOwner = lifecycleOwner;
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    public DefaultSubscriptionController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mManager = context.getSystemService(SubscriptionManager.class);
         mIsRtlMode = context.getResources().getConfiguration().getLayoutDirection()
                 == View.LAYOUT_DIRECTION_RTL;
         mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
         mDataSubscriptionChangedReceiver = new DefaultSubscriptionReceiver(context, this);
-        mLifecycleOwner = lifecycleOwner;
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
     }
 
     /** @return the id of the default subscription for the service, or
diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
index bec7ee7..28c05c5 100644
--- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
+++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
@@ -72,16 +72,20 @@
 
     public MobileDataPreferenceController(Context context, String key, Lifecycle lifecycle,
             LifecycleOwner lifecycleOwner, int subId) {
-        super(context, key);
+        this(context, key);
         mSubId = subId;
-        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
-        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
         mLifecycleOwner = lifecycleOwner;
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
     }
 
+    public MobileDataPreferenceController(Context context, String key) {
+        super(context, key);
+        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
+    }
+
     @Override
     public int getAvailabilityStatus(int subId) {
         if (Flags.isDualSimOnboardingEnabled()) {
diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
index 603d915..db95d70 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
@@ -708,12 +708,13 @@
         return tm.getNetworkOperatorName();
     }
 
-    private static int[] getActiveSubscriptionIdList(Context context) {
+    @VisibleForTesting
+    static int[] getActiveSubscriptionIdList(Context context) {
         final SubscriptionManager subscriptionManager = context.getSystemService(
                 SubscriptionManager.class).createForAllUserProfiles();
         final List<SubscriptionInfo> subInfoList =
-                subscriptionManager.getActiveSubscriptionInfoList();
-        if (subInfoList == null) {
+                SubscriptionUtil.getActiveSubscriptions(subscriptionManager);
+        if (subInfoList == null || subInfoList.isEmpty()) {
             return new int[0];
         }
         int[] activeSubIds = new int[subInfoList.size()];
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 1961329..0000000
--- 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.
-         *
-         * <p> 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<CellInfo> 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.
-         *
-         * <p> There is no more result returned from {@link TelephonyManager} if an error occurred.
-         *
-         * <p> {@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.
-     *
-     * <p> 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)}.
-     *
-     * <p> 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<List<CellInfo>> 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<RadioAccessSpecifier> 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<List<CellInfo>>() {
-                @Override
-                public void onSuccess(List<CellInfo> 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<CellInfo> 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<CellInfo> 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<List<CellInfo>> mCallback;
-        private final TelephonyManager mTelephonyManager;
-
-        NetworkScanSyncTask(
-                TelephonyManager telephonyManager, SettableFuture<List<CellInfo>> callback) {
-            mTelephonyManager = telephonyManager;
-            mCallback = callback;
-        }
-
-        @Override
-        public void run() {
-            final CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks();
-            if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) {
-                final List<CellInfo> 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 c8617af..5995a13 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.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,18 @@
 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.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.collect.ImmutableList;
+
+import kotlin.Unit;
+
 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 +75,8 @@
     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
@@ -84,25 +84,21 @@
     private View mProgressHeader;
     private Preference mStatusMessagePreference;
     @VisibleForTesting
-    List<CellInfo> mCellInfoList;
+    @NonNull
+    List<CellInfo> mCellInfoList = ImmutableList.of();
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private TelephonyManager mTelephonyManager;
     private SatelliteManager mSatelliteManager;
     private CarrierConfigManager mCarrierConfigManager;
     private List<String> 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;
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -114,7 +110,6 @@
     @Initializer
     protected void onCreateInitialization() {
         Context context = getContext();
-        mUseNewApi = enableNewAutoSelectNetworkUI(context);
         mSubId = getSubId();
 
         mPreferenceCategory = getPreferenceCategory(PREF_KEY_NETWORK_OPERATORS);
@@ -124,8 +119,6 @@
         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 +129,13 @@
                 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,30 +194,25 @@
     }
 
     @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();
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
+    private void launchNetworkScan() {
+        setProgressBarVisible(true);
+        mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> {
+            if (isPreferenceScreenEnabled()) {
+                scanResultHandler(networkScanResult);
+            }
 
-        updateForbiddenPlmns();
-        if (isProgressBarVisible()) {
-            return;
-        }
-        if (mWaitingForNumberOfScanResults <= 0) {
-            startNetworkQuery();
-        }
+            return Unit.INSTANCE;
+        });
     }
 
     /**
@@ -257,14 +228,6 @@
     }
 
     @Override
-    public void onStop() {
-        if (mWaitingForNumberOfScanResults <= 0) {
-            stopNetworkQuery();
-        }
-        super.onStop();
-    }
-
-    @Override
     public boolean onPreferenceTreeClick(Preference preference) {
         if (preference == mSelectedPreference) {
             Log.d(TAG, "onPreferenceTreeClick: preference is mSelectedPreference. Do nothing.");
@@ -275,8 +238,6 @@
             return false;
         }
 
-        stopNetworkQuery();
-
         // Refresh the last selected item in case users reselect network.
         clearPreferenceSummary();
         if (mSelectedPreference != null) {
@@ -294,8 +255,6 @@
         // 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 +288,6 @@
             switch (msg.what) {
                 case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE:
                     final boolean isSucceed = (boolean) msg.obj;
-                    stopNetworkQuery();
                     setProgressBarVisible(false);
                     enablePreferenceScreen(true);
 
@@ -341,86 +299,15 @@
                         Log.e(TAG, "No preference to update!");
                     }
                     break;
-                case EVENT_NETWORK_SCAN_RESULTS:
-                    scanResultHandler((List<CellInfo>) 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<CellInfo> doAggregation(List<CellInfo> cellInfoListInput) {
-        if (!mIsAggregationEnabled) {
-            Log.d(TAG, "no aggregation");
-            return new ArrayList<>(cellInfoListInput);
-        }
-        ArrayList<CellInfo> aggregatedList = new ArrayList<>();
-        for (CellInfo cellInfo : cellInfoListInput) {
-            String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity());
-            Class className = cellInfo.getClass();
-
-            Optional<CellInfo> 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<CellInfo> filterOutSatellitePlmn(List<CellInfo> cellInfoList) {
+    @VisibleForTesting
+    List<CellInfo> filterOutSatellitePlmn(List<CellInfo> cellInfoList) {
         List<String> aggregatedSatellitePlmn = getSatellitePlmnsForCarrierWrapper();
         if (!mShouldFilterOutSatellitePlmn.get() || aggregatedSatellitePlmn.isEmpty()) {
             return cellInfoList;
@@ -455,56 +342,19 @@
         }
     }
 
-    private final NetworkScanHelper.NetworkScanCallback mCallback =
-            new NetworkScanHelper.NetworkScanCallback() {
-                public void onResults(List<CellInfo> 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<CellInfo> results) {
-        if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) {
-            Log.d(TAG, "CellInfoList (drop): "
-                    + CellInfoUtil.cellInfoListToString(new ArrayList<>(results)));
-            return;
-        }
-        mWaitingForNumberOfScanResults--;
-        if ((mWaitingForNumberOfScanResults <= 0) && (!isResumed())) {
-            stopNetworkQuery();
-        }
-
-        mCellInfoList = doAggregation(results);
+    protected void scanResultHandler(NetworkScanRepository.NetworkScanResult results) {
+        mCellInfoList = filterOutSatellitePlmn(results.getCellInfos());
         Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList));
-        if (mCellInfoList != null && mCellInfoList.size() != 0) {
-            final NetworkOperatorPreference connectedPref = updateAllPreferenceCategory();
-            if (connectedPref != null) {
-                // update selected preference instance into connected preference
-                if (mSelectedPreference != null) {
-                    mSelectedPreference = connectedPref;
-                }
-            } else if (!isPreferenceScreenEnabled()) {
-                mSelectedPreference.setSummary(R.string.network_connecting);
-            }
-            enablePreferenceScreen(true);
-        } else if (isPreferenceScreenEnabled()) {
+        updateAllPreferenceCategory();
+        NetworkScanRepository.NetworkScanState state = results.getState();
+        if (state == NetworkScanRepository.NetworkScanState.ERROR) {
+            addMessagePreference(R.string.network_query_error);
+        } else if (mCellInfoList.isEmpty()) {
             addMessagePreference(R.string.empty_networks_list);
-            // keep showing progress bar, it will be stopped when error or completed
-            setProgressBarVisible(true);
         }
+        // keep showing progress bar, it will be stopped when error or completed
+        setProgressBarVisible(state == NetworkScanRepository.NetworkScanState.ACTIVE);
     }
 
     @Keep
@@ -521,11 +371,8 @@
 
     /**
      * Update the content of network operators list.
-     *
-     * @return preference which shows connected
      */
-    @Nullable
-    private NetworkOperatorPreference updateAllPreferenceCategory() {
+    private void updateAllPreferenceCategory() {
         int numberOfPreferences = mPreferenceCategory.getPreferenceCount();
 
         // remove unused preferences
@@ -536,7 +383,6 @@
         }
 
         // update the content of preference
-        NetworkOperatorPreference connectedPref = null;
         for (int index = 0; index < mCellInfoList.size(); index++) {
             final CellInfo cellInfo = mCellInfoList.get(index);
 
@@ -561,23 +407,10 @@
 
             if (mCellInfoList.get(index).isRegistered()) {
                 pref.setSummary(R.string.network_connected);
-                connectedPref = pref;
             } else {
                 pref.setSummary(null);
             }
         }
-
-        // update selected preference instance by index
-        for (int index = 0; index < mCellInfoList.size(); index++) {
-            final CellInfo cellInfo = mCellInfoList.get(index);
-
-            if ((mSelectedPreference != null) && mSelectedPreference.isSameCell(cellInfo)) {
-                mSelectedPreference = (NetworkOperatorPreference)
-                        (mPreferenceCategory.getPreference(index));
-            }
-        }
-
-        return connectedPref;
     }
 
     /**
@@ -646,18 +479,6 @@
         }
     }
 
-    private long getNewRequestId() {
-        return Math.max(mRequestIdManualNetworkSelect,
-                mRequestIdManualNetworkScan) + 1;
-    }
-
-    private boolean isProgressBarVisible() {
-        if (mProgressHeader == null) {
-            return false;
-        }
-        return (mProgressHeader.getVisibility() == View.VISIBLE);
-    }
-
     protected void setProgressBarVisible(boolean visible) {
         if (mProgressHeader != null) {
             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -665,35 +486,13 @@
     }
 
     private void addMessagePreference(int messageId) {
-        setProgressBarVisible(false);
         mStatusMessagePreference.setTitle(messageId);
         mPreferenceCategory.removeAll();
         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/RoamingPreferenceController.java b/src/com/android/settings/network/telephony/RoamingPreferenceController.java
index fb8cd51..bf02308 100644
--- a/src/com/android/settings/network/telephony/RoamingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.java
@@ -63,16 +63,20 @@
 
     public RoamingPreferenceController(Context context, String key, Lifecycle lifecycle,
             LifecycleOwner lifecycleOwner, int subId) {
-        super(context, key);
+        this(context, key);
         mSubId = subId;
-        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
-        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
         mLifecycleOwner = lifecycleOwner;
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
     }
 
+    public RoamingPreferenceController(Context context, String key) {
+        super(context, key);
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
+    }
+
     @Override
     public int getAvailabilityStatus() {
         final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
diff --git a/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java
index c49647d..c35a78c 100644
--- a/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java
+++ b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java
@@ -35,6 +35,12 @@
                 .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support);
     }
 
+    public SmsDefaultSubscriptionController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mIsAskEverytimeSupported = mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support);
+    }
+
     @Override
     protected int getDefaultSubscriptionId() {
         int defaultSmsSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
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 0000000..0344002
--- /dev/null
+++ b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
@@ -0,0 +1,177 @@
+/*
+ * 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.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.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEach
+
+class NetworkScanRepository(context: Context, subId: Int) {
+    enum class NetworkScanState {
+        ACTIVE, COMPLETE, ERROR
+    }
+
+    data class NetworkScanResult(
+        val state: NetworkScanState,
+        val cellInfos: List<CellInfo>,
+    )
+
+    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<NetworkScanResult> = callbackFlow {
+        var state = NetworkScanState.ACTIVE
+        var cellInfos: List<CellInfo> = emptyList()
+
+        val callback = object : TelephonyScanManager.NetworkScanCallback() {
+            override fun onResults(results: List<CellInfo>) {
+                cellInfos = results.distinctBy { CellInfoScanKey(it) }
+                sendResult()
+            }
+
+            override fun onComplete() {
+                state = NetworkScanState.COMPLETE
+                sendResult()
+                // Don't call close() here since onComplete() could happens before onResults()
+            }
+
+            override fun onError(error: Int) {
+                state = NetworkScanState.ERROR
+                sendResult()
+                close()
+            }
+
+            private fun sendResult() {
+                trySend(NetworkScanResult(state, cellInfos))
+            }
+        }
+
+        val networkScan = telephonyManager.requestNetworkScan(
+            createNetworkScan(),
+            Dispatchers.Default.asExecutor(),
+            callback,
+        )
+
+        awaitClose { networkScan.stopScan() }
+    }.conflate().onEach { Log.d(TAG, "networkScanFlow: $it") }.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<Int> {
+        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/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index 9fa9651..1b8d24e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -35,7 +35,6 @@
 
 import androidx.preference.PreferenceScreen;
 
-import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
@@ -374,11 +373,10 @@
     }
 
     @Test
-    public void displayPreference_init_showLoading() {
+    public void displayPreference_init_showEmptySpace() {
         mController.displayPreference(mPreferenceScreen);
 
-        verify(mBatteryUsageProgressBarPref)
-                .setBottomSummary(mContext.getString(R.string.settings_license_activity_loading));
+        verify(mBatteryUsageProgressBarPref).setBottomSummary(" ");
     }
 
     private CharSequence formatBatteryPercentageText() {
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 0000000..c0b918f
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt
@@ -0,0 +1,279 @@
+/*
+ * 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.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<TelephonyManager> {
+        on { createForSubscriptionId(SUB_ID) } doReturn mock
+        on { requestNetworkScan(any(), any(), any()) } doAnswer {
+            callback = it.arguments[2] as TelephonyScanManager.NetworkScanCallback
+            mock<NetworkScan>()
+        }
+    }
+
+    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(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = cellInfos,
+            )
+        )
+    }
+
+    @Test
+    fun networkScanFlow_onComplete(): Unit = runBlocking {
+        val listDeferred = async {
+            repository.networkScanFlow().toListWithTimeout()
+        }
+        delay(100)
+
+        callback?.onComplete()
+
+        assertThat(listDeferred.await()).containsExactly(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.COMPLETE,
+                cellInfos = emptyList(),
+            )
+        )
+    }
+
+    @Test
+    fun networkScanFlow_onError(): Unit = runBlocking {
+        val listDeferred = async {
+            repository.networkScanFlow().toListWithTimeout()
+        }
+        delay(100)
+
+        callback?.onError(1)
+
+        assertThat(listDeferred.await()).containsExactly(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ERROR,
+                cellInfos = emptyList(),
+            )
+        )
+    }
+
+    @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(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = 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(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = 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<NetworkScanRequest> {
+            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<NetworkScanRequest> {
+            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/MobileNetworkUtilsTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java
index 947ba75..570a320 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java
@@ -240,6 +240,33 @@
     }
 
     @Test
+    public void getActiveSubscriptionIdList_nonActive_returnEmptyArray() {
+        int[] expectedList = new int[0];
+        when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(new ArrayList<>());
+
+        assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext))
+                .isEqualTo(expectedList);
+    }
+
+    @Test
+    public void getActiveSubscriptionIdList_normalCaseTwoActiveSims_returnValidSubId() {
+        int[] expectedList = {SUB_ID_1, SUB_ID_2};
+
+        assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext))
+                .isEqualTo(expectedList);
+    }
+
+    @Test
+    public void getActiveSubscriptionIdList_TwoActiveSimsAndOneIsNtn_returnOneSubId() {
+        int[] expectedList = {SUB_ID_2};
+        when(mSubscriptionInfo1.isEmbedded()).thenReturn(true);
+        when(mSubscriptionInfo1.isOnlyNonTerrestrialNetwork()).thenReturn(true);
+
+        assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext))
+                .isEqualTo(expectedList);
+    }
+
+    @Test
     public void shouldDisplayNetworkSelectOptions_HideCarrierNetwork_returnFalse() {
         mCarrierConfig.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL,
                 true);
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 f046c9a..0000000
--- 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<CellInfo> 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<List<CellInfo>> argument = ArgumentCaptor.forClass(List.class);
-
-        startNetworkScan_incremental(true);
-
-        verify(mNetworkScanCallback, times(1)).onResults(argument.capture());
-        List<CellInfo> 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<RadioAccessSpecifier> 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<RadioAccessSpecifier> 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<ModemInfo> logicalModemList = new ArrayList<>();
-        logicalModemList.add(modemInfo);
-        return new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
-                logicalModemList, false, deviceNrCapabilities);
-    }
-
-    private NetworkScanRequest createNetworkScanRequest(
-            List<RadioAccessSpecifier> 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 080534e..d71af84 100644
--- a/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java
@@ -44,8 +44,12 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.network.telephony.scan.NetworkScanRepository;
+import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanResult;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -83,7 +87,6 @@
 
     public Context mContext;
     public PreferenceCategory mPreferenceCategory;
-    public boolean mIsAggregationEnabled = true;
 
     private TargetClass mNetworkSelectSettings;
 
@@ -104,12 +107,13 @@
         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();
     }
@@ -163,8 +167,7 @@
         }
 
         @Override
-        protected NetworkOperatorPreference
-                createNetworkOperatorPreference(CellInfo cellInfo) {
+        protected NetworkOperatorPreference createNetworkOperatorPreference(CellInfo cellInfo) {
             NetworkOperatorPreference pref = super.createNetworkOperatorPreference(cellInfo);
             if (cellInfo == mTestEnv.mCellInfo1) {
                 pref.updateCell(cellInfo, mTestEnv.mCellId1);
@@ -175,11 +178,6 @@
         }
 
         @Override
-        protected boolean enableAggregation(Context context) {
-            return mTestEnv.mIsAggregationEnabled;
-        }
-
-        @Override
         protected int getSubId() {
             return SUB_ID;
         }
@@ -188,9 +186,14 @@
     @Test
     @UiThreadTest
     public void updateAllPreferenceCategory_correctOrderingPreference() {
+        NetworkScanResult result = new NetworkScanResult(
+                NetworkScanRepository.NetworkScanState.COMPLETE,
+                ImmutableList.of(mCellInfo1, mCellInfo2));
         mNetworkSelectSettings.onCreateInitialization();
         mNetworkSelectSettings.enablePreferenceScreen(true);
-        mNetworkSelectSettings.scanResultHandler(Arrays.asList(mCellInfo1, mCellInfo2));
+
+        mNetworkSelectSettings.scanResultHandler(result);
+
         assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
         final NetworkOperatorPreference preference =
                 (NetworkOperatorPreference) mPreferenceCategory.getPreference(1);
@@ -210,77 +213,7 @@
     }
 
     @Test
-    public void doAggregation_hasDuplicateItemsDiffCellIdCase1_removeSamePlmnRatItem() {
-        mNetworkSelectSettings.onCreateInitialization();
-        List<CellInfo> testList = Arrays.asList(
-                createLteCellInfo(true, 123, "123", "232", "CarrierA"),
-                createLteCellInfo(true, 1234, "123", "232", "CarrierA"),
-                createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
-        List<CellInfo> 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<CellInfo> 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<CellInfo> 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<CellInfo> testList = Arrays.asList(
-                createLteCellInfo(true, 123, "123", "232", "CarrierA"),
-                createLteCellInfo(true, 123, "456", "232", "CarrierA"),
-                createGsmCellInfo(false, 123, "123", "232", "CarrierB"));
-        List<CellInfo> 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<CellInfo> 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<CellInfo> 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<CellInfo> 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<CellInfo> 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 +237,11 @@
         List<CellInfo> 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 +269,17 @@
                 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 +305,7 @@
                 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,