• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
getService()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. */
onResults(List<CellInfo> results)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          */
onComplete()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          */
onError(int error)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
onCreate()294     public void onCreate() {
295         mState = QUERY_READY;
296     }
297 
298     /**
299      * Required for service implementation.
300      */
301     @Override
onStart(Intent intent, int startId)302     public void onStart(Intent intent, int startId) {
303     }
304 
305     /**
306      * Handle the bind request.
307      */
308     @Override
onBind(Intent intent)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      */
broadcastQueryResults(Message msg)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      */
getCellInfoList(List<OperatorInfo> operatorInfoList)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 
log(String msg)400     private static void log(String msg) {
401         Log.d(LOG_TAG, msg);
402     }
403 }