Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2008 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.phone;
     18 
     19 import android.app.Service;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.AsyncResult;
     23 import android.os.Binder;
     24 import android.os.Handler;
     25 import android.os.IBinder;
     26 import android.os.Message;
     27 import android.os.RemoteCallbackList;
     28 import android.os.RemoteException;
     29 import android.telephony.AccessNetworkConstants;
     30 import android.telephony.CellIdentityGsm;
     31 import android.telephony.CellInfo;
     32 import android.telephony.CellInfoGsm;
     33 import android.telephony.NetworkScan;
     34 import android.telephony.NetworkScanRequest;
     35 import android.telephony.RadioAccessSpecifier;
     36 import android.telephony.TelephonyManager;
     37 import android.telephony.TelephonyScanManager;
     38 import android.util.Log;
     39 
     40 import com.android.internal.telephony.OperatorInfo;
     41 import com.android.internal.telephony.Phone;
     42 import com.android.internal.telephony.PhoneFactory;
     43 
     44 import java.util.ArrayList;
     45 import java.util.List;
     46 
     47 /**
     48  * Service code used to assist in querying the network for service
     49  * availability.
     50  */
     51 public class NetworkQueryService extends Service {
     52     // debug data
     53     private static final String LOG_TAG = "NetworkQuery";
     54     private static final boolean DBG = true;
     55 
     56     // static events
     57     private static final int EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED = 100;
     58     private static final int EVENT_NETWORK_SCAN_RESULTS = 200;
     59     private static final int EVENT_NETWORK_SCAN_ERROR = 300;
     60     private static final int EVENT_NETWORK_SCAN_COMPLETED = 400;
     61 
     62     // static states indicating the query status of the service
     63     private static final int QUERY_READY = -1;
     64     private static final int QUERY_IS_RUNNING = -2;
     65 
     66     // error statuses that will be retured in the callback.
     67     public static final int QUERY_OK = 0;
     68     public static final int QUERY_EXCEPTION = 1;
     69 
     70     static final String ACTION_LOCAL_BINDER = "com.android.phone.intent.action.LOCAL_BINDER";
     71 
     72     /** state of the query service */
     73     private int mState;
     74 
     75     private NetworkScan mNetworkScan;
     76 
     77     // NetworkScanRequest parameters
     78     private static final int SCAN_TYPE = NetworkScanRequest.SCAN_TYPE_ONE_SHOT;
     79     private static final boolean INCREMENTAL_RESULTS = true;
     80     // The parameters below are in seconds
     81     private static final int SEARCH_PERIODICITY_SEC = 5;
     82     private static final int MAX_SEARCH_TIME_SEC = 300;
     83     private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
     84 
     85     /**
     86      * Class for clients to access.  Because we know this service always
     87      * runs in the same process as its clients, we don't need to deal with
     88      * IPC.
     89      */
     90     public class LocalBinder extends Binder {
     91         INetworkQueryService getService() {
     92             return mBinder;
     93         }
     94     }
     95     private final IBinder mLocalBinder = new LocalBinder();
     96 
     97     /**
     98      * Local handler to receive the network query compete callback
     99      * from the RIL.
    100      */
    101     Handler mHandler = new Handler() {
    102         @Override
    103         public void handleMessage(Message msg) {
    104             switch (msg.what) {
    105                 // if the scan is complete, broadcast the results.
    106                 // to all registerd callbacks.
    107                 case EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED:
    108                     if (DBG) log("scan via Phone completed, broadcasting results");
    109                     broadcastQueryResults(msg);
    110                     break;
    111 
    112                 case EVENT_NETWORK_SCAN_RESULTS:
    113                     if (DBG) log("get scan results, broadcasting results");
    114                     broadcastQueryResults(msg);
    115                     break;
    116 
    117                 case EVENT_NETWORK_SCAN_ERROR:
    118                     if (DBG) log("get scan error, broadcasting error code");
    119                     broadcastQueryResults(msg);
    120                     break;
    121 
    122                 case EVENT_NETWORK_SCAN_COMPLETED:
    123                     if (DBG) log("network scan or stop network query completed");
    124                     broadcastQueryResults(msg);
    125                     break;
    126             }
    127         }
    128     };
    129 
    130     /**
    131      * List of callback objects, also used to synchronize access to
    132      * itself and to changes in state.
    133      */
    134     final RemoteCallbackList<INetworkQueryServiceCallback> mCallbacks =
    135             new RemoteCallbackList<INetworkQueryServiceCallback>();
    136 
    137     /**
    138      * This implementation of NetworkScanCallbackImpl is used to receive callback notifications from
    139      * the Telephony Manager.
    140      */
    141     public class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback {
    142 
    143         /** Returns the scan results to the user, this callback will be called at least one time. */
    144         public void onResults(List<CellInfo> results) {
    145             if (DBG) log("got network scan results: " + results.size());
    146             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
    147             msg.sendToTarget();
    148         }
    149 
    150         /**
    151          * Informs the user that the scan has stopped.
    152          *
    153          * This callback will be called when the scan is finished or cancelled by the user.
    154          * The related NetworkScanRequest will be deleted after this callback.
    155          */
    156         public void onComplete() {
    157             if (DBG) log("network scan completed");
    158             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
    159             msg.sendToTarget();
    160         }
    161 
    162         /**
    163          * Informs the user that there is some error about the scan.
    164          *
    165          * This callback will be called whenever there is any error about the scan, and the scan
    166          * will be terminated. onComplete() will NOT be called.
    167          */
    168         public void onError(int error) {
    169             if (DBG) log("network scan got error: " + error);
    170             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
    171             msg.sendToTarget();
    172         }
    173     }
    174 
    175     /**
    176      * Implementation of the INetworkQueryService interface.
    177      */
    178     private final INetworkQueryService.Stub mBinder = new INetworkQueryService.Stub() {
    179 
    180         /**
    181          * Starts a query with a INetworkQueryServiceCallback object if
    182          * one has not been started yet.  Ignore the new query request
    183          * if the query has been started already.  Either way, place the
    184          * callback object in the queue to be notified upon request
    185          * completion.
    186          */
    187         public void startNetworkQuery(
    188                 INetworkQueryServiceCallback cb, int phoneId, boolean isIncrementalResult) {
    189             if (cb != null) {
    190                 // register the callback to the list of callbacks.
    191                 synchronized (mCallbacks) {
    192                     mCallbacks.register(cb);
    193                     if (DBG) log("registering callback " + cb.getClass().toString());
    194 
    195                     switch (mState) {
    196                         case QUERY_READY:
    197 
    198                             if (isIncrementalResult) {
    199                                 if (DBG) log("start network scan via TelephonManager");
    200                                 TelephonyManager tm = (TelephonyManager) getSystemService(
    201                                         Context.TELEPHONY_SERVICE);
    202                                 // The Radio Access Specifiers below are meant to scan
    203                                 // all the bands for all the supported technologies.
    204                                 RadioAccessSpecifier gsm = new RadioAccessSpecifier(
    205                                         AccessNetworkConstants.AccessNetworkType.GERAN,
    206                                         null /* bands */,
    207                                         null /* channels */);
    208                                 RadioAccessSpecifier lte = new RadioAccessSpecifier(
    209                                         AccessNetworkConstants.AccessNetworkType.EUTRAN,
    210                                         null /* bands */,
    211                                         null /* channels */);
    212                                 RadioAccessSpecifier wcdma = new RadioAccessSpecifier(
    213                                         AccessNetworkConstants.AccessNetworkType.UTRAN,
    214                                         null /* bands */,
    215                                         null /* channels */);
    216                                 RadioAccessSpecifier[] radioAccessSpecifier = {gsm, lte, wcdma};
    217                                 NetworkScanRequest networkScanRequest = new NetworkScanRequest(
    218                                         SCAN_TYPE,
    219                                         radioAccessSpecifier,
    220                                         SEARCH_PERIODICITY_SEC,
    221                                         MAX_SEARCH_TIME_SEC,
    222                                         INCREMENTAL_RESULTS,
    223                                         INCREMENTAL_RESULTS_PERIODICITY_SEC,
    224                                         null /* List of PLMN ids (MCC-MNC) */);
    225 
    226                                 // Construct a NetworkScanCallback
    227                                 NetworkQueryService.NetworkScanCallbackImpl
    228                                         networkScanCallback =
    229                                         new NetworkQueryService.NetworkScanCallbackImpl();
    230 
    231                                 // Request network scan
    232                                 mNetworkScan = tm.requestNetworkScan(networkScanRequest,
    233                                         networkScanCallback);
    234                                 mState = QUERY_IS_RUNNING;
    235                             } else {
    236                                 Phone phone = PhoneFactory.getPhone(phoneId);
    237                                 if (phone != null) {
    238                                     phone.getAvailableNetworks(
    239                                             mHandler.obtainMessage(
    240                                                     EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED));
    241                                     mState = QUERY_IS_RUNNING;
    242                                     if (DBG) log("start network scan via Phone");
    243                                 } else {
    244                                     if (DBG) {
    245                                         log("phone is null");
    246                                     }
    247                                 }
    248                             }
    249 
    250                             break;
    251                         // do nothing if we're currently busy.
    252                         case QUERY_IS_RUNNING:
    253                             if (DBG) log("query already in progress");
    254                             break;
    255                         default:
    256                     }
    257                 }
    258             }
    259         }
    260 
    261         /**
    262          * Stops a query with a INetworkQueryServiceCallback object as
    263          * a token.
    264          */
    265         public void stopNetworkQuery() {
    266             if (DBG) log("stop network query");
    267             // Tells the RIL to terminate the query request.
    268             if (mNetworkScan != null) {
    269                 try {
    270                     mNetworkScan.stop();
    271                     mState = QUERY_READY;
    272                 } catch (RemoteException e) {
    273                     if (DBG) log("stop mNetworkScan failed");
    274                 } catch (IllegalArgumentException e) {
    275                     // Do nothing, scan has already completed.
    276                 }
    277             }
    278         }
    279 
    280         /**
    281          * Unregisters the callback without impacting an underlying query.
    282          */
    283         public void unregisterCallback(INetworkQueryServiceCallback cb) {
    284             if (cb != null) {
    285                 synchronized (mCallbacks) {
    286                     if (DBG) log("unregistering callback " + cb.getClass().toString());
    287                     mCallbacks.unregister(cb);
    288                 }
    289             }
    290         }
    291     };
    292 
    293     @Override
    294     public void onCreate() {
    295         mState = QUERY_READY;
    296     }
    297 
    298     /**
    299      * Required for service implementation.
    300      */
    301     @Override
    302     public void onStart(Intent intent, int startId) {
    303     }
    304 
    305     /**
    306      * Handle the bind request.
    307      */
    308     @Override
    309     public IBinder onBind(Intent intent) {
    310         if (DBG) log("binding service implementation");
    311         if (ACTION_LOCAL_BINDER.equals(intent.getAction())) {
    312             return mLocalBinder;
    313         }
    314 
    315         return mBinder;
    316     }
    317 
    318     /**
    319      * Broadcast the results from the query to all registered callback
    320      * objects.
    321      */
    322     private void broadcastQueryResults(Message msg) {
    323         // reset the state.
    324         synchronized (mCallbacks) {
    325             mState = QUERY_READY;
    326 
    327             // Make the calls to all the registered callbacks.
    328             for (int i = (mCallbacks.beginBroadcast() - 1); i >= 0; i--) {
    329                 INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i);
    330                 if (DBG) log("broadcasting results to " + cb.getClass().toString());
    331                 try {
    332                     switch (msg.what) {
    333                         case EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED:
    334                             AsyncResult ar = (AsyncResult) msg.obj;
    335                             if (ar != null) {
    336                                 cb.onResults(getCellInfoList((List<OperatorInfo>) ar.result));
    337                             } else {
    338                                 if (DBG) log("AsyncResult is null.");
    339                             }
    340                             // Send the onComplete() callback to indicate the one-time network
    341                             // scan has completed.
    342                             cb.onComplete();
    343                             break;
    344 
    345                         case EVENT_NETWORK_SCAN_RESULTS:
    346                             cb.onResults((List<CellInfo>) msg.obj);
    347                             break;
    348 
    349                         case EVENT_NETWORK_SCAN_COMPLETED:
    350                             cb.onComplete();
    351                             break;
    352 
    353                         case EVENT_NETWORK_SCAN_ERROR:
    354                             cb.onError(msg.arg1);
    355                             break;
    356                     }
    357                 } catch (RemoteException e) {
    358                 }
    359             }
    360 
    361             // finish up.
    362             mCallbacks.finishBroadcast();
    363         }
    364     }
    365 
    366     /**
    367      * Wraps up a list of OperatorInfo object to a list of CellInfo object. GsmCellInfo is used here
    368      * only because operatorInfo does not contain technology type while CellInfo is an abstract
    369      * object that requires to specify technology type. It doesn't matter which CellInfo type to
    370      * use here, since we only want to wrap the operator info and PLMN to a CellInfo object.
    371      */
    372     private List<CellInfo> getCellInfoList(List<OperatorInfo> operatorInfoList) {
    373         List<CellInfo> cellInfoList = new ArrayList<>();
    374         for (OperatorInfo oi: operatorInfoList) {
    375             String operatorNumeric = oi.getOperatorNumeric();
    376             String mcc = null;
    377             String mnc = null;
    378             log("operatorNumeric: " + operatorNumeric);
    379             if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) {
    380                 mcc = operatorNumeric.substring(0, 3);
    381                 mnc = operatorNumeric.substring(3);
    382             }
    383             CellIdentityGsm cig = new CellIdentityGsm(
    384                     Integer.MAX_VALUE /* lac */,
    385                     Integer.MAX_VALUE /* cid */,
    386                     Integer.MAX_VALUE /* arfcn */,
    387                     Integer.MAX_VALUE /* bsic */,
    388                     mcc,
    389                     mnc,
    390                     oi.getOperatorAlphaLong(),
    391                     oi.getOperatorAlphaShort());
    392 
    393             CellInfoGsm ci = new CellInfoGsm();
    394             ci.setCellIdentity(cig);
    395             cellInfoList.add(ci);
    396         }
    397         return cellInfoList;
    398     }
    399 
    400     private static void log(String msg) {
    401         Log.d(LOG_TAG, msg);
    402     }
    403 }