Merge "Network selection add NGRAN when device support SA" into sc-dev
diff --git a/src/com/android/settings/network/telephony/NetworkScanHelper.java b/src/com/android/settings/network/telephony/NetworkScanHelper.java
index 524a737..e0a9c5e 100644
--- a/src/com/android/settings/network/telephony/NetworkScanHelper.java
+++ b/src/com/android/settings/network/telephony/NetworkScanHelper.java
@@ -21,11 +21,14 @@
 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.google.common.util.concurrent.FutureCallback;
@@ -37,6 +40,7 @@
 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;
@@ -111,10 +115,14 @@
     public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2;
 
     /** The constants below are used in the async network scan. */
-    private static final boolean INCREMENTAL_RESULTS = true;
-    private static final int SEARCH_PERIODICITY_SEC = 5;
-    private static final int MAX_SEARCH_TIME_SEC = 300;
-    private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
+    @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;
@@ -133,7 +141,8 @@
         mExecutor = executor;
     }
 
-    private NetworkScanRequest createNetworkScanForPreferredAccessNetworks() {
+    @VisibleForTesting
+    NetworkScanRequest createNetworkScanForPreferredAccessNetworks() {
         long networkTypeBitmap3gpp = mTelephonyManager.getPreferredNetworkTypeBitmask()
                 & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP;
 
@@ -161,14 +170,13 @@
         // 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.
         //
-        // FIXME(b/151119451): re-enable this code once there is a way to distinguish SA from NSA
-        // support in the modem.
-        //
-        // if (networkTypeBitmap3gpp == 0
-        //        || (networkTypeBitmap3gpp & TelephonyManager.NETWORK_CLASS_BITMASK_5G) != 0) {
-        //    radioAccessSpecifiers.add(
-        //            new RadioAccessSpecifier(AccessNetworkType.NGRAN, null, null));
-        // }
+        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,
@@ -253,6 +261,12 @@
         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}.
diff --git a/tests/robotests/src/com/android/settings/network/telephony/NetworkScanHelperTest.java b/tests/robotests/src/com/android/settings/network/telephony/NetworkScanHelperTest.java
deleted file mode 100644
index 1e7bfaf..0000000
--- a/tests/robotests/src/com/android/settings/network/telephony/NetworkScanHelperTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2019 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.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.CellInfo;
-import android.telephony.NetworkScan;
-import android.telephony.NetworkScanRequest;
-import android.telephony.TelephonyManager;
-import android.telephony.TelephonyScanManager;
-
-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 org.robolectric.RobolectricTestRunner;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-@RunWith(RobolectricTestRunner.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;
-
-    private 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();
-    }
-
-    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/NetworkScanHelperTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java
new file mode 100644
index 0000000..f046c9a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java
@@ -0,0 +1,260 @@
+/*
+ * 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();
+        }
+    }
+
+}