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 com.android.internal.telephony.OperatorInfo;
     23 import android.os.AsyncResult;
     24 import android.os.Binder;
     25 import android.os.Handler;
     26 import android.os.IBinder;
     27 import android.os.Message;
     28 import android.os.RemoteCallbackList;
     29 import android.os.RemoteException;
     30 import android.telephony.SubscriptionManager;
     31 import com.android.internal.telephony.Phone;
     32 import com.android.internal.telephony.PhoneFactory;
     33 import android.util.Log;
     34 
     35 import java.util.ArrayList;
     36 
     37 /**
     38  * Service code used to assist in querying the network for service
     39  * availability.
     40  */
     41 public class NetworkQueryService extends Service {
     42     // debug data
     43     private static final String LOG_TAG = "NetworkQuery";
     44     private static final boolean DBG = true;
     45 
     46     // static events
     47     private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
     48 
     49     // static states indicating the query status of the service
     50     private static final int QUERY_READY = -1;
     51     private static final int QUERY_IS_RUNNING = -2;
     52 
     53     // error statuses that will be retured in the callback.
     54     public static final int QUERY_OK = 0;
     55     public static final int QUERY_EXCEPTION = 1;
     56 
     57     static final String ACTION_LOCAL_BINDER = "com.android.phone.intent.action.LOCAL_BINDER";
     58 
     59     /** state of the query service */
     60     private int mState;
     61 
     62     /**
     63      * Class for clients to access.  Because we know this service always
     64      * runs in the same process as its clients, we don't need to deal with
     65      * IPC.
     66      */
     67     public class LocalBinder extends Binder {
     68         INetworkQueryService getService() {
     69             return mBinder;
     70         }
     71     }
     72     private final IBinder mLocalBinder = new LocalBinder();
     73 
     74     /**
     75      * Local handler to receive the network query compete callback
     76      * from the RIL.
     77      */
     78     Handler mHandler = new Handler() {
     79         @Override
     80         public void handleMessage(Message msg) {
     81             switch (msg.what) {
     82                 // if the scan is complete, broadcast the results.
     83                 // to all registerd callbacks.
     84                 case EVENT_NETWORK_SCAN_COMPLETED:
     85                     if (DBG) log("scan completed, broadcasting results");
     86                     broadcastQueryResults((AsyncResult) msg.obj);
     87                     break;
     88             }
     89         }
     90     };
     91 
     92     /**
     93      * List of callback objects, also used to synchronize access to
     94      * itself and to changes in state.
     95      */
     96     final RemoteCallbackList<INetworkQueryServiceCallback> mCallbacks =
     97         new RemoteCallbackList<INetworkQueryServiceCallback> ();
     98 
     99     /**
    100      * Implementation of the INetworkQueryService interface.
    101      */
    102     private final INetworkQueryService.Stub mBinder = new INetworkQueryService.Stub() {
    103 
    104         /**
    105          * Starts a query with a INetworkQueryServiceCallback object if
    106          * one has not been started yet.  Ignore the new query request
    107          * if the query has been started already.  Either way, place the
    108          * callback object in the queue to be notified upon request
    109          * completion.
    110          */
    111         public void startNetworkQuery(INetworkQueryServiceCallback cb, int phoneId) {
    112             if (cb != null) {
    113                 // register the callback to the list of callbacks.
    114                 synchronized (mCallbacks) {
    115                     mCallbacks.register(cb);
    116                     if (DBG) log("registering callback " + cb.getClass().toString());
    117 
    118                     switch (mState) {
    119                         case QUERY_READY:
    120                             // TODO: we may want to install a timeout here in case we
    121                             // do not get a timely response from the RIL.
    122                             Phone phone = PhoneFactory.getPhone(phoneId);
    123                             if (phone != null) {
    124                                 phone.getAvailableNetworks(
    125                                         mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED));
    126                                 mState = QUERY_IS_RUNNING;
    127                                 if (DBG) log("starting new query");
    128                             } else {
    129                                 if (DBG) {
    130                                     log("phone is null");
    131                                 }
    132                             }
    133                             break;
    134 
    135                         // do nothing if we're currently busy.
    136                         case QUERY_IS_RUNNING:
    137                             if (DBG) log("query already in progress");
    138                             break;
    139                         default:
    140                     }
    141                 }
    142             }
    143         }
    144 
    145         /**
    146          * Stops a query with a INetworkQueryServiceCallback object as
    147          * a token.
    148          */
    149         public void stopNetworkQuery(INetworkQueryServiceCallback cb) {
    150             // currently we just unregister the callback, since there is
    151             // no way to tell the RIL to terminate the query request.
    152             // This means that the RIL may still be busy after the stop
    153             // request was made, but the state tracking logic ensures
    154             // that the delay will only last for 1 request even with
    155             // repeated button presses in the NetworkSetting activity.
    156             unregisterCallback(cb);
    157         }
    158 
    159         /**
    160          * Unregisters the callback without impacting an underlying query.
    161          */
    162         public void unregisterCallback(INetworkQueryServiceCallback cb) {
    163             if (cb != null) {
    164                 synchronized (mCallbacks) {
    165                     if (DBG) log("unregistering callback " + cb.getClass().toString());
    166                     mCallbacks.unregister(cb);
    167                 }
    168             }
    169         }
    170     };
    171 
    172     @Override
    173     public void onCreate() {
    174         mState = QUERY_READY;
    175     }
    176 
    177     /**
    178      * Required for service implementation.
    179      */
    180     @Override
    181     public void onStart(Intent intent, int startId) {
    182     }
    183 
    184     /**
    185      * Handle the bind request.
    186      */
    187     @Override
    188     public IBinder onBind(Intent intent) {
    189         if (DBG) log("binding service implementation");
    190         if (ACTION_LOCAL_BINDER.equals(intent.getAction())) {
    191             return mLocalBinder;
    192         }
    193 
    194         return mBinder;
    195     }
    196 
    197     /**
    198      * Broadcast the results from the query to all registered callback
    199      * objects.
    200      */
    201     private void broadcastQueryResults (AsyncResult ar) {
    202         // reset the state.
    203         synchronized (mCallbacks) {
    204             mState = QUERY_READY;
    205 
    206             // see if we need to do any work.
    207             if (ar == null) {
    208                 if (DBG) log("AsyncResult is null.");
    209                 return;
    210             }
    211 
    212             // TODO: we may need greater accuracy here, but for now, just a
    213             // simple status integer will suffice.
    214             int exception = (ar.exception == null) ? QUERY_OK : QUERY_EXCEPTION;
    215             if (DBG) log("AsyncResult has exception " + exception);
    216 
    217             // Make the calls to all the registered callbacks.
    218             for (int i = (mCallbacks.beginBroadcast() - 1); i >= 0; i--) {
    219                 INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i);
    220                 if (DBG) log("broadcasting results to " + cb.getClass().toString());
    221                 try {
    222                     cb.onQueryComplete((ArrayList<OperatorInfo>) ar.result, exception);
    223                 } catch (RemoteException e) {
    224                 }
    225             }
    226 
    227             // finish up.
    228             mCallbacks.finishBroadcast();
    229         }
    230     }
    231 
    232     private static void log(String msg) {
    233         Log.d(LOG_TAG, msg);
    234     }
    235 }
    236