Home | History | Annotate | Download | only in telephony
      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.UTRAN;
     22 
     23 import android.hardware.radio.V1_0.RadioError;
     24 import android.os.AsyncResult;
     25 import android.os.Binder;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.os.IBinder;
     29 import android.os.Message;
     30 import android.os.Messenger;
     31 import android.os.Process;
     32 import android.os.RemoteException;
     33 import android.telephony.CellInfo;
     34 import android.telephony.NetworkScan;
     35 import android.telephony.NetworkScanRequest;
     36 import android.telephony.RadioAccessSpecifier;
     37 import android.telephony.TelephonyScanManager;
     38 import android.util.Log;
     39 
     40 import java.util.List;
     41 import java.util.concurrent.atomic.AtomicInteger;
     42 
     43 /**
     44  * Manages radio access network scan requests.
     45  *
     46  * Provides methods to start and stop network scan requests, and keeps track of all the live scans.
     47  *
     48  * {@hide}
     49  */
     50 public final class NetworkScanRequestTracker {
     51 
     52     private static final String TAG = "ScanRequestTracker";
     53 
     54     private static final int CMD_START_NETWORK_SCAN = 1;
     55     private static final int EVENT_START_NETWORK_SCAN_DONE = 2;
     56     private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3;
     57     private static final int CMD_STOP_NETWORK_SCAN = 4;
     58     private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5;
     59     private static final int CMD_INTERRUPT_NETWORK_SCAN = 6;
     60     private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7;
     61 
     62     private final Handler mHandler = new Handler() {
     63         @Override
     64         public void handleMessage(Message msg) {
     65             switch (msg.what) {
     66                 case CMD_START_NETWORK_SCAN:
     67                     mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj);
     68                     break;
     69 
     70                 case EVENT_START_NETWORK_SCAN_DONE:
     71                     mScheduler.startScanDone((AsyncResult) msg.obj);
     72                     break;
     73 
     74                 case EVENT_RECEIVE_NETWORK_SCAN_RESULT:
     75                     mScheduler.receiveResult((AsyncResult) msg.obj);
     76                     break;
     77 
     78                 case CMD_STOP_NETWORK_SCAN:
     79                     mScheduler.doStopScan(msg.arg1);
     80                     break;
     81 
     82                 case EVENT_STOP_NETWORK_SCAN_DONE:
     83                     mScheduler.stopScanDone((AsyncResult) msg.obj);
     84                     break;
     85 
     86                 case CMD_INTERRUPT_NETWORK_SCAN:
     87                     mScheduler.doInterruptScan(msg.arg1);
     88                     break;
     89 
     90                 case EVENT_INTERRUPT_NETWORK_SCAN_DONE:
     91                     mScheduler.interruptScanDone((AsyncResult) msg.obj);
     92                     break;
     93             }
     94         }
     95     };
     96 
     97     // The sequence number of NetworkScanRequests
     98     private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1);
     99     private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler();
    100 
    101     private void logEmptyResultOrException(AsyncResult ar) {
    102         if (ar.result == null) {
    103             Log.e(TAG, "NetworkScanResult: Empty result");
    104         } else {
    105             Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception);
    106         }
    107     }
    108 
    109     private boolean isValidScan(NetworkScanRequestInfo nsri) {
    110         if (nsri.mRequest == null || nsri.mRequest.getSpecifiers() == null) {
    111             return false;
    112         }
    113         if (nsri.mRequest.getSpecifiers().length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
    114             return false;
    115         }
    116         for (RadioAccessSpecifier ras : nsri.mRequest.getSpecifiers()) {
    117             if (ras.getRadioAccessNetwork() != GERAN && ras.getRadioAccessNetwork() != UTRAN
    118                     && ras.getRadioAccessNetwork() != EUTRAN) {
    119                 return false;
    120             }
    121             if (ras.getBands() != null && ras.getBands().length > NetworkScanRequest.MAX_BANDS) {
    122                 return false;
    123             }
    124             if (ras.getChannels() != null
    125                     && ras.getChannels().length > NetworkScanRequest.MAX_CHANNELS) {
    126                 return false;
    127             }
    128         }
    129 
    130         if ((nsri.mRequest.getSearchPeriodicity() < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
    131                 || (nsri.mRequest.getSearchPeriodicity()
    132                 > NetworkScanRequest.MAX_SEARCH_PERIODICITY_SEC)) {
    133             return false;
    134         }
    135 
    136         if ((nsri.mRequest.getMaxSearchTime() < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
    137                 || (nsri.mRequest.getMaxSearchTime() > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
    138             return false;
    139         }
    140 
    141         if ((nsri.mRequest.getIncrementalResultsPeriodicity()
    142                 < NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC)
    143                 || (nsri.mRequest.getIncrementalResultsPeriodicity()
    144                 > NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC)) {
    145             return false;
    146         }
    147 
    148         if ((nsri.mRequest.getSearchPeriodicity() > nsri.mRequest.getMaxSearchTime())
    149                 || (nsri.mRequest.getIncrementalResultsPeriodicity()
    150                         > nsri.mRequest.getMaxSearchTime())) {
    151             return false;
    152         }
    153 
    154         if ((nsri.mRequest.getPlmns() != null)
    155                 && (nsri.mRequest.getPlmns().size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
    156             return false;
    157         }
    158         return true;
    159     }
    160 
    161     /** Sends a message back to the application via its callback. */
    162     private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err,
    163             List<CellInfo> result) {
    164         Messenger messenger = nsri.mMessenger;
    165         Message message = Message.obtain();
    166         message.what = what;
    167         message.arg1 = err;
    168         message.arg2 = nsri.mScanId;
    169         if (result != null) {
    170             CellInfo[] ci = result.toArray(new CellInfo[result.size()]);
    171             Bundle b = new Bundle();
    172             b.putParcelableArray(TelephonyScanManager.SCAN_RESULT_KEY, ci);
    173             message.setData(b);
    174         } else {
    175             message.obj = null;
    176         }
    177         try {
    178             messenger.send(message);
    179         } catch (RemoteException e) {
    180             Log.e(TAG, "Exception in notifyMessenger: " + e);
    181         }
    182     }
    183 
    184     /**
    185     * Tracks info about the radio network scan.
    186      *
    187     * Also used to notice when the calling process dies so we can self-expire.
    188     */
    189     class NetworkScanRequestInfo implements IBinder.DeathRecipient {
    190         private final NetworkScanRequest mRequest;
    191         private final Messenger mMessenger;
    192         private final IBinder mBinder;
    193         private final Phone mPhone;
    194         private final int mScanId;
    195         private final int mUid;
    196         private final int mPid;
    197         private boolean mIsBinderDead;
    198 
    199         NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone) {
    200             super();
    201             mRequest = r;
    202             mMessenger = m;
    203             mBinder = b;
    204             mScanId = id;
    205             mPhone = phone;
    206             mUid = Binder.getCallingUid();
    207             mPid = Binder.getCallingPid();
    208             mIsBinderDead = false;
    209 
    210             try {
    211                 mBinder.linkToDeath(this, 0);
    212             } catch (RemoteException e) {
    213                 binderDied();
    214             }
    215         }
    216 
    217         synchronized void setIsBinderDead(boolean val) {
    218             mIsBinderDead = val;
    219         }
    220 
    221         synchronized boolean getIsBinderDead() {
    222             return mIsBinderDead;
    223         }
    224 
    225         NetworkScanRequest getRequest() {
    226             return mRequest;
    227         }
    228 
    229         void unlinkDeathRecipient() {
    230             if (mBinder != null) {
    231                 mBinder.unlinkToDeath(this, 0);
    232             }
    233         }
    234 
    235         @Override
    236         public void binderDied() {
    237             Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied("
    238                     + mRequest + ", " + mBinder + ")");
    239             setIsBinderDead(true);
    240             interruptNetworkScan(mScanId);
    241         }
    242     }
    243 
    244     /**
    245      * Handles multiplexing and scheduling for multiple requests.
    246      */
    247     private class NetworkScanRequestScheduler {
    248 
    249         private NetworkScanRequestInfo mLiveRequestInfo;
    250         private NetworkScanRequestInfo mPendingRequestInfo;
    251 
    252         private int rilErrorToScanError(int rilError) {
    253             switch (rilError) {
    254                 case RadioError.NONE:
    255                     return NetworkScan.SUCCESS;
    256                 case RadioError.RADIO_NOT_AVAILABLE:
    257                     Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE");
    258                     return NetworkScan.ERROR_MODEM_ERROR;
    259                 case RadioError.REQUEST_NOT_SUPPORTED:
    260                     Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED");
    261                     return NetworkScan.ERROR_UNSUPPORTED;
    262                 case RadioError.NO_MEMORY:
    263                     Log.e(TAG, "rilErrorToScanError: NO_MEMORY");
    264                     return NetworkScan.ERROR_MODEM_ERROR;
    265                 case RadioError.INTERNAL_ERR:
    266                     Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR");
    267                     return NetworkScan.ERROR_MODEM_ERROR;
    268                 case RadioError.MODEM_ERR:
    269                     Log.e(TAG, "rilErrorToScanError: MODEM_ERR");
    270                     return NetworkScan.ERROR_MODEM_ERROR;
    271                 case RadioError.OPERATION_NOT_ALLOWED:
    272                     Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED");
    273                     return NetworkScan.ERROR_MODEM_ERROR;
    274                 case RadioError.INVALID_ARGUMENTS:
    275                     Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS");
    276                     return NetworkScan.ERROR_INVALID_SCAN;
    277                 case RadioError.DEVICE_IN_USE:
    278                     Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
    279                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
    280                 default:
    281                     Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " +  rilError);
    282                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
    283             }
    284         }
    285 
    286         private int commandExceptionErrorToScanError(CommandException.Error error) {
    287             switch (error) {
    288                 case RADIO_NOT_AVAILABLE:
    289                     Log.e(TAG, "commandExceptionErrorToScanError: RADIO_NOT_AVAILABLE");
    290                     return NetworkScan.ERROR_MODEM_ERROR;
    291                 case REQUEST_NOT_SUPPORTED:
    292                     Log.e(TAG, "commandExceptionErrorToScanError: REQUEST_NOT_SUPPORTED");
    293                     return NetworkScan.ERROR_UNSUPPORTED;
    294                 case NO_MEMORY:
    295                     Log.e(TAG, "commandExceptionErrorToScanError: NO_MEMORY");
    296                     return NetworkScan.ERROR_MODEM_ERROR;
    297                 case INTERNAL_ERR:
    298                     Log.e(TAG, "commandExceptionErrorToScanError: INTERNAL_ERR");
    299                     return NetworkScan.ERROR_MODEM_ERROR;
    300                 case MODEM_ERR:
    301                     Log.e(TAG, "commandExceptionErrorToScanError: MODEM_ERR");
    302                     return NetworkScan.ERROR_MODEM_ERROR;
    303                 case OPERATION_NOT_ALLOWED:
    304                     Log.e(TAG, "commandExceptionErrorToScanError: OPERATION_NOT_ALLOWED");
    305                     return NetworkScan.ERROR_MODEM_ERROR;
    306                 case INVALID_ARGUMENTS:
    307                     Log.e(TAG, "commandExceptionErrorToScanError: INVALID_ARGUMENTS");
    308                     return NetworkScan.ERROR_INVALID_SCAN;
    309                 case DEVICE_IN_USE:
    310                     Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
    311                     return NetworkScan.ERROR_MODEM_UNAVAILABLE;
    312                 default:
    313                     Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
    314                             +  error);
    315                     return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
    316             }
    317         }
    318 
    319         private void doStartScan(NetworkScanRequestInfo nsri) {
    320             if (nsri == null) {
    321                 Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null");
    322                 return;
    323             }
    324             if (!isValidScan(nsri)) {
    325                 notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
    326                         NetworkScan.ERROR_INVALID_SCAN, null);
    327                 return;
    328             }
    329             if (nsri.getIsBinderDead()) {
    330                 Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died");
    331                 return;
    332             }
    333             if (!startNewScan(nsri)) {
    334                 if (!interruptLiveScan(nsri)) {
    335                     if (!cacheScan(nsri)) {
    336                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
    337                                 NetworkScan.ERROR_MODEM_UNAVAILABLE, null);
    338                     }
    339                 }
    340             }
    341         }
    342 
    343         private synchronized void startScanDone(AsyncResult ar) {
    344             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
    345             if (nsri == null) {
    346                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null");
    347                 return;
    348             }
    349             if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) {
    350                 Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo");
    351                 return;
    352             }
    353             if (ar.exception == null && ar.result != null) {
    354                 // Register for the scan results if the scan started successfully.
    355                 nsri.mPhone.mCi.registerForNetworkScanResult(mHandler,
    356                         EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
    357             } else {
    358                 logEmptyResultOrException(ar);
    359                 if (ar.exception != null) {
    360                     CommandException.Error error =
    361                             ((CommandException) (ar.exception)).getCommandError();
    362                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
    363                 } else {
    364                     Log.wtf(TAG, "EVENT_START_NETWORK_SCAN_DONE: ar.exception can not be null!");
    365                 }
    366             }
    367         }
    368 
    369         private void receiveResult(AsyncResult ar) {
    370             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
    371             if (nsri == null) {
    372                 Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
    373                 return;
    374             }
    375             if (ar.exception == null && ar.result != null) {
    376                 NetworkScanResult nsr = (NetworkScanResult) ar.result;
    377                 if (nsr.scanError == NetworkScan.SUCCESS) {
    378                     notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
    379                             rilErrorToScanError(nsr.scanError), nsr.networkInfos);
    380                     if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
    381                         deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
    382                         nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
    383                     }
    384                 } else {
    385                     if (nsr.networkInfos != null) {
    386                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
    387                                 NetworkScan.SUCCESS, nsr.networkInfos);
    388                     }
    389                     deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
    390                     nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
    391                 }
    392             } else {
    393                 logEmptyResultOrException(ar);
    394                 deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
    395                 nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
    396             }
    397         }
    398 
    399 
    400         // Stops the scan if the scanId and uid match the mScanId and mUid.
    401         // If the scan to be stopped is the live scan, we only send the request to RIL, while the
    402         // mLiveRequestInfo will not be cleared and the user will not be notified either.
    403         // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and
    404         // notify the user.
    405         private synchronized void doStopScan(int scanId) {
    406             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
    407                 mLiveRequestInfo.mPhone.stopNetworkScan(
    408                         mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo));
    409             } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) {
    410                 notifyMessenger(mPendingRequestInfo,
    411                         TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null);
    412                 mPendingRequestInfo = null;
    413             } else {
    414                 Log.e(TAG, "stopScan: scan " + scanId + " does not exist!");
    415             }
    416         }
    417 
    418         private void stopScanDone(AsyncResult ar) {
    419             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
    420             if (nsri == null) {
    421                 Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
    422                 return;
    423             }
    424             if (ar.exception == null && ar.result != null) {
    425                 deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
    426             } else {
    427                 logEmptyResultOrException(ar);
    428                 if (ar.exception != null) {
    429                     CommandException.Error error =
    430                             ((CommandException) (ar.exception)).getCommandError();
    431                     deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
    432                 } else {
    433                     Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
    434                 }
    435             }
    436             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
    437         }
    438 
    439         // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
    440         private synchronized void doInterruptScan(int scanId) {
    441             if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
    442                 mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage(
    443                         EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo));
    444             } else {
    445                 Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!");
    446             }
    447         }
    448 
    449         private void interruptScanDone(AsyncResult ar) {
    450             NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
    451             if (nsri == null) {
    452                 Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null");
    453                 return;
    454             }
    455             nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
    456             deleteScanAndMayNotify(nsri, 0, false);
    457         }
    458 
    459         // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is
    460         // stopped, a new scan will automatically start with nsri.
    461         // The new scan can interrupt the live scan only when all the below requirements are met:
    462         //   1. There is 1 live scan and no other pending scan
    463         //   2. The new scan is requested by mobile network setting menu (owned by PHONE process)
    464         //   3. The live scan is not requested by mobile network setting menu
    465         private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
    466             if (mLiveRequestInfo != null && mPendingRequestInfo == null
    467                     && nsri.mUid == Process.PHONE_UID
    468                             && mLiveRequestInfo.mUid != Process.PHONE_UID) {
    469                 doInterruptScan(mLiveRequestInfo.mScanId);
    470                 mPendingRequestInfo = nsri;
    471                 notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
    472                         NetworkScan.ERROR_INTERRUPTED, null);
    473                 return true;
    474             }
    475             return false;
    476         }
    477 
    478         private boolean cacheScan(NetworkScanRequestInfo nsri) {
    479             // TODO(30954762): Cache periodic scan for OC-MR1.
    480             return false;
    481         }
    482 
    483         // Starts a new scan with nsri if there is no live scan running.
    484         private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
    485             if (mLiveRequestInfo == null) {
    486                 mLiveRequestInfo = nsri;
    487                 nsri.mPhone.startNetworkScan(nsri.getRequest(),
    488                         mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
    489                 return true;
    490             }
    491             return false;
    492         }
    493 
    494 
    495         // Deletes the mLiveRequestInfo and notify the user if it matches nsri.
    496         private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error,
    497                 boolean notify) {
    498             if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) {
    499                 if (notify) {
    500                     if (error == NetworkScan.SUCCESS) {
    501                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error,
    502                                 null);
    503                     } else {
    504                         notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error,
    505                                 null);
    506                     }
    507                 }
    508                 mLiveRequestInfo = null;
    509                 if (mPendingRequestInfo != null) {
    510                     startNewScan(mPendingRequestInfo);
    511                     mPendingRequestInfo = null;
    512                 }
    513             }
    514         }
    515     }
    516 
    517     /**
    518      * Interrupts an ongoing network scan
    519      *
    520      * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
    521      * difference is that stopNetworkScan is only used by the callers to stop their own scans, so
    522      * sanity check will be done to make sure the request is valid; while this method is only
    523      * internally used by NetworkScanRequestTracker so sanity check is not needed.
    524      */
    525     private void interruptNetworkScan(int scanId) {
    526         // scanId will be stored at Message.arg1
    527         mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget();
    528     }
    529 
    530     /**
    531      * Starts a new network scan
    532      *
    533      * This function only wraps all the incoming information and delegate then to the handler thread
    534      * which will actually handles the scan request. So a new scanId will always be generated and
    535      * returned to the user, no matter how this scan will be actually handled.
    536      */
    537     public int startNetworkScan(
    538             NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone) {
    539         int scanId = mNextNetworkScanRequestId.getAndIncrement();
    540         NetworkScanRequestInfo nsri =
    541                 new NetworkScanRequestInfo(request, messenger, binder, scanId, phone);
    542         // nsri will be stored as Message.obj
    543         mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
    544         return scanId;
    545     }
    546 
    547     /**
    548      * Stops an ongoing network scan
    549      *
    550      * The ongoing scan will be stopped only when the input scanId and caller's uid matches the
    551      * corresponding information associated with it.
    552      */
    553     public void stopNetworkScan(int scanId) {
    554         synchronized (mScheduler) {
    555             if ((mScheduler.mLiveRequestInfo != null
    556                     && scanId == mScheduler.mLiveRequestInfo.mScanId
    557                     && Binder.getCallingUid() == mScheduler.mLiveRequestInfo.mUid)
    558                     || (mScheduler.mPendingRequestInfo != null
    559                     && scanId == mScheduler.mPendingRequestInfo.mScanId
    560                     && Binder.getCallingUid() == mScheduler.mPendingRequestInfo.mUid)) {
    561                 // scanId will be stored at Message.arg1
    562                 mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget();
    563             } else {
    564                 throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!");
    565             }
    566         }
    567     }
    568 }
    569