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 {
getService()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
onCreate()173     public void onCreate() {
174         mState = QUERY_READY;
175     }
176 
177     /**
178      * Required for service implementation.
179      */
180     @Override
onStart(Intent intent, int startId)181     public void onStart(Intent intent, int startId) {
182     }
183 
184     /**
185      * Handle the bind request.
186      */
187     @Override
onBind(Intent intent)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      */
broadcastQueryResults(AsyncResult ar)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 
log(String msg)232     private static void log(String msg) {
233         Log.d(LOG_TAG, msg);
234     }
235 }
236