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 org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 
27 import android.content.Context;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.os.AsyncTask;
31 import android.os.Handler;
32 import android.os.HandlerThread;
33 import android.os.Message;
34 import android.os.Parcel;
35 import android.os.Process;
36 import android.provider.Settings;
37 import android.telephony.AccessNetworkConstants;
38 import android.telephony.CellInfo;
39 import android.telephony.CellInfoGsm;
40 import android.telephony.CellInfoLte;
41 import android.telephony.CellInfoWcdma;
42 import android.telephony.NetworkScan;
43 import android.telephony.NetworkScanRequest;
44 import android.telephony.RadioAccessSpecifier;
45 import android.telephony.TelephonyManager;
46 import android.telephony.TelephonyScanManager;
47 import android.util.Log;
48 
49 import androidx.test.InstrumentationRegistry;
50 import androidx.test.runner.AndroidJUnit4;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.List;
60 import java.util.stream.Collectors;
61 
62 /**
63  * Build, install and run the tests by running the commands below:
64  *  make cts -j64
65  *  cts-tradefed run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.NetworkScanApiTest
66  */
67 @RunWith(AndroidJUnit4.class)
68 public class NetworkScanApiTest {
69     private TelephonyManager mTelephonyManager;
70     private PackageManager mPackageManager;
71     private static final String TAG = "NetworkScanApiTest";
72     private int mNetworkScanStatus;
73     private static final int EVENT_NETWORK_SCAN_START = 100;
74     private static final int EVENT_NETWORK_SCAN_RESULTS = 200;
75     private static final int EVENT_NETWORK_SCAN_RESTRICTED_RESULTS = 201;
76     private static final int EVENT_NETWORK_SCAN_ERROR = 300;
77     private static final int EVENT_NETWORK_SCAN_COMPLETED = 400;
78     private static final int EVENT_SCAN_DENIED = 500;
79     private List<CellInfo> mScanResults = null;
80     private NetworkScanHandlerThread mTestHandlerThread;
81     private Handler mHandler;
82     private NetworkScan mNetworkScan;
83     private NetworkScanRequest mNetworkScanRequest;
84     private NetworkScanCallbackImpl mNetworkScanCallback;
85     private static final int MAX_CELLINFO_WAIT_MILLIS = 5000; // 5 seconds
86     private static final int SCAN_SEARCH_TIME_SECONDS = 60;
87     // Wait one second longer than the max scan search time to give the test time to receive the
88     // results.
89     private static final int MAX_INIT_WAIT_MS = (SCAN_SEARCH_TIME_SECONDS + 1) * 1000;
90     private Object mLock = new Object();
91     private boolean mReady;
92     private int mErrorCode;
93     /* All the following constants are used to construct NetworkScanRequest*/
94     private static final int SCAN_TYPE = NetworkScanRequest.SCAN_TYPE_ONE_SHOT;
95     private static final boolean INCREMENTAL_RESULTS = true;
96     private static final int SEARCH_PERIODICITY_SEC = 5;
97     private static final int MAX_SEARCH_TIME_SEC = 300;
98     private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
99     private static final ArrayList<String> MCC_MNC = new ArrayList<>();
100     private static final RadioAccessSpecifier[] RADIO_ACCESS_SPECIFIERS = {
101             new RadioAccessSpecifier(
102                     AccessNetworkConstants.AccessNetworkType.GERAN,
103                     null /* bands */,
104                     null /* channels */),
105             new RadioAccessSpecifier(
106                     AccessNetworkConstants.AccessNetworkType.EUTRAN,
107                     null /* bands */,
108                     null /* channels */),
109             new RadioAccessSpecifier(
110                     AccessNetworkConstants.AccessNetworkType.UTRAN,
111                     null /* bands */,
112                     null /* channels */)
113     };
114 
115     // Needed because NETWORK_SCAN_PERMISSION is a systemapi
116     public static final String NETWORK_SCAN_PERMISSION = "android.permission.NETWORK_SCAN";
117 
118     @Before
setUp()119     public void setUp() throws Exception {
120         Context context = InstrumentationRegistry.getContext();
121         mTelephonyManager = (TelephonyManager)
122                 context.getSystemService(Context.TELEPHONY_SERVICE);
123         mPackageManager = context.getPackageManager();
124         InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
125                 context.getPackageName(), ACCESS_FINE_LOCATION);
126         InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
127                 context.getPackageName(), ACCESS_BACKGROUND_LOCATION);
128         mTestHandlerThread = new NetworkScanHandlerThread(TAG);
129         mTestHandlerThread.start();
130     }
131 
132     @After
tearDown()133     public void tearDown() throws Exception {
134         mTestHandlerThread.quit();
135     }
136 
waitUntilReady()137     private void waitUntilReady() {
138         synchronized (mLock) {
139             try {
140                 mLock.wait(MAX_INIT_WAIT_MS);
141             } catch (InterruptedException ie) {
142             }
143 
144             if (!mReady) {
145                 fail("NetworkScanApiTest failed to initialize");
146             }
147         }
148     }
149 
setReady(boolean ready)150     private void setReady(boolean ready) {
151         synchronized (mLock) {
152             mReady = ready;
153             mLock.notifyAll();
154         }
155     }
156 
157     private class NetworkScanHandlerThread extends HandlerThread {
158 
NetworkScanHandlerThread(String name)159         public NetworkScanHandlerThread(String name) {
160             super(name);
161         }
162 
163         @Override
onLooperPrepared()164         public void onLooperPrepared() {
165             /* create a custom handler for the Handler Thread */
166             mHandler = new Handler(mTestHandlerThread.getLooper()) {
167                 @Override
168                 public void handleMessage(Message msg) {
169                     switch (msg.what) {
170                         case EVENT_NETWORK_SCAN_START:
171                             Log.d(TAG, "request network scan");
172                             boolean useShellIdentity = (Boolean) msg.obj;
173                             if (useShellIdentity) {
174                                 InstrumentationRegistry.getInstrumentation().getUiAutomation()
175                                         .adoptShellPermissionIdentity();
176                             }
177                             try {
178                                 mNetworkScan = mTelephonyManager.requestNetworkScan(
179                                         mNetworkScanRequest,
180                                         AsyncTask.SERIAL_EXECUTOR,
181                                         mNetworkScanCallback);
182                                 if (mNetworkScan == null) {
183                                     mNetworkScanStatus = EVENT_SCAN_DENIED;
184                                     setReady(true);
185                                 }
186                             } catch (SecurityException e) {
187                                 mNetworkScanStatus = EVENT_SCAN_DENIED;
188                                 setReady(true);
189                             } finally {
190                                 if (useShellIdentity) {
191                                     InstrumentationRegistry.getInstrumentation().getUiAutomation()
192                                             .dropShellPermissionIdentity();
193                                 }
194                             }
195                             break;
196                         default:
197                             Log.d(TAG, "Unknown Event " + msg.what);
198                     }
199                 }
200             };
201         }
202     }
203 
204     private class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback {
205         @Override
onResults(List<CellInfo> results)206         public void onResults(List<CellInfo> results) {
207             Log.d(TAG, "onResults: " + results.toString());
208             mNetworkScanStatus = EVENT_NETWORK_SCAN_RESULTS;
209             mScanResults = results;
210         }
211 
212         @Override
onComplete()213         public void onComplete() {
214             Log.d(TAG, "onComplete");
215             mNetworkScanStatus = EVENT_NETWORK_SCAN_COMPLETED;
216             setReady(true);
217         }
218 
219         @Override
onError(int error)220         public void onError(int error) {
221             Log.d(TAG, "onError: " + String.valueOf(error));
222             mNetworkScanStatus = EVENT_NETWORK_SCAN_ERROR;
223             mErrorCode = error;
224             setReady(true);
225         }
226     }
227 
228     private class CellInfoResultsCallback extends TelephonyManager.CellInfoCallback {
229         public List<CellInfo> cellInfo;
230 
231         @Override
onCellInfo(List<CellInfo> cellInfo)232         public synchronized void onCellInfo(List<CellInfo> cellInfo) {
233             this.cellInfo = cellInfo;
234             notifyAll();
235         }
236 
wait(int millis)237         public synchronized void wait(int millis) throws InterruptedException {
238             if (cellInfo == null) {
239                 super.wait(millis);
240             }
241         }
242     }
243 
getRadioAccessSpecifier(List<CellInfo> allCellInfo)244     private List<RadioAccessSpecifier> getRadioAccessSpecifier(List<CellInfo> allCellInfo) {
245         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
246         List<Integer> lteChannels = new ArrayList<>();
247         List<Integer> wcdmaChannels = new ArrayList<>();
248         List<Integer> gsmChannels = new ArrayList<>();
249         for (int i = 0; i < allCellInfo.size(); i++) {
250             CellInfo cellInfo = allCellInfo.get(i);
251             if (cellInfo instanceof CellInfoLte) {
252                 lteChannels.add(((CellInfoLte) cellInfo).getCellIdentity().getEarfcn());
253             } else if (cellInfo instanceof CellInfoWcdma) {
254                 wcdmaChannels.add(((CellInfoWcdma) cellInfo).getCellIdentity().getUarfcn());
255             } else if (cellInfo instanceof CellInfoGsm) {
256                 gsmChannels.add(((CellInfoGsm) cellInfo).getCellIdentity().getArfcn());
257             }
258         }
259         if (!lteChannels.isEmpty()) {
260             Log.d(TAG, "lte channels" + lteChannels.toString());
261             int ranLte = AccessNetworkConstants.AccessNetworkType.EUTRAN;
262             radioAccessSpecifier.add(
263                     new RadioAccessSpecifier(ranLte, null /* bands */,
264                             lteChannels.stream().mapToInt(i->i).toArray()));
265         }
266         if (!wcdmaChannels.isEmpty()) {
267             Log.d(TAG, "wcdma channels" + wcdmaChannels.toString());
268             int ranWcdma = AccessNetworkConstants.AccessNetworkType.UTRAN;
269             radioAccessSpecifier.add(
270                     new RadioAccessSpecifier(ranWcdma, null /* bands */,
271                             wcdmaChannels.stream().mapToInt(i->i).toArray()));
272         }
273         if (!gsmChannels.isEmpty()) {
274             Log.d(TAG, "gsm channels" + gsmChannels.toString());
275             int ranGsm = AccessNetworkConstants.AccessNetworkType.GERAN;
276             radioAccessSpecifier.add(
277                     new RadioAccessSpecifier(ranGsm, null /* bands */,
278                             gsmChannels.stream().mapToInt(i->i).toArray()));
279         }
280         return radioAccessSpecifier;
281     }
282 
283     /**
284      * Tests that the device properly requests a network scan.
285      */
286     @Test
testRequestNetworkScan()287     public void testRequestNetworkScan() {
288         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
289             // Checks whether the cellular stack should be running on this device.
290             Log.e(TAG, "No cellular support, the test will be skipped.");
291             return;
292         }
293         if (!mTelephonyManager.hasCarrierPrivileges()) {
294             fail("This test requires a SIM card with carrier privilege rule on it.");
295         }
296         boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
297         try {
298             mNetworkScanRequest = buildNetworkScanRequest(true);
299             mNetworkScanCallback = new NetworkScanCallbackImpl();
300             Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START, false);
301             setReady(false);
302             startNetworkScan.sendToTarget();
303             waitUntilReady();
304 
305             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
306             assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
307                             + mErrorCode + ", not ScanCompleted"
308                             + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
309                             + " ERROR_UNSUPPORTED",
310                     isScanStatusValid());
311         } finally {
312             getAndSetLocationSwitch(isLocationSwitchOn);
313         }
314     }
315 
316     @Test
testRequestNetworkScanLocationOffPass()317     public void testRequestNetworkScanLocationOffPass() {
318         requestNetworkScanLocationOffHelper(false, true);
319     }
320 
321     @Test
testRequestNetworkScanLocationOffFail()322     public void testRequestNetworkScanLocationOffFail() {
323         requestNetworkScanLocationOffHelper(true, true);
324     }
325 
requestNetworkScanLocationOffHelper(boolean includeBandsAndChannels, boolean useSpecialScanPermission)326     public void requestNetworkScanLocationOffHelper(boolean includeBandsAndChannels,
327             boolean useSpecialScanPermission) {
328         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
329             // Checks whether the cellular stack should be running on this device.
330             Log.e(TAG, "No cellular support, the test will be skipped.");
331             return;
332         }
333         if (!mTelephonyManager.hasCarrierPrivileges()) {
334             fail("This test requires a SIM card with carrier privilege rule on it.");
335         }
336 
337         mNetworkScanRequest = buildNetworkScanRequest(includeBandsAndChannels);
338 
339         boolean isLocationSwitchOn = getAndSetLocationSwitch(false);
340         try {
341             mNetworkScanCallback = new NetworkScanCallbackImpl();
342             Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START,
343                     useSpecialScanPermission);
344             setReady(false);
345             startNetworkScan.sendToTarget();
346             waitUntilReady();
347             if (includeBandsAndChannels) {
348                 // If we included the bands when location is off, expect a security error and
349                 // nothing else.
350                 assertEquals(EVENT_SCAN_DENIED, mNetworkScanStatus);
351                 return;
352             }
353 
354             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
355             assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
356                             + mErrorCode + ", not ScanCompleted"
357                             + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
358                             + " ERROR_UNSUPPORTED",
359                     isScanStatusValid());
360         } finally {
361             getAndSetLocationSwitch(isLocationSwitchOn);
362         }
363     }
364 
buildNetworkScanRequest(boolean includeBandsAndChannels)365     private NetworkScanRequest buildNetworkScanRequest(boolean includeBandsAndChannels) {
366         // Make sure that there should be at least one entry.
367         List<CellInfo> allCellInfo = getCellInfo();
368         List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
369 
370         if (allCellInfo != null && allCellInfo.size() != 0) {
371             // Construct a NetworkScanRequest
372             radioAccessSpecifier = getRadioAccessSpecifier(allCellInfo);
373             if (!includeBandsAndChannels) {
374                 radioAccessSpecifier = radioAccessSpecifier.stream().map(spec ->
375                     new RadioAccessSpecifier(spec.getRadioAccessNetwork(), null, null))
376                     .collect(Collectors.toList());
377             }
378         }
379 
380         Log.d(TAG, "number of radioAccessSpecifier: " + radioAccessSpecifier.size());
381         if (radioAccessSpecifier.isEmpty()) {
382             // Put in some arbitrary bands and channels so that we trip the location check if needed
383             int[] fakeBands = includeBandsAndChannels
384                     ? new int[] { AccessNetworkConstants.EutranBand.BAND_5 }
385                     : null;
386             int[] fakeChannels = includeBandsAndChannels ? new int[] { 2400 } : null;
387 
388             RadioAccessSpecifier gsm = new RadioAccessSpecifier(
389                     AccessNetworkConstants.AccessNetworkType.GERAN,
390                     null /* bands */,
391                     null /* channels */);
392             RadioAccessSpecifier lte = new RadioAccessSpecifier(
393                     AccessNetworkConstants.AccessNetworkType.EUTRAN,
394                     fakeBands /* bands */,
395                     fakeChannels /* channels */);
396             RadioAccessSpecifier wcdma = new RadioAccessSpecifier(
397                     AccessNetworkConstants.AccessNetworkType.UTRAN,
398                     null /* bands */,
399                     null /* channels */);
400             radioAccessSpecifier.add(gsm);
401             radioAccessSpecifier.add(lte);
402             radioAccessSpecifier.add(wcdma);
403         }
404         RadioAccessSpecifier[] radioAccessSpecifierArray =
405                 new RadioAccessSpecifier[radioAccessSpecifier.size()];
406         return new NetworkScanRequest(
407                 NetworkScanRequest.SCAN_TYPE_ONE_SHOT /* scan type */,
408                 radioAccessSpecifier.toArray(radioAccessSpecifierArray),
409                 5 /* search periodicity */,
410                 SCAN_SEARCH_TIME_SECONDS /* max search time */,
411                 true /*enable incremental results*/,
412                 5 /* incremental results periodicity */,
413                 null /* List of PLMN ids (MCC-MNC) */);
414 
415     }
416 
getCellInfo()417     private List<CellInfo> getCellInfo() {
418         CellInfoResultsCallback resultsCallback = new CellInfoResultsCallback();
419         mTelephonyManager.requestCellInfoUpdate(r -> r.run(), resultsCallback);
420         try {
421             resultsCallback.wait(MAX_CELLINFO_WAIT_MILLIS);
422         } catch (InterruptedException ex) {
423             fail("CellInfoCallback was interrupted: " + ex);
424         }
425         return resultsCallback.cellInfo;
426     }
427 
428     @Test
testNetworkScanPermission()429     public void testNetworkScanPermission() {
430         PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
431 
432         List<Integer> specialUids = Arrays.asList(Process.SYSTEM_UID,
433                 Process.PHONE_UID, Process.SHELL_UID);
434 
435         List<PackageInfo> holding = pm.getPackagesHoldingPermissions(
436                 new String[] { NETWORK_SCAN_PERMISSION },
437                 PackageManager.MATCH_DISABLED_COMPONENTS);
438 
439         List<Integer> nonSpecialPackages = holding.stream()
440                 .map(pi -> {
441                     try {
442                         return pm.getPackageUid(pi.packageName, 0);
443                     } catch (PackageManager.NameNotFoundException e) {
444                         return Process.INVALID_UID;
445                     }
446                 })
447                 .filter(uid -> !specialUids.contains(uid))
448                 .collect(Collectors.toList());
449 
450         if (nonSpecialPackages.size() > 1) {
451             fail("Only one app on the device is allowed to hold the NETWORK_SCAN permission.");
452         }
453     }
454 
getAndSetLocationSwitch(boolean enabled)455     private boolean getAndSetLocationSwitch(boolean enabled) {
456         InstrumentationRegistry.getInstrumentation().getUiAutomation()
457                 .adoptShellPermissionIdentity();
458         try {
459             int oldLocationMode = Settings.Secure.getInt(
460                     InstrumentationRegistry.getContext().getContentResolver(),
461                     Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
462 
463             int locationMode = enabled ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
464                     : Settings.Secure.LOCATION_MODE_OFF;
465             if (locationMode != oldLocationMode) {
466                 Settings.Secure.putInt(InstrumentationRegistry.getContext().getContentResolver(),
467                         Settings.Secure.LOCATION_MODE, locationMode);
468             }
469             return oldLocationMode == Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
470         } finally {
471             InstrumentationRegistry.getInstrumentation().getUiAutomation()
472                     .dropShellPermissionIdentity();
473         }
474     }
475 
isScanStatusValid()476     private boolean isScanStatusValid() {
477         // TODO(b/72162885): test the size of ScanResults is not zero after the blocking bug fixed.
478         if ((mNetworkScanStatus == EVENT_NETWORK_SCAN_COMPLETED) && (mScanResults != null)) {
479             // Scan complete.
480             return true;
481         }
482         if ((mNetworkScanStatus == EVENT_NETWORK_SCAN_ERROR)
483                 && ((mErrorCode == NetworkScan.ERROR_MODEM_UNAVAILABLE)
484                 || (mErrorCode == NetworkScan.ERROR_UNSUPPORTED))) {
485             // Scan error but the error type is allowed.
486             return true;
487         }
488         return false;
489     }
490 
getPlmns()491     private ArrayList<String> getPlmns() {
492         ArrayList<String> mccMncs = new ArrayList<>();
493         mccMncs.add("310260");
494         mccMncs.add("310120");
495         return mccMncs;
496     }
497 
498     /**
499      * To test its constructor and getters.
500      */
501     @Test
testNetworkScanRequest_ConstructorAndGetters()502     public void testNetworkScanRequest_ConstructorAndGetters() {
503         NetworkScanRequest networkScanRequest = new NetworkScanRequest(
504                 SCAN_TYPE,
505                 RADIO_ACCESS_SPECIFIERS,
506                 SEARCH_PERIODICITY_SEC,
507                 MAX_SEARCH_TIME_SEC,
508                 INCREMENTAL_RESULTS,
509                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
510                 getPlmns());
511 
512         assertEquals("getScanType() returns wrong value",
513                 SCAN_TYPE, networkScanRequest.getScanType());
514         assertEquals("getSpecifiers() returns wrong value",
515                 RADIO_ACCESS_SPECIFIERS, networkScanRequest.getSpecifiers());
516         assertEquals("getSearchPeriodicity() returns wrong value",
517                 SEARCH_PERIODICITY_SEC, networkScanRequest.getSearchPeriodicity());
518         assertEquals("getMaxSearchTime() returns wrong value",
519                 MAX_SEARCH_TIME_SEC, networkScanRequest.getMaxSearchTime());
520         assertEquals("getIncrementalResults() returns wrong value",
521                 INCREMENTAL_RESULTS, networkScanRequest.getIncrementalResults());
522         assertEquals("getIncrementalResultsPeriodicity() returns wrong value",
523                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
524                 networkScanRequest.getIncrementalResultsPeriodicity());
525         assertEquals("getPlmns() returns wrong value", getPlmns(), networkScanRequest.getPlmns());
526         assertEquals("describeContents() returns wrong value",
527                 0, networkScanRequest.describeContents());
528     }
529 
530     /**
531      * To test its hashCode method.
532      */
533     @Test
testNetworkScanRequestParcel_Hashcode()534     public void testNetworkScanRequestParcel_Hashcode() {
535         NetworkScanRequest networkScanRequest1 = new NetworkScanRequest(
536                 SCAN_TYPE,
537                 RADIO_ACCESS_SPECIFIERS,
538                 SEARCH_PERIODICITY_SEC,
539                 MAX_SEARCH_TIME_SEC,
540                 INCREMENTAL_RESULTS,
541                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
542                 getPlmns());
543 
544         NetworkScanRequest networkScanRequest2 = new NetworkScanRequest(
545                 SCAN_TYPE,
546                 RADIO_ACCESS_SPECIFIERS,
547                 SEARCH_PERIODICITY_SEC,
548                 MAX_SEARCH_TIME_SEC,
549                 INCREMENTAL_RESULTS,
550                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
551                 getPlmns());
552 
553         NetworkScanRequest networkScanRequest3 = new NetworkScanRequest(
554                 SCAN_TYPE,
555                 null,
556                 SEARCH_PERIODICITY_SEC,
557                 MAX_SEARCH_TIME_SEC,
558                 false,
559                 0,
560                 getPlmns());
561 
562         assertEquals("hashCode() returns different hash code for same objects",
563                 networkScanRequest1.hashCode(), networkScanRequest2.hashCode());
564         assertNotSame("hashCode() returns same hash code for different objects",
565                 networkScanRequest1.hashCode(), networkScanRequest3.hashCode());
566     }
567 
568     /**
569      * To test its comparision method.
570      */
571     @Test
testNetworkScanRequestParcel_Equals()572     public void testNetworkScanRequestParcel_Equals() {
573         NetworkScanRequest networkScanRequest1 = new NetworkScanRequest(
574                 SCAN_TYPE,
575                 RADIO_ACCESS_SPECIFIERS,
576                 SEARCH_PERIODICITY_SEC,
577                 MAX_SEARCH_TIME_SEC,
578                 INCREMENTAL_RESULTS,
579                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
580                 getPlmns());
581 
582         NetworkScanRequest networkScanRequest2 = new NetworkScanRequest(
583                 SCAN_TYPE,
584                 RADIO_ACCESS_SPECIFIERS,
585                 SEARCH_PERIODICITY_SEC,
586                 MAX_SEARCH_TIME_SEC,
587                 INCREMENTAL_RESULTS,
588                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
589                 getPlmns());
590 
591         assertTrue(networkScanRequest1.equals(networkScanRequest2));
592 
593         networkScanRequest2 = new NetworkScanRequest(
594                 SCAN_TYPE,
595                 RADIO_ACCESS_SPECIFIERS,
596                 SEARCH_PERIODICITY_SEC,
597                 MAX_SEARCH_TIME_SEC,
598                 INCREMENTAL_RESULTS,
599                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
600                 null /* List of PLMN ids (MCC-MNC) */);
601         assertFalse(networkScanRequest1.equals(networkScanRequest2));
602     }
603 
604     /**
605      * To test its writeToParcel and createFromParcel methods.
606      */
607     @Test
testNetworkScanRequestParcel_Parcel()608     public void testNetworkScanRequestParcel_Parcel() {
609         NetworkScanRequest networkScanRequest = new NetworkScanRequest(
610                 SCAN_TYPE,
611                 null /* Radio Access Specifier */,
612                 SEARCH_PERIODICITY_SEC,
613                 MAX_SEARCH_TIME_SEC,
614                 INCREMENTAL_RESULTS,
615                 INCREMENTAL_RESULTS_PERIODICITY_SEC,
616                 getPlmns());
617 
618         Parcel p = Parcel.obtain();
619         networkScanRequest.writeToParcel(p, 0);
620         p.setDataPosition(0);
621         NetworkScanRequest newnsr = NetworkScanRequest.CREATOR.createFromParcel(p);
622         assertTrue(networkScanRequest.equals(newnsr));
623     }
624 }
625