1 /*
2  * Copyright (C) 2017 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 
17 package com.android.internal.telephony;
18 
19 import static android.os.Binder.withCleanCallingIdentity;
20 import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
21 import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
22 import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
23 import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
24 
25 import android.content.Context;
26 import android.hardware.radio.V1_0.RadioError;
27 import android.os.AsyncResult;
28 import android.os.Build;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Message;
33 import android.os.Messenger;
34 import android.os.Process;
35 import android.os.RemoteException;
36 import android.telephony.CellInfo;
37 import android.telephony.LocationAccessPolicy;
38 import android.telephony.NetworkScan;
39 import android.telephony.NetworkScanRequest;
40 import android.telephony.RadioAccessSpecifier;
41 import android.telephony.SubscriptionInfo;
42 import android.telephony.TelephonyScanManager;
43 import android.util.Log;
44 
45 import java.util.Collection;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.concurrent.atomic.AtomicInteger;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51 
52 /**
53  * Manages radio access network scan requests.
54  *
55  * Provides methods to start and stop network scan requests, and keeps track of all the live scans.
56  *
57  * {@hide}
58  */
59 public final class NetworkScanRequestTracker {
60 
61     private static final String TAG = "ScanRequestTracker";
62 
63     private static final int CMD_START_NETWORK_SCAN = 1;
64     private static final int EVENT_START_NETWORK_SCAN_DONE = 2;
65     private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3;
66     private static final int CMD_STOP_NETWORK_SCAN = 4;
67     private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5;
68     private static final int CMD_INTERRUPT_NETWORK_SCAN = 6;
69     private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7;
70     private static final int EVENT_MODEM_RESET = 8;
71     private static final int EVENT_RADIO_UNAVAILABLE = 9;
72 
73     private final Handler mHandler = new Handler() {
74         @Override
75         public void handleMessage(Message msg) {
76             Log.d(TAG, "Received Event :" + msg.what);
77             switch (msg.what) {
78                 case CMD_START_NETWORK_SCAN:
79                     mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj);
80                     break;
81 
82                 case EVENT_START_NETWORK_SCAN_DONE:
83                     mScheduler.startScanDone((AsyncResult) msg.obj);
84                     break;
85 
86                 case EVENT_RECEIVE_NETWORK_SCAN_RESULT:
87                     mScheduler.receiveResult((AsyncResult) msg.obj);
88                     break;
89 
90                 case CMD_STOP_NETWORK_SCAN:
91                     mScheduler.doStopScan(msg.arg1);
92                     break;
93 
94                 case EVENT_STOP_NETWORK_SCAN_DONE:
95                     mScheduler.stopScanDone((AsyncResult) msg.obj);
96                     break;
97 
98                 case CMD_INTERRUPT_NETWORK_SCAN:
99                     mScheduler.doInterruptScan(msg.arg1);
100                     break;
101 
102                 case EVENT_INTERRUPT_NETWORK_SCAN_DONE:
103                     mScheduler.interruptScanDone((AsyncResult) msg.obj);
104                     break;
105 
106                 case EVENT_RADIO_UNAVAILABLE:
107                     // Fallthrough
108                 case EVENT_MODEM_RESET:
109                     AsyncResult ar = (AsyncResult) msg.obj;
110                     mScheduler.deleteScanAndMayNotify(
111                             (NetworkScanRequestInfo) ar.userObj,
112                             NetworkScan.ERROR_MODEM_ERROR,
113                             true);
114                     break;
115             }
116         }
117     };
118 
119     // The sequence number of NetworkScanRequests
120     private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1);
121     private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler();
122 
logEmptyResultOrException(AsyncResult ar)123     private void logEmptyResultOrException(AsyncResult ar) {
124         if (ar.result == null) {
125             Log.e(TAG, "NetworkScanResult: Empty result");
126         } else {
127             Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception);
128         }
129     }
130 
isValidScan(NetworkScanRequestInfo nsri)131     private boolean isValidScan(NetworkScanRequestInfo nsri) {
132         if (nsri.mRequest == null || nsri.mRequest.getSpecifiers() == null) {
133             return false;
134         }
135         if (nsri.mRequest.getSpecifiers().length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
136             return false;
137         }
138         for (RadioAccessSpecifier ras : nsri.mRequest.getSpecifiers()) {
139             if (ras.getRadioAccessNetwork() != GERAN && ras.getRadioAccessNetwork() != UTRAN
140                     && ras.getRadioAccessNetwork() != EUTRAN
141                     && ras.getRadioAccessNetwork() != NGRAN) {
142                 return false;
143             }
144             if (ras.getBands() != null && ras.getBands().length > NetworkScanRequest.MAX_BANDS) {
145                 return false;
146             }
147             if (ras.getChannels() != null
148                     && ras.getChannels().length > NetworkScanRequest.MAX_CHANNELS) {
149                 return false;
150             }
151         }
152 
153         if ((nsri.mRequest.getSearchPeriodicity() < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
154                 || (nsri.mRequest.getSearchPeriodicity()
155                 > NetworkScanRequest.MAX_SEARCH_PERIODICITY_SEC)) {
156             return false;
157         }
158 
159         if ((nsri.mRequest.getMaxSearchTime() < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
160                 || (nsri.mRequest.getMaxSearchTime() > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
161             return false;
162         }
163 
164         if ((nsri.mRequest.getIncrementalResultsPeriodicity()
165                 < NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC)
166                 || (nsri.mRequest.getIncrementalResultsPeriodicity()
167                 > NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC)) {
168             return false;
169         }
170 
171         if ((nsri.mRequest.getSearchPeriodicity() > nsri.mRequest.getMaxSearchTime())
172                 || (nsri.mRequest.getIncrementalResultsPeriodicity()
173                         > nsri.mRequest.getMaxSearchTime())) {
174             return false;
175         }
176 
177         if ((nsri.mRequest.getPlmns() != null)
178                 && (nsri.mRequest.getPlmns().size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
179             return false;
180         }
181         return true;
182     }
183 
doesCellInfoCorrespondToKnownMccMnc(CellInfo ci, Collection<String> knownMccMncs)184     private static boolean doesCellInfoCorrespondToKnownMccMnc(CellInfo ci,
185             Collection<String> knownMccMncs) {
186         String mccMnc = ci.getCellIdentity().getMccString()
187                 + ci.getCellIdentity().getMncString();
188         return knownMccMncs.contains(mccMnc);
189     }
190 
191     /**
192      * @return A list of MCC/MNC ids that apps should be allowed to see as results from a network
193      * scan when scan results are restricted due to location privacy.
194      */
getAllowedMccMncsForLocationRestrictedScan(Context context)195     public static Set<String> getAllowedMccMncsForLocationRestrictedScan(Context context) {
196         return withCleanCallingIdentity(() -> SubscriptionController.getInstance()
197             .getAvailableSubscriptionInfoList(context.getOpPackageName(), null).stream()
198             .flatMap(NetworkScanRequestTracker::getAllowableMccMncsFromSubscriptionInfo)
199             .collect(Collectors.toSet()));
200     }
201 
getAllowableMccMncsFromSubscriptionInfo(SubscriptionInfo info)202     private static Stream<String> getAllowableMccMncsFromSubscriptionInfo(SubscriptionInfo info) {
203         Stream<String> plmns = Stream.of(info.getEhplmns(), info.getHplmns()).flatMap(List::stream);
204         if (info.getMccString() != null && info.getMncString() != null) {
205             plmns = Stream.concat(plmns, Stream.of(info.getMccString() + info.getMncString()));
206         }
207         return plmns;
208     }
209 
210     /** Sends a message back to the application via its callback. */
notifyMessenger(NetworkScanRequestInfo nsri, int what, int err, List<CellInfo> result)211     private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err,
212             List<CellInfo> result) {
213         Messenger messenger = nsri.mMessenger;
214         Message message = Message.obtain();
215         message.what = what;
216         message.arg1 = err;
217         message.arg2 = nsri.mScanId;
218 
219         if (result != null) {
220             if (what == TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS) {
221                 Set<String> allowedMccMncs =
222                         getAllowedMccMncsForLocationRestrictedScan(nsri.mPhone.getContext());
223 
224                 result = result.stream().map(CellInfo::sanitizeLocationInfo)
225                         .filter(ci -> doesCellInfoCorrespondToKnownMccMnc(ci, allowedMccMncs))
226                         .collect(Collectors.toList());
227             }
228 
229             CellInfo[] ci = result.toArray(new CellInfo[result.size()]);
230             Bundle b = new Bundle();
231             b.putParcelableArray(TelephonyScanManager.SCAN_RESULT_KEY, ci);
232             message.setData(b);
233         } else {
234             message.obj = null;
235         }
236         try {
237             messenger.send(message);
238         } catch (RemoteException e) {
239             Log.e(TAG, "Exception in notifyMessenger: " + e);
240         }
241     }
242 
243     /**
244     * Tracks info about the radio network scan.
245      *
246     * Also used to notice when the calling process dies so we can self-expire.
247     */
248     class NetworkScanRequestInfo implements IBinder.DeathRecipient {
249         private final NetworkScanRequest mRequest;
250         private final Messenger mMessenger;
251         private final IBinder mBinder;
252         private final Phone mPhone;
253         private final int mScanId;
254         private final int mUid;
255         private final int mPid;
256         private final String mCallingPackage;
257         private boolean mIsBinderDead;
258 
NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone, int callingUid, int callingPid, String callingPackage)259         NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone,
260                 int callingUid, int callingPid, String callingPackage) {
261             super();
262             mRequest = r;
263             mMessenger = m;
264             mBinder = b;
265             mScanId = id;
266             mPhone = phone;
267             mUid = callingUid;
268             mPid = callingPid;
269             mCallingPackage = callingPackage;
270             mIsBinderDead = false;
271 
272             try {
273                 mBinder.linkToDeath(this, 0);
274             } catch (RemoteException e) {
275                 binderDied();
276             }
277         }
278 
setIsBinderDead(boolean val)279         synchronized void setIsBinderDead(boolean val) {
280             mIsBinderDead = val;
281         }
282 
getIsBinderDead()283         synchronized boolean getIsBinderDead() {
284             return mIsBinderDead;
285         }
286 
getRequest()287         NetworkScanRequest getRequest() {
288             return mRequest;
289         }
290 
unlinkDeathRecipient()291         void unlinkDeathRecipient() {
292             if (mBinder != null) {
293                 mBinder.unlinkToDeath(this, 0);
294             }
295         }
296 
297         @Override
binderDied()298         public void binderDied() {
299             Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied("
300                     + mRequest + ", " + mBinder + ")");
301             setIsBinderDead(true);
302             interruptNetworkScan(mScanId);
303         }
304     }
305 
306     /**
307      * Handles multiplexing and scheduling for multiple requests.
308      */
309     private class NetworkScanRequestScheduler {
310 
311         private NetworkScanRequestInfo mLiveRequestInfo;
312         private NetworkScanRequestInfo mPendingRequestInfo;
313 
rilErrorToScanError(int rilError)314         private int rilErrorToScanError(int rilError) {
315             switch (rilError) {
316                 case RadioError.NONE:
317                     return NetworkScan.SUCCESS;
318                 case RadioError.RADIO_NOT_AVAILABLE:
319                     Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE");
320                     return NetworkScan.ERROR_MODEM_ERROR;
321                 case RadioError.REQUEST_NOT_SUPPORTED:
322                     Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED");
323                     return NetworkScan.ERROR_UNSUPPORTED;
324                 case RadioError.NO_MEMORY:
325                     Log.e(TAG, "rilErrorToScanError: NO_MEMORY");
326                     return NetworkScan.ERROR_MODEM_ERROR;
327                 case RadioError.INTERNAL_ERR:
328                     Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR");
329                     return NetworkScan.ERROR_MODEM_ERROR;
330                 case RadioError.MODEM_ERR:
331                     Log.e(TAG, "rilErrorToScanError: MODEM_ERR");
332                     return NetworkScan.ERROR_MODEM_ERROR;
333                 case RadioError.OPERATION_NOT_ALLOWED:
334                     Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED");
335                     return NetworkScan.ERROR_MODEM_ERROR;
336                 case RadioError.INVALID_ARGUMENTS:
337                     Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS");
338                     return NetworkScan.ERROR_INVALID_SCAN;
339                 case RadioError.DEVICE_IN_USE:
340                     Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
341                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
342                 default:
343                     Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " +  rilError);
344                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
345             }
346         }
347 
commandExceptionErrorToScanError(CommandException.Error error)348         private int commandExceptionErrorToScanError(CommandException.Error error) {
349             switch (error) {
350                 case RADIO_NOT_AVAILABLE:
351                     Log.e(TAG, "commandExceptionErrorToScanError: RADIO_NOT_AVAILABLE");
352                     return NetworkScan.ERROR_MODEM_ERROR;
353                 case REQUEST_NOT_SUPPORTED:
354                     Log.e(TAG, "commandExceptionErrorToScanError: REQUEST_NOT_SUPPORTED");
355                     return NetworkScan.ERROR_UNSUPPORTED;
356                 case NO_MEMORY:
357                     Log.e(TAG, "commandExceptionErrorToScanError: NO_MEMORY");
358                     return NetworkScan.ERROR_MODEM_ERROR;
359                 case INTERNAL_ERR:
360                     Log.e(TAG, "commandExceptionErrorToScanError: INTERNAL_ERR");
361                     return NetworkScan.ERROR_MODEM_ERROR;
362                 case MODEM_ERR:
363                     Log.e(TAG, "commandExceptionErrorToScanError: MODEM_ERR");
364                     return NetworkScan.ERROR_MODEM_ERROR;
365                 case OPERATION_NOT_ALLOWED:
366                     Log.e(TAG, "commandExceptionErrorToScanError: OPERATION_NOT_ALLOWED");
367                     return NetworkScan.ERROR_MODEM_ERROR;
368                 case INVALID_ARGUMENTS:
369                     Log.e(TAG, "commandExceptionErrorToScanError: INVALID_ARGUMENTS");
370                     return NetworkScan.ERROR_INVALID_SCAN;
371                 case DEVICE_IN_USE:
372                     Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
373                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
374                 default:
375                     Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
376                             +  error);
377                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
378             }
379         }
380 
doStartScan(NetworkScanRequestInfo nsri)381         private void doStartScan(NetworkScanRequestInfo nsri) {
382             if (nsri == null) {
383                 Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null");
384                 return;
385             }
386             if (!isValidScan(nsri)) {
387                 notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
388                         NetworkScan.ERROR_INVALID_SCAN, null);
389                 return;
390             }
391             if (nsri.getIsBinderDead()) {
392                 Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died");
393                 return;
394             }
395             if (!startNewScan(nsri)) {
396                 if (!interruptLiveScan(nsri)) {
397                     if (!cacheScan(nsri)) {
398                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
399                                 NetworkScan.ERROR_MODEM_UNAVAILABLE, null);
400                     }
401                 }
402             }
403         }
404 
startScanDone(AsyncResult ar)405         private synchronized void startScanDone(AsyncResult ar) {
406             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
407             if (nsri == null) {
408                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null");
409                 return;
410             }
411             if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) {
412                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo");
413                 return;
414             }
415             if (ar.exception == null && ar.result != null) {
416                 // Register for the scan results if the scan started successfully.
417                 nsri.mPhone.mCi.registerForNetworkScanResult(mHandler,
418                         EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
419             } else {
420                 logEmptyResultOrException(ar);
421                 if (ar.exception != null) {
422                     CommandException.Error error =
423                             ((CommandException) (ar.exception)).getCommandError();
424                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
425                 } else {
426                     Log.wtf(TAG, "EVENT_START_NETWORK_SCAN_DONE: ar.exception can not be null!");
427                 }
428             }
429         }
430 
receiveResult(AsyncResult ar)431         private void receiveResult(AsyncResult ar) {
432             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
433             if (nsri == null) {
434                 Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
435                 return;
436             }
437             LocationAccessPolicy.LocationPermissionQuery locationQuery =
438                     new LocationAccessPolicy.LocationPermissionQuery.Builder()
439                     .setCallingPackage(nsri.mCallingPackage)
440                     .setCallingPid(nsri.mPid)
441                     .setCallingUid(nsri.mUid)
442                     .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
443                     .setMethod("NetworkScanTracker#onResult")
444                     .build();
445             if (ar.exception == null && ar.result != null) {
446                 NetworkScanResult nsr = (NetworkScanResult) ar.result;
447                 boolean isLocationAccessAllowed = LocationAccessPolicy.checkLocationPermission(
448                         nsri.mPhone.getContext(), locationQuery)
449                         == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
450                 int notifyMsg = isLocationAccessAllowed
451                         ? TelephonyScanManager.CALLBACK_SCAN_RESULTS
452                         : TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS;
453                 if (nsr.scanError == NetworkScan.SUCCESS) {
454                     if (nsri.mPhone.getServiceStateTracker() != null) {
455                         nsri.mPhone.getServiceStateTracker().updateOperatorNameForCellInfo(
456                                 nsr.networkInfos);
457                     }
458 
459                     notifyMessenger(nsri, notifyMsg,
460                             rilErrorToScanError(nsr.scanError), nsr.networkInfos);
461                     if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
462                         deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
463                         nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
464                     }
465                 } else {
466                     if (nsr.networkInfos != null) {
467                         notifyMessenger(nsri, notifyMsg,
468                                 rilErrorToScanError(nsr.scanError), nsr.networkInfos);
469                     }
470                     deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
471                     nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
472                 }
473             } else {
474                 logEmptyResultOrException(ar);
475                 deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
476                 nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
477             }
478         }
479 
480         // Stops the scan if the scanId and uid match the mScanId and mUid.
481         // If the scan to be stopped is the live scan, we only send the request to RIL, while the
482         // mLiveRequestInfo will not be cleared and the user will not be notified either.
483         // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and
484         // notify the user.
doStopScan(int scanId)485         private synchronized void doStopScan(int scanId) {
486             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
487                 mLiveRequestInfo.mPhone.stopNetworkScan(
488                         mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo));
489             } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) {
490                 notifyMessenger(mPendingRequestInfo,
491                         TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null);
492                 mPendingRequestInfo = null;
493             } else {
494                 Log.e(TAG, "stopScan: scan " + scanId + " does not exist!");
495             }
496         }
497 
stopScanDone(AsyncResult ar)498         private void stopScanDone(AsyncResult ar) {
499             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
500             if (nsri == null) {
501                 Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
502                 return;
503             }
504             if (ar.exception == null && ar.result != null) {
505                 deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
506             } else {
507                 logEmptyResultOrException(ar);
508                 if (ar.exception != null) {
509                     CommandException.Error error =
510                             ((CommandException) (ar.exception)).getCommandError();
511                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
512                 } else {
513                     Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
514                 }
515             }
516             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
517         }
518 
519         // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
doInterruptScan(int scanId)520         private synchronized void doInterruptScan(int scanId) {
521             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
522                 mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage(
523                         EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo));
524             } else {
525                 Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!");
526             }
527         }
528 
interruptScanDone(AsyncResult ar)529         private void interruptScanDone(AsyncResult ar) {
530             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
531             if (nsri == null) {
532                 Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null");
533                 return;
534             }
535             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
536             deleteScanAndMayNotify(nsri, 0, false);
537         }
538 
539         // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is
540         // stopped, a new scan will automatically start with nsri.
541         // The new scan can interrupt the live scan only when all the below requirements are met:
542         //   1. There is 1 live scan and no other pending scan
543         //   2. The new scan is requested by mobile network setting menu (owned by SYSTEM process)
544         //   3. The live scan is not requested by mobile network setting menu
interruptLiveScan(NetworkScanRequestInfo nsri)545         private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
546             if (mLiveRequestInfo != null && mPendingRequestInfo == null
547                     && nsri.mUid == Process.SYSTEM_UID
548                             && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
549                 doInterruptScan(mLiveRequestInfo.mScanId);
550                 mPendingRequestInfo = nsri;
551                 notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
552                         NetworkScan.ERROR_INTERRUPTED, null);
553                 return true;
554             }
555             return false;
556         }
557 
cacheScan(NetworkScanRequestInfo nsri)558         private boolean cacheScan(NetworkScanRequestInfo nsri) {
559             // TODO(30954762): Cache periodic scan for OC-MR1.
560             return false;
561         }
562 
563         // Starts a new scan with nsri if there is no live scan running.
startNewScan(NetworkScanRequestInfo nsri)564         private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
565             if (mLiveRequestInfo == null) {
566                 mLiveRequestInfo = nsri;
567                 nsri.mPhone.startNetworkScan(nsri.getRequest(),
568                         mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
569                 nsri.mPhone.mCi.registerForModemReset(mHandler, EVENT_MODEM_RESET, nsri);
570                 nsri.mPhone.mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, nsri);
571                 return true;
572             }
573             return false;
574         }
575 
576 
577         // Deletes the mLiveRequestInfo and notify the user if it matches nsri.
deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error, boolean notify)578         private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error,
579                 boolean notify) {
580             if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) {
581                 if (notify) {
582                     if (error == NetworkScan.SUCCESS) {
583                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error,
584                                 null);
585                     } else {
586                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error,
587                                 null);
588                     }
589                 }
590                 mLiveRequestInfo.mPhone.mCi.unregisterForModemReset(mHandler);
591                 mLiveRequestInfo.mPhone.mCi.unregisterForNotAvailable(mHandler);
592                 mLiveRequestInfo = null;
593                 if (mPendingRequestInfo != null) {
594                     startNewScan(mPendingRequestInfo);
595                     mPendingRequestInfo = null;
596                 }
597             }
598         }
599     }
600 
601     /**
602      * Interrupts an ongoing network scan
603      *
604      * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
605      * difference is that stopNetworkScan is only used by the callers to stop their own scans, so
606      * correctness check will be done to make sure the request is valid; while this method is only
607      * internally used by NetworkScanRequestTracker so correctness check is not needed.
608      */
interruptNetworkScan(int scanId)609     private void interruptNetworkScan(int scanId) {
610         // scanId will be stored at Message.arg1
611         mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget();
612     }
613 
614     /**
615      * Starts a new network scan
616      *
617      * This function only wraps all the incoming information and delegate then to the handler thread
618      * which will actually handles the scan request. So a new scanId will always be generated and
619      * returned to the user, no matter how this scan will be actually handled.
620      */
startNetworkScan( NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone, int callingUid, int callingPid, String callingPackage)621     public int startNetworkScan(
622             NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone,
623             int callingUid, int callingPid, String callingPackage) {
624         int scanId = mNextNetworkScanRequestId.getAndIncrement();
625         NetworkScanRequestInfo nsri =
626                 new NetworkScanRequestInfo(request, messenger, binder, scanId, phone,
627                         callingUid, callingPid, callingPackage);
628         // nsri will be stored as Message.obj
629         mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
630         return scanId;
631     }
632 
633     /**
634      * Stops an ongoing network scan
635      *
636      * The ongoing scan will be stopped only when the input scanId and caller's uid matches the
637      * corresponding information associated with it.
638      */
stopNetworkScan(int scanId, int callingUid)639     public void stopNetworkScan(int scanId, int callingUid) {
640         synchronized (mScheduler) {
641             if ((mScheduler.mLiveRequestInfo != null
642                     && scanId == mScheduler.mLiveRequestInfo.mScanId
643                     && callingUid == mScheduler.mLiveRequestInfo.mUid)
644                     || (mScheduler.mPendingRequestInfo != null
645                     && scanId == mScheduler.mPendingRequestInfo.mScanId
646                     && callingUid == mScheduler.mPendingRequestInfo.mUid)) {
647                 // scanId will be stored at Message.arg1
648                 mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget();
649             } else {
650                 throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!");
651             }
652         }
653     }
654 }
655