• 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 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         final long token = Binder.clearCallingIdentity();
197         try {
198             return SubscriptionController.getInstance()
199                     .getAvailableSubscriptionInfoList(context.getOpPackageName(),
200                             context.getAttributionTag()).stream()
201                     .flatMap(NetworkScanRequestTracker::getAllowableMccMncsFromSubscriptionInfo)
202                     .collect(Collectors.toSet());
203         } finally {
204             Binder.restoreCallingIdentity(token);
205         }
206     }
207 
getAllowableMccMncsFromSubscriptionInfo(SubscriptionInfo info)208     private static Stream<String> getAllowableMccMncsFromSubscriptionInfo(SubscriptionInfo info) {
209         Stream<String> plmns = Stream.of(info.getEhplmns(), info.getHplmns()).flatMap(List::stream);
210         if (info.getMccString() != null && info.getMncString() != null) {
211             plmns = Stream.concat(plmns, Stream.of(info.getMccString() + info.getMncString()));
212         }
213         return plmns;
214     }
215 
216     /** Sends a message back to the application via its callback. */
notifyMessenger(NetworkScanRequestInfo nsri, int what, int err, List<CellInfo> result)217     private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err,
218             List<CellInfo> result) {
219         Messenger messenger = nsri.mMessenger;
220         Message message = Message.obtain();
221         message.what = what;
222         message.arg1 = err;
223         message.arg2 = nsri.mScanId;
224 
225         if (result != null) {
226             if (what == TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS) {
227                 Set<String> allowedMccMncs =
228                         getAllowedMccMncsForLocationRestrictedScan(nsri.mPhone.getContext());
229 
230                 result = result.stream().map(CellInfo::sanitizeLocationInfo)
231                         .filter(ci -> doesCellInfoCorrespondToKnownMccMnc(ci, allowedMccMncs))
232                         .collect(Collectors.toList());
233             }
234 
235             CellInfo[] ci = result.toArray(new CellInfo[result.size()]);
236             Bundle b = new Bundle();
237             b.putParcelableArray(TelephonyScanManager.SCAN_RESULT_KEY, ci);
238             message.setData(b);
239         } else {
240             message.obj = null;
241         }
242         try {
243             messenger.send(message);
244         } catch (RemoteException e) {
245             Log.e(TAG, "Exception in notifyMessenger: " + e);
246         }
247     }
248 
249     /**
250     * Tracks info about the radio network scan.
251      *
252     * Also used to notice when the calling process dies so we can self-expire.
253     */
254     class NetworkScanRequestInfo implements IBinder.DeathRecipient {
255         private final NetworkScanRequest mRequest;
256         private final Messenger mMessenger;
257         private final IBinder mBinder;
258         private final Phone mPhone;
259         private final int mScanId;
260         private final int mUid;
261         private final int mPid;
262         private boolean mRenounceFineLocationAccess;
263         private final String mCallingPackage;
264         private boolean mIsBinderDead;
265 
NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone, int callingUid, int callingPid, String callingPackage, boolean renounceFineLocationAccess)266         NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone,
267                 int callingUid, int callingPid, String callingPackage,
268                 boolean renounceFineLocationAccess) {
269             super();
270             mRequest = r;
271             mMessenger = m;
272             mBinder = b;
273             mScanId = id;
274             mPhone = phone;
275             mUid = callingUid;
276             mPid = callingPid;
277             mCallingPackage = callingPackage;
278             mIsBinderDead = false;
279             mRenounceFineLocationAccess = renounceFineLocationAccess;
280 
281             try {
282                 mBinder.linkToDeath(this, 0);
283             } catch (RemoteException e) {
284                 binderDied();
285             }
286         }
287 
setIsBinderDead(boolean val)288         synchronized void setIsBinderDead(boolean val) {
289             mIsBinderDead = val;
290         }
291 
getIsBinderDead()292         synchronized boolean getIsBinderDead() {
293             return mIsBinderDead;
294         }
295 
getRequest()296         NetworkScanRequest getRequest() {
297             return mRequest;
298         }
299 
unlinkDeathRecipient()300         void unlinkDeathRecipient() {
301             if (mBinder != null) {
302                 mBinder.unlinkToDeath(this, 0);
303             }
304         }
305 
306         @Override
binderDied()307         public void binderDied() {
308             Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied("
309                     + mRequest + ", " + mBinder + ")");
310             setIsBinderDead(true);
311             interruptNetworkScan(mScanId);
312         }
313     }
314 
315     /**
316      * Handles multiplexing and scheduling for multiple requests.
317      */
318     private class NetworkScanRequestScheduler {
319 
320         private NetworkScanRequestInfo mLiveRequestInfo;
321         private NetworkScanRequestInfo mPendingRequestInfo;
322 
rilErrorToScanError(int rilError)323         private int rilErrorToScanError(int rilError) {
324             switch (rilError) {
325                 case RadioError.NONE:
326                     return NetworkScan.SUCCESS;
327                 case RadioError.RADIO_NOT_AVAILABLE:
328                     Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE");
329                     return NetworkScan.ERROR_MODEM_ERROR;
330                 case RadioError.REQUEST_NOT_SUPPORTED:
331                     Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED");
332                     return NetworkScan.ERROR_UNSUPPORTED;
333                 case RadioError.NO_MEMORY:
334                     Log.e(TAG, "rilErrorToScanError: NO_MEMORY");
335                     return NetworkScan.ERROR_MODEM_ERROR;
336                 case RadioError.INTERNAL_ERR:
337                     Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR");
338                     return NetworkScan.ERROR_MODEM_ERROR;
339                 case RadioError.MODEM_ERR:
340                     Log.e(TAG, "rilErrorToScanError: MODEM_ERR");
341                     return NetworkScan.ERROR_MODEM_ERROR;
342                 case RadioError.OPERATION_NOT_ALLOWED:
343                     Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED");
344                     return NetworkScan.ERROR_MODEM_ERROR;
345                 case RadioError.INVALID_ARGUMENTS:
346                     Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS");
347                     return NetworkScan.ERROR_INVALID_SCAN;
348                 case RadioError.DEVICE_IN_USE:
349                     Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
350                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
351                 default:
352                     Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " +  rilError);
353                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
354             }
355         }
356 
commandExceptionErrorToScanError(CommandException.Error error)357         private int commandExceptionErrorToScanError(CommandException.Error error) {
358             switch (error) {
359                 case RADIO_NOT_AVAILABLE:
360                     Log.e(TAG, "commandExceptionErrorToScanError: RADIO_NOT_AVAILABLE");
361                     return NetworkScan.ERROR_MODEM_ERROR;
362                 case REQUEST_NOT_SUPPORTED:
363                     Log.e(TAG, "commandExceptionErrorToScanError: REQUEST_NOT_SUPPORTED");
364                     return NetworkScan.ERROR_UNSUPPORTED;
365                 case NO_MEMORY:
366                     Log.e(TAG, "commandExceptionErrorToScanError: NO_MEMORY");
367                     return NetworkScan.ERROR_MODEM_ERROR;
368                 case INTERNAL_ERR:
369                     Log.e(TAG, "commandExceptionErrorToScanError: INTERNAL_ERR");
370                     return NetworkScan.ERROR_MODEM_ERROR;
371                 case MODEM_ERR:
372                     Log.e(TAG, "commandExceptionErrorToScanError: MODEM_ERR");
373                     return NetworkScan.ERROR_MODEM_ERROR;
374                 case OPERATION_NOT_ALLOWED:
375                     Log.e(TAG, "commandExceptionErrorToScanError: OPERATION_NOT_ALLOWED");
376                     return NetworkScan.ERROR_MODEM_ERROR;
377                 case INVALID_ARGUMENTS:
378                     Log.e(TAG, "commandExceptionErrorToScanError: INVALID_ARGUMENTS");
379                     return NetworkScan.ERROR_INVALID_SCAN;
380                 case DEVICE_IN_USE:
381                     Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
382                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
383                 default:
384                     Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
385                             +  error);
386                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
387             }
388         }
389 
doStartScan(NetworkScanRequestInfo nsri)390         private void doStartScan(NetworkScanRequestInfo nsri) {
391             if (nsri == null) {
392                 Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null");
393                 return;
394             }
395             if (!isValidScan(nsri)) {
396                 notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
397                         NetworkScan.ERROR_INVALID_SCAN, null);
398                 return;
399             }
400             if (nsri.getIsBinderDead()) {
401                 Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died");
402                 return;
403             }
404             if (!startNewScan(nsri)) {
405                 if (!interruptLiveScan(nsri)) {
406                     if (!cacheScan(nsri)) {
407                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
408                                 NetworkScan.ERROR_MODEM_UNAVAILABLE, null);
409                     }
410                 }
411             }
412         }
413 
startScanDone(AsyncResult ar)414         private synchronized void startScanDone(AsyncResult ar) {
415             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
416             if (nsri == null) {
417                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null");
418                 return;
419             }
420             if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) {
421                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo");
422                 return;
423             }
424             if (ar.exception == null && ar.result != null) {
425                 // Register for the scan results if the scan started successfully.
426                 nsri.mPhone.mCi.registerForNetworkScanResult(mHandler,
427                         EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
428             } else {
429                 logEmptyResultOrException(ar);
430                 if (ar.exception != null) {
431                     CommandException.Error error =
432                             ((CommandException) (ar.exception)).getCommandError();
433                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
434                 } else {
435                     Log.wtf(TAG, "EVENT_START_NETWORK_SCAN_DONE: ar.exception can not be null!");
436                 }
437             }
438         }
439 
receiveResult(AsyncResult ar)440         private void receiveResult(AsyncResult ar) {
441             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
442             if (nsri == null) {
443                 Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
444                 return;
445             }
446             LocationAccessPolicy.LocationPermissionQuery locationQuery =
447                     new LocationAccessPolicy.LocationPermissionQuery.Builder()
448                     .setCallingPackage(nsri.mCallingPackage)
449                     .setCallingPid(nsri.mPid)
450                     .setCallingUid(nsri.mUid)
451                     .setCallingFeatureId(nsri.mPhone.getContext().getAttributionTag())
452                     .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
453                     .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
454                     .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
455                     .setMethod("NetworkScanTracker#onResult")
456                     .build();
457             if (ar.exception == null && ar.result != null) {
458                 NetworkScanResult nsr = (NetworkScanResult) ar.result;
459                 boolean isLocationAccessAllowed = !nsri.mRenounceFineLocationAccess
460                         && LocationAccessPolicy.checkLocationPermission(
461                         nsri.mPhone.getContext(), locationQuery)
462                         == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
463                 int notifyMsg = isLocationAccessAllowed
464                         ? TelephonyScanManager.CALLBACK_SCAN_RESULTS
465                         : TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS;
466                 if (nsr.scanError == NetworkScan.SUCCESS) {
467                     if (nsri.mPhone.getServiceStateTracker() != null) {
468                         nsri.mPhone.getServiceStateTracker().updateOperatorNameForCellInfo(
469                                 nsr.networkInfos);
470                     }
471 
472                     notifyMessenger(nsri, notifyMsg,
473                             rilErrorToScanError(nsr.scanError), nsr.networkInfos);
474                     if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
475                         deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
476                         nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
477                     }
478                 } else {
479                     if (nsr.networkInfos != null) {
480                         notifyMessenger(nsri, notifyMsg,
481                                 rilErrorToScanError(nsr.scanError), nsr.networkInfos);
482                     }
483                     deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
484                     nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
485                 }
486             } else {
487                 logEmptyResultOrException(ar);
488                 deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
489                 nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
490             }
491         }
492 
493         // Stops the scan if the scanId and uid match the mScanId and mUid.
494         // If the scan to be stopped is the live scan, we only send the request to RIL, while the
495         // mLiveRequestInfo will not be cleared and the user will not be notified either.
496         // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and
497         // notify the user.
doStopScan(int scanId)498         private synchronized void doStopScan(int scanId) {
499             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
500                 mLiveRequestInfo.mPhone.stopNetworkScan(
501                         mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo));
502             } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) {
503                 notifyMessenger(mPendingRequestInfo,
504                         TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null);
505                 mPendingRequestInfo = null;
506             } else {
507                 Log.e(TAG, "stopScan: scan " + scanId + " does not exist!");
508             }
509         }
510 
stopScanDone(AsyncResult ar)511         private void stopScanDone(AsyncResult ar) {
512             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
513             if (nsri == null) {
514                 Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
515                 return;
516             }
517             if (ar.exception == null && ar.result != null) {
518                 deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
519             } else {
520                 logEmptyResultOrException(ar);
521                 if (ar.exception != null) {
522                     CommandException.Error error =
523                             ((CommandException) (ar.exception)).getCommandError();
524                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
525                 } else {
526                     Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
527                 }
528             }
529             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
530         }
531 
532         // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
doInterruptScan(int scanId)533         private synchronized void doInterruptScan(int scanId) {
534             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
535                 mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage(
536                         EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo));
537             } else {
538                 Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!");
539             }
540         }
541 
interruptScanDone(AsyncResult ar)542         private void interruptScanDone(AsyncResult ar) {
543             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
544             if (nsri == null) {
545                 Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null");
546                 return;
547             }
548             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
549             deleteScanAndMayNotify(nsri, 0, false);
550         }
551 
552         // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is
553         // stopped, a new scan will automatically start with nsri.
554         // The new scan can interrupt the live scan only when all the below requirements are met:
555         //   1. There is 1 live scan and no other pending scan
556         //   2. The new scan is requested by mobile network setting menu (owned by SYSTEM process)
557         //   3. The live scan is not requested by mobile network setting menu
interruptLiveScan(NetworkScanRequestInfo nsri)558         private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
559             if (mLiveRequestInfo != null && mPendingRequestInfo == null
560                     && nsri.mUid == Process.SYSTEM_UID
561                             && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
562                 doInterruptScan(mLiveRequestInfo.mScanId);
563                 mPendingRequestInfo = nsri;
564                 notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
565                         NetworkScan.ERROR_INTERRUPTED, null);
566                 return true;
567             }
568             return false;
569         }
570 
cacheScan(NetworkScanRequestInfo nsri)571         private boolean cacheScan(NetworkScanRequestInfo nsri) {
572             // TODO(30954762): Cache periodic scan for OC-MR1.
573             return false;
574         }
575 
576         // Starts a new scan with nsri if there is no live scan running.
startNewScan(NetworkScanRequestInfo nsri)577         private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
578             if (mLiveRequestInfo == null) {
579                 mLiveRequestInfo = nsri;
580                 nsri.mPhone.startNetworkScan(nsri.getRequest(),
581                         mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
582                 nsri.mPhone.mCi.registerForModemReset(mHandler, EVENT_MODEM_RESET, nsri);
583                 nsri.mPhone.mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, nsri);
584                 return true;
585             }
586             return false;
587         }
588 
589 
590         // Deletes the mLiveRequestInfo and notify the user if it matches nsri.
deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error, boolean notify)591         private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error,
592                 boolean notify) {
593             if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) {
594                 if (notify) {
595                     if (error == NetworkScan.SUCCESS) {
596                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error,
597                                 null);
598                     } else {
599                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error,
600                                 null);
601                     }
602                 }
603                 mLiveRequestInfo.mPhone.mCi.unregisterForModemReset(mHandler);
604                 mLiveRequestInfo.mPhone.mCi.unregisterForNotAvailable(mHandler);
605                 mLiveRequestInfo = null;
606                 if (mPendingRequestInfo != null) {
607                     startNewScan(mPendingRequestInfo);
608                     mPendingRequestInfo = null;
609                 }
610             }
611         }
612     }
613 
614     /**
615      * Interrupts an ongoing network scan
616      *
617      * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
618      * difference is that stopNetworkScan is only used by the callers to stop their own scans, so
619      * correctness check will be done to make sure the request is valid; while this method is only
620      * internally used by NetworkScanRequestTracker so correctness check is not needed.
621      */
interruptNetworkScan(int scanId)622     private void interruptNetworkScan(int scanId) {
623         // scanId will be stored at Message.arg1
624         mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget();
625     }
626 
627     /**
628      * Starts a new network scan
629      *
630      * This function only wraps all the incoming information and delegate then to the handler thread
631      * which will actually handles the scan request. So a new scanId will always be generated and
632      * returned to the user, no matter how this scan will be actually handled.
633      */
startNetworkScan( boolean renounceFineLocationAccess, NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone, int callingUid, int callingPid, String callingPackage)634     public int startNetworkScan(
635             boolean renounceFineLocationAccess, NetworkScanRequest request, Messenger messenger,
636             IBinder binder, Phone phone,
637             int callingUid, int callingPid, String callingPackage) {
638         int scanId = mNextNetworkScanRequestId.getAndIncrement();
639         NetworkScanRequestInfo nsri =
640                 new NetworkScanRequestInfo(request, messenger, binder, scanId, phone,
641                         callingUid, callingPid, callingPackage, renounceFineLocationAccess);
642         // nsri will be stored as Message.obj
643         mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
644         return scanId;
645     }
646 
647     /**
648      * Stops an ongoing network scan
649      *
650      * The ongoing scan will be stopped only when the input scanId and caller's uid matches the
651      * corresponding information associated with it.
652      */
stopNetworkScan(int scanId, int callingUid)653     public void stopNetworkScan(int scanId, int callingUid) {
654         synchronized (mScheduler) {
655             if ((mScheduler.mLiveRequestInfo != null
656                     && scanId == mScheduler.mLiveRequestInfo.mScanId
657                     && callingUid == mScheduler.mLiveRequestInfo.mUid)
658                     || (mScheduler.mPendingRequestInfo != null
659                     && scanId == mScheduler.mPendingRequestInfo.mScanId
660                     && callingUid == mScheduler.mPendingRequestInfo.mUid)) {
661                 // scanId will be stored at Message.arg1
662                 mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget();
663             } else {
664                 throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!");
665             }
666         }
667     }
668 }
669