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