xref: /aosp_15_r20/cts/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.carrierapi.cts;
17 
18 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
19 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import static org.junit.Assert.fail;
25 
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.PackageManager;
32 import android.location.LocationManager;
33 import android.os.AsyncTask;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.os.Message;
37 import android.os.Parcel;
38 import android.os.Process;
39 import android.os.UserHandle;
40 import android.telephony.AccessNetworkConstants;
41 import android.telephony.CellInfo;
42 import android.telephony.CellInfoGsm;
43 import android.telephony.CellInfoLte;
44 import android.telephony.CellInfoWcdma;
45 import android.telephony.NetworkScan;
46 import android.telephony.NetworkScanRequest;
47 import android.telephony.RadioAccessSpecifier;
48 import android.telephony.TelephonyManager;
49 import android.telephony.TelephonyScanManager;
50 import android.util.Log;
51 
52 import androidx.test.InstrumentationRegistry;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.List;
63 import java.util.concurrent.CountDownLatch;
64 import java.util.concurrent.TimeUnit;
65 import java.util.stream.Collectors;
66 
67 /**
68  * Unit tests for {@link TelephonyManager}'s network scan APIs.
69  *
70  * <p>Test using `atest CtsCarrierApiTestCases:NetworkScanApiTest` or `make cts -j64 && cts-tradefed
71  * run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.NetworkScanApiTest`
72  */
73 @RunWith(AndroidJUnit4.class)
74 public class NetworkScanApiTest extends BaseCarrierApiTest {
75     private static final String TAG = "NetworkScanApiTest";
76 
77     private TelephonyManager mTelephonyManager;
78     private int mNetworkScanStatus;
79     private static final int EVENT_NETWORK_SCAN_START = 100;
80     private static final int EVENT_NETWORK_SCAN_RENOUNCE_START = 101;
81     private static final int EVENT_NETWORK_SCAN_RESULTS = 200;
82     private static final int EVENT_NETWORK_SCAN_RESTRICTED_RESULTS = 201;
83     private static final int EVENT_NETWORK_SCAN_ERROR = 300;
84     private static final int EVENT_NETWORK_SCAN_COMPLETED = 400;
85     private static final int EVENT_SCAN_DENIED = 500;
86     private List<CellInfo> mScanResults = null;
87     private HandlerThread mTestHandlerThread;
88     private Handler mHandler;
89     private NetworkScan mNetworkScan;
90     private NetworkScanRequest mNetworkScanRequest;
91     private NetworkScanCallbackImpl mNetworkScanCallback;
92     private static final int LOCATION_SETTING_CHANGE_WAIT_MS = 1000;
93     private static final int MAX_CELLINFO_WAIT_MILLIS = 5000; // 5 seconds
94     private static final int SCAN_SEARCH_TIME_SECONDS = 60;
95     // Wait one second longer than the max scan search time to give the test time to receive the
96     // results.
97     private static final int MAX_INIT_WAIT_MS = (SCAN_SEARCH_TIME_SECONDS + 1) * 1000;
98     private Object mLock = new Object();
99     private boolean mReady;
100     private int mErrorCode;
101     /* All the following constants are used to construct NetworkScanRequest*/
102     private static final int SCAN_TYPE = NetworkScanRequest.SCAN_TYPE_ONE_SHOT;
103     private static final boolean INCREMENTAL_RESULTS = true;
104     private static final int SEARCH_PERIODICITY_SEC = 5;
105     private static final int MAX_SEARCH_TIME_SEC = 300;
106     private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
107     private static final ArrayList<String> MCC_MNC = new ArrayList<>();
108     private static final RadioAccessSpecifier[] RADIO_ACCESS_SPECIFIERS = {
109         new RadioAccessSpecifier(
110                 AccessNetworkConstants.AccessNetworkType.GERAN,
111                 null /* bands */,
112                 null /* channels */),
113         new RadioAccessSpecifier(
114                 AccessNetworkConstants.AccessNetworkType.EUTRAN,
115                 null /* bands */,
116                 null /* channels */),
117         new RadioAccessSpecifier(
118                 AccessNetworkConstants.AccessNetworkType.UTRAN,
119                 null /* bands */,
120                 null /* channels */)
121     };
122 
123     // Needed because NETWORK_SCAN_PERMISSION is a systemapi
124     public static final String NETWORK_SCAN_PERMISSION = "android.permission.NETWORK_SCAN";
125 
126     @Before
setUp()127     public void setUp() throws Exception {
128         mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
129         String selfPackageName = getContext().getPackageName();
130         InstrumentationRegistry.getInstrumentation()
131                 .getUiAutomation()
132                 .grantRuntimePermission(selfPackageName, ACCESS_FINE_LOCATION);
133         InstrumentationRegistry.getInstrumentation()
134                 .getUiAutomation()
135                 .grantRuntimePermission(selfPackageName, ACCESS_BACKGROUND_LOCATION);
136         mTestHandlerThread = new HandlerThread(TAG);
137         mTestHandlerThread.start();
138         /* create a custom handler for the Handler Thread */
139         mHandler =
140                 new Handler(mTestHandlerThread.getLooper()) {
141                     @Override
142                     public void handleMessage(Message msg) {
143                         switch (msg.what) {
144                             case EVENT_NETWORK_SCAN_START: {
145                                 Log.d(TAG, "request network scan");
146                                 boolean useShellIdentity = (Boolean) msg.obj;
147                                 if (useShellIdentity) {
148                                     InstrumentationRegistry.getInstrumentation()
149                                             .getUiAutomation()
150                                             .adoptShellPermissionIdentity();
151                                 }
152                                 try {
153                                     mNetworkScan =
154                                             mTelephonyManager.requestNetworkScan(
155                                                     mNetworkScanRequest,
156                                                     AsyncTask.SERIAL_EXECUTOR,
157                                                     mNetworkScanCallback);
158                                     if (mNetworkScan == null) {
159                                         mNetworkScanStatus = EVENT_SCAN_DENIED;
160                                         setReady(true);
161                                     }
162                                 } catch (SecurityException e) {
163                                     mNetworkScanStatus = EVENT_SCAN_DENIED;
164                                     setReady(true);
165                                 } finally {
166                                     if (useShellIdentity) {
167                                         InstrumentationRegistry.getInstrumentation()
168                                                 .getUiAutomation()
169                                                 .dropShellPermissionIdentity();
170                                     }
171                                 }
172                                 break;
173                             }
174                             case EVENT_NETWORK_SCAN_RENOUNCE_START: {
175                                 Log.d(TAG, "request network scan with renounce");
176                                 boolean useShellIdentity = (Boolean) msg.obj;
177                                 if (useShellIdentity) {
178                                     InstrumentationRegistry.getInstrumentation()
179                                             .getUiAutomation()
180                                             .adoptShellPermissionIdentity();
181                                 }
182                                 try {
183                                     mNetworkScan = mTelephonyManager.requestNetworkScan(
184                                             TelephonyManager.INCLUDE_LOCATION_DATA_NONE,
185                                             mNetworkScanRequest,
186                                             AsyncTask.SERIAL_EXECUTOR,
187                                             mNetworkScanCallback);
188                                     if (mNetworkScan == null) {
189                                         mNetworkScanStatus = EVENT_SCAN_DENIED;
190                                         setReady(true);
191                                     }
192                                 } catch (SecurityException e) {
193                                     mNetworkScanStatus = EVENT_SCAN_DENIED;
194                                     setReady(true);
195                                 } finally {
196                                     if (useShellIdentity) {
197                                         InstrumentationRegistry.getInstrumentation()
198                                                 .getUiAutomation()
199                                                 .dropShellPermissionIdentity();
200                                     }
201                                 }
202                                 break;
203                             }
204                             default:
205                                 Log.d(TAG, "Unknown Event " + msg.what);
206                         }
207                     }
208                 };
209     }
210 
211     @After
tearDown()212     public void tearDown() throws Exception {
213         if (!werePreconditionsSatisfied()) return;
214 
215         // Revoking runtime permissions makes ActivityManager kill our process, so we don't do it,
216         // as the test harness will eventually uninstall this APK after testing completes anyway, so
217         // we aren't really leaking anything long-term.
218         mTestHandlerThread.quit();
219     }
220 
waitUntilReady()221     private void waitUntilReady() {
222         synchronized (mLock) {
223             try {
224                 mLock.wait(MAX_INIT_WAIT_MS);
225             } catch (InterruptedException ie) {
226             }
227 
228             assertWithMessage("NetworkScanApiTest failed to initialize").that(mReady).isTrue();
229         }
230     }
231 
setReady(boolean ready)232     private void setReady(boolean ready) {
233         synchronized (mLock) {
234             mReady = ready;
235             mLock.notifyAll();
236         }
237     }
238 
239     private class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback {
240         @Override
onResults(List<CellInfo> results)241         public void onResults(List<CellInfo> results) {
242             Log.d(TAG, "onResults: " + results.toString());
243             mNetworkScanStatus = EVENT_NETWORK_SCAN_RESULTS;
244             mScanResults = results;
245         }
246 
247         @Override
onComplete()248         public void onComplete() {
249             Log.d(TAG, "onComplete");
250             mNetworkScanStatus = EVENT_NETWORK_SCAN_COMPLETED;
251             setReady(true);
252         }
253 
254         @Override
onError(int error)255         public void onError(int error) {
256             Log.d(TAG, "onError: " + String.valueOf(error));
257             mNetworkScanStatus = EVENT_NETWORK_SCAN_ERROR;
258             mErrorCode = error;
259             setReady(true);
260         }
261     }
262 
263     private class CellInfoResultsCallback extends TelephonyManager.CellInfoCallback {
264         public List<CellInfo> cellInfo;
265 
266         @Override
onCellInfo(List<CellInfo> cellInfo)267         public synchronized void onCellInfo(List<CellInfo> cellInfo) {
268             this.cellInfo = cellInfo;
269             notifyAll();
270         }
271 
wait(int millis)272         public synchronized void wait(int millis) throws InterruptedException {
273             if (cellInfo == null) {
274                 super.wait(millis);
275             }
276         }
277     }
278 
getRadioAccessSpecifier(List<CellInfo> allCellInfo)279     private List<RadioAccessSpecifier> getRadioAccessSpecifier(List<CellInfo> allCellInfo) {
280         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
281         List<Integer> lteChannels = new ArrayList<>();
282         List<Integer> wcdmaChannels = new ArrayList<>();
283         List<Integer> gsmChannels = new ArrayList<>();
284         for (int i = 0; i < allCellInfo.size(); i++) {
285             CellInfo cellInfo = allCellInfo.get(i);
286             if (cellInfo instanceof CellInfoLte) {
287                 lteChannels.add(((CellInfoLte) cellInfo).getCellIdentity().getEarfcn());
288             } else if (cellInfo instanceof CellInfoWcdma) {
289                 wcdmaChannels.add(((CellInfoWcdma) cellInfo).getCellIdentity().getUarfcn());
290             } else if (cellInfo instanceof CellInfoGsm) {
291                 gsmChannels.add(((CellInfoGsm) cellInfo).getCellIdentity().getArfcn());
292             }
293         }
294         if (!lteChannels.isEmpty()) {
295             Log.d(TAG, "lte channels" + lteChannels.toString());
296             int ranLte = AccessNetworkConstants.AccessNetworkType.EUTRAN;
297             radioAccessSpecifier.add(
298                     new RadioAccessSpecifier(
299                             ranLte,
300                             null /* bands */,
301                             lteChannels.stream().mapToInt(i -> i).toArray()));
302         }
303         if (!wcdmaChannels.isEmpty()) {
304             Log.d(TAG, "wcdma channels" + wcdmaChannels.toString());
305             int ranWcdma = AccessNetworkConstants.AccessNetworkType.UTRAN;
306             radioAccessSpecifier.add(
307                     new RadioAccessSpecifier(
308                             ranWcdma,
309                             null /* bands */,
310                             wcdmaChannels.stream().mapToInt(i -> i).toArray()));
311         }
312         if (!gsmChannels.isEmpty()) {
313             Log.d(TAG, "gsm channels" + gsmChannels.toString());
314             int ranGsm = AccessNetworkConstants.AccessNetworkType.GERAN;
315             radioAccessSpecifier.add(
316                     new RadioAccessSpecifier(
317                             ranGsm,
318                             null /* bands */,
319                             gsmChannels.stream().mapToInt(i -> i).toArray()));
320         }
321         return radioAccessSpecifier;
322     }
323 
324     /** Tests that the device properly requests a network scan. */
325     @Test
testRequestNetworkScan()326     public void testRequestNetworkScan() {
327         boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
328         try {
329             mNetworkScanRequest = buildNetworkScanRequest(true);
330             mNetworkScanCallback = new NetworkScanCallbackImpl();
331             Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START, false);
332             setReady(false);
333             startNetworkScan.sendToTarget();
334             waitUntilReady();
335 
336             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
337             assertWithMessage(
338                             "The final scan status is "
339                                     + mNetworkScanStatus
340                                     + " with error code "
341                                     + mErrorCode
342                                     + ", not ScanCompleted"
343                                     + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
344                                     + " ERROR_UNSUPPORTED")
345                     .that(isScanStatusValid())
346                     .isTrue();
347         } finally {
348             getAndSetLocationSwitch(isLocationSwitchOn);
349         }
350     }
351 
352     /** Tests that the device properly requests a network scan. */
353     @Test
testRequestNetworkScanWithRenounce()354     public void testRequestNetworkScanWithRenounce() {
355         boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
356         try {
357             mNetworkScanRequest = buildNetworkScanRequest(true);
358             mNetworkScanCallback = new NetworkScanCallbackImpl();
359             Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RENOUNCE_START,
360                     false);
361             setReady(false);
362             startNetworkScan.sendToTarget();
363             waitUntilReady();
364 
365             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
366             assertWithMessage(
367                     "The final scan status is "
368                             + mNetworkScanStatus
369                             + " with error code "
370                             + mErrorCode
371                             + ", not ScanCompleted"
372                             + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
373                             + " ERROR_UNSUPPORTED")
374                     .that(mNetworkScanStatus)
375                     .isEqualTo(EVENT_SCAN_DENIED);
376         } finally {
377             getAndSetLocationSwitch(isLocationSwitchOn);
378         }
379     }
380 
381     @Test
testRequestNetworkScanWithRenounceWithoutChannels()382     public void testRequestNetworkScanWithRenounceWithoutChannels() {
383         boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
384         try {
385             mNetworkScanRequest = buildNetworkScanRequest(/*includeBandsAndChannels=*/false);
386             mNetworkScanCallback = new NetworkScanCallbackImpl();
387             Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RENOUNCE_START,
388                     false);
389             setReady(false);
390             startNetworkScan.sendToTarget();
391             waitUntilReady();
392 
393             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
394             assertWithMessage(
395                     "The final scan status is "
396                             + mNetworkScanStatus
397                             + " with error code "
398                             + mErrorCode
399                             + ", not ScanCompleted"
400                             + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
401                             + " ERROR_UNSUPPORTED")
402                     .that(isScanStatusValid())
403                     .isTrue();
404         } finally {
405             getAndSetLocationSwitch(isLocationSwitchOn);
406         }
407     }
408 
409     @Test
testRequestNetworkScanLocationOffPass()410     public void testRequestNetworkScanLocationOffPass() {
411         requestNetworkScanLocationOffHelper(false, true);
412     }
413 
414     @Test
testRequestNetworkScanLocationOffFail()415     public void testRequestNetworkScanLocationOffFail() {
416         requestNetworkScanLocationOffHelper(true, true);
417     }
418 
requestNetworkScanLocationOffHelper( boolean includeBandsAndChannels, boolean useSpecialScanPermission)419     public void requestNetworkScanLocationOffHelper(
420             boolean includeBandsAndChannels, boolean useSpecialScanPermission) {
421         mNetworkScanRequest = buildNetworkScanRequest(includeBandsAndChannels);
422 
423         boolean isLocationSwitchOn = getAndSetLocationSwitch(false);
424         try {
425             mNetworkScanCallback = new NetworkScanCallbackImpl();
426             Message startNetworkScan =
427                     mHandler.obtainMessage(EVENT_NETWORK_SCAN_START, useSpecialScanPermission);
428             setReady(false);
429             startNetworkScan.sendToTarget();
430             waitUntilReady();
431             if (includeBandsAndChannels) {
432                 // If we included the bands when location is off, expect a security error and
433                 // nothing else.
434                 assertThat(mNetworkScanStatus).isEqualTo(EVENT_SCAN_DENIED);
435                 return;
436             }
437 
438             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
439             assertWithMessage(
440                             "The final scan status is "
441                                     + mNetworkScanStatus
442                                     + " with error code "
443                                     + mErrorCode
444                                     + ", not ScanCompleted"
445                                     + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
446                                     + " ERROR_UNSUPPORTED")
447                     .that(isScanStatusValid())
448                     .isTrue();
449         } finally {
450             getAndSetLocationSwitch(isLocationSwitchOn);
451         }
452     }
453 
buildNetworkScanRequest(boolean includeBandsAndChannels)454     private NetworkScanRequest buildNetworkScanRequest(boolean includeBandsAndChannels) {
455         // Make sure that there should be at least one entry.
456         List<CellInfo> allCellInfo = getCellInfo();
457         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
458 
459         if (allCellInfo != null && allCellInfo.size() != 0) {
460             // Construct a NetworkScanRequest
461             radioAccessSpecifier = getRadioAccessSpecifier(allCellInfo);
462             if (!includeBandsAndChannels) {
463                 radioAccessSpecifier =
464                         radioAccessSpecifier.stream()
465                                 .map(
466                                         spec ->
467                                                 new RadioAccessSpecifier(
468                                                         spec.getRadioAccessNetwork(), null, null))
469                                 .collect(Collectors.toList());
470             }
471         }
472 
473         Log.d(TAG, "number of radioAccessSpecifier: " + radioAccessSpecifier.size());
474         if (radioAccessSpecifier.isEmpty()) {
475             // Put in some arbitrary bands and channels so that we trip the location check if needed
476             int[] fakeBands =
477                     includeBandsAndChannels
478                             ? new int[] {AccessNetworkConstants.EutranBand.BAND_5}
479                             : null;
480             int[] fakeChannels = includeBandsAndChannels ? new int[] {2400} : null;
481 
482             RadioAccessSpecifier gsm =
483                     new RadioAccessSpecifier(
484                             AccessNetworkConstants.AccessNetworkType.GERAN,
485                             null /* bands */,
486                             null /* channels */);
487             RadioAccessSpecifier lte =
488                     new RadioAccessSpecifier(
489                             AccessNetworkConstants.AccessNetworkType.EUTRAN,
490                             fakeBands /* bands */,
491                             fakeChannels /* channels */);
492             RadioAccessSpecifier wcdma =
493                     new RadioAccessSpecifier(
494                             AccessNetworkConstants.AccessNetworkType.UTRAN,
495                             null /* bands */,
496                             null /* channels */);
497             radioAccessSpecifier.add(gsm);
498             radioAccessSpecifier.add(lte);
499             radioAccessSpecifier.add(wcdma);
500         }
501         RadioAccessSpecifier[] radioAccessSpecifierArray =
502                 new RadioAccessSpecifier[radioAccessSpecifier.size()];
503         return new NetworkScanRequest(
504                 NetworkScanRequest.SCAN_TYPE_ONE_SHOT /* scan type */,
505                 radioAccessSpecifier.toArray(radioAccessSpecifierArray),
506                 5 /* search periodicity */,
507                 SCAN_SEARCH_TIME_SECONDS /* max search time */,
508                 true /*enable incremental results*/,
509                 5 /* incremental results periodicity */,
510                 null /* List of PLMN ids (MCC-MNC) */);
511     }
512 
getCellInfo()513     private List<CellInfo> getCellInfo() {
514         CellInfoResultsCallback resultsCallback = new CellInfoResultsCallback();
515         mTelephonyManager.requestCellInfoUpdate(r -> r.run(), resultsCallback);
516         try {
517             resultsCallback.wait(MAX_CELLINFO_WAIT_MILLIS);
518         } catch (InterruptedException ex) {
519             fail("CellInfoCallback was interrupted: " + ex);
520         }
521         return resultsCallback.cellInfo;
522     }
523 
524     @Test
testNetworkScanPermission()525     public void testNetworkScanPermission() {
526         PackageManager pm = getContext().getPackageManager();
527 
528         List<Integer> specialUids =
529                 Arrays.asList(Process.SYSTEM_UID, Process.PHONE_UID, Process.SHELL_UID);
530 
531         List<PackageInfo> holding =
532                 pm.getPackagesHoldingPermissions(
533                         new String[] {NETWORK_SCAN_PERMISSION},
534                         PackageManager.MATCH_DISABLED_COMPONENTS);
535 
536         List<Integer> nonSpecialPackages =
537                 holding.stream()
538                         .map(
539                                 pi -> {
540                                     try {
541                                         return pm.getPackageUid(pi.packageName, 0);
542                                     } catch (PackageManager.NameNotFoundException e) {
543                                         return Process.INVALID_UID;
544                                     }
545                                 })
546                         .filter(uid -> !specialUids.contains(UserHandle.getAppId(uid)))
547                         .collect(Collectors.toList());
548 
549         assertWithMessage(
550                         "Only one app on the device is allowed to hold the NETWORK_SCAN"
551                                 + " permission.")
552                 .that(nonSpecialPackages.size())
553                 .isAtMost(1);
554     }
555 
getAndSetLocationSwitch(boolean enabled)556     private boolean getAndSetLocationSwitch(boolean enabled) {
557         CountDownLatch locationChangeLatch = new CountDownLatch(1);
558         BroadcastReceiver locationModeChangeReceiver = new BroadcastReceiver() {
559             @Override
560             public void onReceive(Context context, Intent intent) {
561                 if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())
562                         && intent.getBooleanExtra(LocationManager.EXTRA_LOCATION_ENABLED, !enabled)
563                         == enabled) {
564                     locationChangeLatch.countDown();
565                 }
566             }
567         };
568 
569         InstrumentationRegistry.getInstrumentation().getUiAutomation()
570                 .adoptShellPermissionIdentity();
571         try {
572             Context context = InstrumentationRegistry.getContext();
573             LocationManager lm = context.getSystemService(
574                     LocationManager.class);
575             boolean oldLocationOn = lm.isLocationEnabledForUser(
576                     UserHandle.of(UserHandle.myUserId()));
577 
578             if (enabled != oldLocationOn) {
579                 context.registerReceiver(locationModeChangeReceiver,
580                         new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
581                 lm.setLocationEnabledForUser(enabled, UserHandle.of(UserHandle.myUserId()));
582                 try {
583                     assertThat(locationChangeLatch.await(LOCATION_SETTING_CHANGE_WAIT_MS,
584                             TimeUnit.MILLISECONDS)).isTrue();
585                 } catch (InterruptedException e) {
586                     Log.w(NetworkScanApiTest.class.getSimpleName(),
587                             "Interrupted while waiting for location settings change. Test results"
588                                     + " may not be accurate.");
589                 } finally {
590                     context.unregisterReceiver(locationModeChangeReceiver);
591                 }
592             }
593             return oldLocationOn;
594         } finally {
595             InstrumentationRegistry.getInstrumentation().getUiAutomation()
596                     .dropShellPermissionIdentity();
597         }
598     }
599 
isScanStatusValid()600     private boolean isScanStatusValid() {
601         // TODO(b/72162885): test the size of ScanResults is not zero after the blocking bug fixed.
602         if ((mNetworkScanStatus == EVENT_NETWORK_SCAN_COMPLETED) && (mScanResults != null)) {
603             // Scan complete.
604             return true;
605         }
606         if ((mNetworkScanStatus == EVENT_NETWORK_SCAN_ERROR)
607                 && ((mErrorCode == NetworkScan.ERROR_MODEM_UNAVAILABLE)
608                         || (mErrorCode == NetworkScan.ERROR_UNSUPPORTED))) {
609             // Scan error but the error type is allowed.
610             return true;
611         }
612         return false;
613     }
614 
getPlmns()615     private ArrayList<String> getPlmns() {
616         ArrayList<String> mccMncs = new ArrayList<>();
617         mccMncs.add("310260");
618         mccMncs.add("310120");
619         return mccMncs;
620     }
621 
622     /** To test its constructor and getters. */
623     @Test
testNetworkScanRequest_constructorAndGetters()624     public void testNetworkScanRequest_constructorAndGetters() {
625         NetworkScanRequest networkScanRequest =
626                 new NetworkScanRequest(
627                         SCAN_TYPE,
628                         RADIO_ACCESS_SPECIFIERS,
629                         SEARCH_PERIODICITY_SEC,
630                         MAX_SEARCH_TIME_SEC,
631                         INCREMENTAL_RESULTS,
632                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
633                         getPlmns());
634 
635         assertWithMessage("getScanType() returns wrong value")
636                 .that(networkScanRequest.getScanType())
637                 .isEqualTo(SCAN_TYPE);
638         assertWithMessage("getSpecifiers() returns wrong value")
639                 .that(networkScanRequest.getSpecifiers())
640                 .isEqualTo(RADIO_ACCESS_SPECIFIERS);
641         assertWithMessage("getSearchPeriodicity() returns wrong value")
642                 .that(networkScanRequest.getSearchPeriodicity())
643                 .isEqualTo(SEARCH_PERIODICITY_SEC);
644         assertWithMessage("getMaxSearchTime() returns wrong value")
645                 .that(networkScanRequest.getMaxSearchTime())
646                 .isEqualTo(MAX_SEARCH_TIME_SEC);
647         assertWithMessage("getIncrementalResults() returns wrong value")
648                 .that(networkScanRequest.getIncrementalResults())
649                 .isEqualTo(INCREMENTAL_RESULTS);
650         assertWithMessage("getIncrementalResultsPeriodicity() returns wrong value")
651                 .that(networkScanRequest.getIncrementalResultsPeriodicity())
652                 .isEqualTo(INCREMENTAL_RESULTS_PERIODICITY_SEC);
653         assertWithMessage("getPlmns() returns wrong value")
654                 .that(networkScanRequest.getPlmns())
655                 .isEqualTo(getPlmns());
656         assertWithMessage("describeContents() returns wrong value")
657                 .that(networkScanRequest.describeContents())
658                 .isEqualTo(0);
659     }
660 
661     /** To test its hashCode method. */
662     @Test
testNetworkScanRequestParcel_hashCode()663     public void testNetworkScanRequestParcel_hashCode() {
664         NetworkScanRequest networkScanRequest1 =
665                 new NetworkScanRequest(
666                         SCAN_TYPE,
667                         RADIO_ACCESS_SPECIFIERS,
668                         SEARCH_PERIODICITY_SEC,
669                         MAX_SEARCH_TIME_SEC,
670                         INCREMENTAL_RESULTS,
671                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
672                         getPlmns());
673 
674         NetworkScanRequest networkScanRequest2 =
675                 new NetworkScanRequest(
676                         SCAN_TYPE,
677                         RADIO_ACCESS_SPECIFIERS,
678                         SEARCH_PERIODICITY_SEC,
679                         MAX_SEARCH_TIME_SEC,
680                         INCREMENTAL_RESULTS,
681                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
682                         getPlmns());
683 
684         NetworkScanRequest networkScanRequest3 =
685                 new NetworkScanRequest(
686                         SCAN_TYPE,
687                         null,
688                         SEARCH_PERIODICITY_SEC,
689                         MAX_SEARCH_TIME_SEC,
690                         false,
691                         0,
692                         getPlmns());
693 
694         assertWithMessage("hashCode() returns different hash code for same objects")
695                 .that(networkScanRequest1.hashCode())
696                 .isEqualTo(networkScanRequest2.hashCode());
697         assertWithMessage("hashCode() returns same hash code for different objects")
698                 .that(networkScanRequest1.hashCode())
699                 .isNotEqualTo(networkScanRequest3.hashCode());
700     }
701 
702     /** To test its comparison method. */
703     @Test
testNetworkScanRequestParcel_equals()704     public void testNetworkScanRequestParcel_equals() {
705         NetworkScanRequest networkScanRequest1 =
706                 new NetworkScanRequest(
707                         SCAN_TYPE,
708                         RADIO_ACCESS_SPECIFIERS,
709                         SEARCH_PERIODICITY_SEC,
710                         MAX_SEARCH_TIME_SEC,
711                         INCREMENTAL_RESULTS,
712                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
713                         getPlmns());
714 
715         NetworkScanRequest networkScanRequest2 =
716                 new NetworkScanRequest(
717                         SCAN_TYPE,
718                         RADIO_ACCESS_SPECIFIERS,
719                         SEARCH_PERIODICITY_SEC,
720                         MAX_SEARCH_TIME_SEC,
721                         INCREMENTAL_RESULTS,
722                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
723                         getPlmns());
724 
725         assertThat(networkScanRequest1).isEqualTo(networkScanRequest2);
726 
727         networkScanRequest2 =
728                 new NetworkScanRequest(
729                         SCAN_TYPE,
730                         RADIO_ACCESS_SPECIFIERS,
731                         SEARCH_PERIODICITY_SEC,
732                         MAX_SEARCH_TIME_SEC,
733                         INCREMENTAL_RESULTS,
734                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
735                         null /* List of PLMN ids (MCC-MNC) */);
736         assertThat(networkScanRequest1).isNotEqualTo(networkScanRequest2);
737     }
738 
739     /** To test its writeToParcel and createFromParcel methods. */
740     @Test
testNetworkScanRequestParcel_parcel()741     public void testNetworkScanRequestParcel_parcel() {
742         NetworkScanRequest networkScanRequest =
743                 new NetworkScanRequest(
744                         SCAN_TYPE,
745                         null /* Radio Access Specifier */,
746                         SEARCH_PERIODICITY_SEC,
747                         MAX_SEARCH_TIME_SEC,
748                         INCREMENTAL_RESULTS,
749                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
750                         getPlmns());
751 
752         Parcel p = Parcel.obtain();
753         networkScanRequest.writeToParcel(p, 0);
754         p.setDataPosition(0);
755         NetworkScanRequest newnsr = NetworkScanRequest.CREATOR.createFromParcel(p);
756         assertThat(networkScanRequest).isEqualTo(newnsr);
757     }
758 }
759