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