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 }