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