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