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