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