1 /* 2 * Copyright (C) 2006 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.Dialog; 20 import android.app.ProgressDialog; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.os.AsyncResult; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Message; 31 import android.os.RemoteException; 32 import android.os.UserManager; 33 import android.preference.Preference; 34 import android.preference.PreferenceActivity; 35 import android.preference.PreferenceGroup; 36 import android.preference.PreferenceScreen; 37 import android.text.TextUtils; 38 import android.util.Log; 39 40 import com.android.internal.telephony.CommandException; 41 import com.android.internal.telephony.Phone; 42 import com.android.internal.telephony.OperatorInfo; 43 44 import java.util.HashMap; 45 import java.util.List; 46 47 /** 48 * "Networks" settings UI for the Phone app. 49 */ 50 public class NetworkSetting extends PreferenceActivity 51 implements DialogInterface.OnCancelListener { 52 53 private static final String LOG_TAG = "phone"; 54 private static final boolean DBG = false; 55 56 private static final int EVENT_NETWORK_SCAN_COMPLETED = 100; 57 private static final int EVENT_NETWORK_SELECTION_DONE = 200; 58 private static final int EVENT_AUTO_SELECT_DONE = 300; 59 60 //dialog ids 61 private static final int DIALOG_NETWORK_SELECTION = 100; 62 private static final int DIALOG_NETWORK_LIST_LOAD = 200; 63 private static final int DIALOG_NETWORK_AUTO_SELECT = 300; 64 65 //String keys for preference lookup 66 private static final String LIST_NETWORKS_KEY = "list_networks_key"; 67 private static final String BUTTON_SRCH_NETWRKS_KEY = "button_srch_netwrks_key"; 68 private static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key"; 69 70 //map of network controls to the network data. 71 private HashMap<Preference, OperatorInfo> mNetworkMap; 72 73 Phone mPhone; 74 protected boolean mIsForeground = false; 75 76 private UserManager mUm; 77 private boolean mUnavailable; 78 79 /** message for network selection */ 80 String mNetworkSelectMsg; 81 82 //preference objects 83 private PreferenceGroup mNetworkList; 84 private Preference mSearchButton; 85 private Preference mAutoSelect; 86 87 private final Handler mHandler = new Handler() { 88 @Override 89 public void handleMessage(Message msg) { 90 AsyncResult ar; 91 switch (msg.what) { 92 case EVENT_NETWORK_SCAN_COMPLETED: 93 networksListLoaded ((List<OperatorInfo>) msg.obj, msg.arg1); 94 break; 95 96 case EVENT_NETWORK_SELECTION_DONE: 97 if (DBG) log("hideProgressPanel"); 98 removeDialog(DIALOG_NETWORK_SELECTION); 99 getPreferenceScreen().setEnabled(true); 100 101 ar = (AsyncResult) msg.obj; 102 if (ar.exception != null) { 103 if (DBG) log("manual network selection: failed!"); 104 displayNetworkSelectionFailed(ar.exception); 105 } else { 106 if (DBG) log("manual network selection: succeeded!"); 107 displayNetworkSelectionSucceeded(); 108 } 109 break; 110 case EVENT_AUTO_SELECT_DONE: 111 if (DBG) log("hideProgressPanel"); 112 113 // Always try to dismiss the dialog because activity may 114 // be moved to background after dialog is shown. 115 try { 116 dismissDialog(DIALOG_NETWORK_AUTO_SELECT); 117 } catch (IllegalArgumentException e) { 118 // "auto select" is always trigged in foreground, so "auto select" dialog 119 // should be shown when "auto select" is trigged. Should NOT get 120 // this exception, and Log it. 121 Log.w(LOG_TAG, "[NetworksList] Fail to dismiss auto select dialog", e); 122 } 123 getPreferenceScreen().setEnabled(true); 124 125 ar = (AsyncResult) msg.obj; 126 if (ar.exception != null) { 127 if (DBG) log("automatic network selection: failed!"); 128 displayNetworkSelectionFailed(ar.exception); 129 } else { 130 if (DBG) log("automatic network selection: succeeded!"); 131 displayNetworkSelectionSucceeded(); 132 } 133 break; 134 } 135 136 return; 137 } 138 }; 139 140 /** 141 * Service connection code for the NetworkQueryService. 142 * Handles the work of binding to a local object so that we can make 143 * the appropriate service calls. 144 */ 145 146 /** Local service interface */ 147 private INetworkQueryService mNetworkQueryService = null; 148 149 /** Service connection */ 150 private final ServiceConnection mNetworkQueryServiceConnection = new ServiceConnection() { 151 152 /** Handle the task of binding the local object to the service */ 153 public void onServiceConnected(ComponentName className, IBinder service) { 154 if (DBG) log("connection created, binding local service."); 155 mNetworkQueryService = ((NetworkQueryService.LocalBinder) service).getService(); 156 // as soon as it is bound, run a query. 157 loadNetworksList(); 158 } 159 160 /** Handle the task of cleaning up the local binding */ 161 public void onServiceDisconnected(ComponentName className) { 162 if (DBG) log("connection disconnected, cleaning local binding."); 163 mNetworkQueryService = null; 164 } 165 }; 166 167 /** 168 * This implementation of INetworkQueryServiceCallback is used to receive 169 * callback notifications from the network query service. 170 */ 171 private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() { 172 173 /** place the message on the looper queue upon query completion. */ 174 public void onQueryComplete(List<OperatorInfo> networkInfoArray, int status) { 175 if (DBG) log("notifying message loop of query completion."); 176 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED, 177 status, 0, networkInfoArray); 178 msg.sendToTarget(); 179 } 180 }; 181 182 @Override onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)183 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 184 boolean handled = false; 185 186 if (preference == mSearchButton) { 187 loadNetworksList(); 188 handled = true; 189 } else if (preference == mAutoSelect) { 190 selectNetworkAutomatic(); 191 handled = true; 192 } else { 193 Preference selectedCarrier = preference; 194 195 String networkStr = selectedCarrier.getTitle().toString(); 196 if (DBG) log("selected network: " + networkStr); 197 198 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE); 199 mPhone.selectNetworkManually(mNetworkMap.get(selectedCarrier), msg); 200 201 displayNetworkSeletionInProgress(networkStr); 202 203 handled = true; 204 } 205 206 return handled; 207 } 208 209 //implemented for DialogInterface.OnCancelListener onCancel(DialogInterface dialog)210 public void onCancel(DialogInterface dialog) { 211 // request that the service stop the query with this callback object. 212 try { 213 mNetworkQueryService.stopNetworkQuery(mCallback); 214 } catch (RemoteException e) { 215 throw new RuntimeException(e); 216 } 217 finish(); 218 } 219 getNormalizedCarrierName(OperatorInfo ni)220 public String getNormalizedCarrierName(OperatorInfo ni) { 221 if (ni != null) { 222 return ni.getOperatorAlphaLong() + " (" + ni.getOperatorNumeric() + ")"; 223 } 224 return null; 225 } 226 227 @Override onCreate(Bundle icicle)228 protected void onCreate(Bundle icicle) { 229 super.onCreate(icicle); 230 231 mUm = (UserManager) getSystemService(Context.USER_SERVICE); 232 233 if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { 234 setContentView(R.layout.telephony_disallowed_preference_screen); 235 mUnavailable = true; 236 return; 237 } 238 239 addPreferencesFromResource(R.xml.carrier_select); 240 241 mPhone = PhoneGlobals.getPhone(); 242 243 mNetworkList = (PreferenceGroup) getPreferenceScreen().findPreference(LIST_NETWORKS_KEY); 244 mNetworkMap = new HashMap<Preference, OperatorInfo>(); 245 246 mSearchButton = getPreferenceScreen().findPreference(BUTTON_SRCH_NETWRKS_KEY); 247 mAutoSelect = getPreferenceScreen().findPreference(BUTTON_AUTO_SELECT_KEY); 248 249 // Start the Network Query service, and bind it. 250 // The OS knows to start he service only once and keep the instance around (so 251 // long as startService is called) until a stopservice request is made. Since 252 // we want this service to just stay in the background until it is killed, we 253 // don't bother stopping it from our end. 254 startService (new Intent(this, NetworkQueryService.class)); 255 bindService (new Intent(this, NetworkQueryService.class), mNetworkQueryServiceConnection, 256 Context.BIND_AUTO_CREATE); 257 } 258 259 @Override onResume()260 public void onResume() { 261 super.onResume(); 262 mIsForeground = true; 263 } 264 265 @Override onPause()266 public void onPause() { 267 super.onPause(); 268 mIsForeground = false; 269 } 270 271 /** 272 * Override onDestroy() to unbind the query service, avoiding service 273 * leak exceptions. 274 */ 275 @Override onDestroy()276 protected void onDestroy() { 277 if (!mUnavailable) { 278 // unbind the service. 279 unbindService(mNetworkQueryServiceConnection); 280 } 281 super.onDestroy(); 282 } 283 284 @Override onCreateDialog(int id)285 protected Dialog onCreateDialog(int id) { 286 287 if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD) || 288 (id == DIALOG_NETWORK_AUTO_SELECT)) { 289 ProgressDialog dialog = new ProgressDialog(this); 290 switch (id) { 291 case DIALOG_NETWORK_SELECTION: 292 // It would be more efficient to reuse this dialog by moving 293 // this setMessage() into onPreparedDialog() and NOT use 294 // removeDialog(). However, this is not possible since the 295 // message is rendered only 2 times in the ProgressDialog - 296 // after show() and before onCreate. 297 dialog.setMessage(mNetworkSelectMsg); 298 dialog.setCancelable(false); 299 dialog.setIndeterminate(true); 300 break; 301 case DIALOG_NETWORK_AUTO_SELECT: 302 dialog.setMessage(getResources().getString(R.string.register_automatically)); 303 dialog.setCancelable(false); 304 dialog.setIndeterminate(true); 305 break; 306 case DIALOG_NETWORK_LIST_LOAD: 307 default: 308 // reinstate the cancelablity of the dialog. 309 dialog.setMessage(getResources().getString(R.string.load_networks_progress)); 310 dialog.setCanceledOnTouchOutside(false); 311 dialog.setOnCancelListener(this); 312 break; 313 } 314 return dialog; 315 } 316 return null; 317 } 318 319 @Override onPrepareDialog(int id, Dialog dialog)320 protected void onPrepareDialog(int id, Dialog dialog) { 321 if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD) || 322 (id == DIALOG_NETWORK_AUTO_SELECT)) { 323 // when the dialogs come up, we'll need to indicate that 324 // we're in a busy state to dissallow further input. 325 getPreferenceScreen().setEnabled(false); 326 } 327 } 328 displayEmptyNetworkList(boolean flag)329 private void displayEmptyNetworkList(boolean flag) { 330 mNetworkList.setTitle(flag ? R.string.empty_networks_list : R.string.label_available); 331 } 332 displayNetworkSeletionInProgress(String networkStr)333 private void displayNetworkSeletionInProgress(String networkStr) { 334 // TODO: use notification manager? 335 mNetworkSelectMsg = getResources().getString(R.string.register_on_network, networkStr); 336 337 if (mIsForeground) { 338 showDialog(DIALOG_NETWORK_SELECTION); 339 } 340 } 341 displayNetworkQueryFailed(int error)342 private void displayNetworkQueryFailed(int error) { 343 String status = getResources().getString(R.string.network_query_error); 344 345 final PhoneGlobals app = PhoneGlobals.getInstance(); 346 app.notificationMgr.postTransientNotification( 347 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 348 } 349 displayNetworkSelectionFailed(Throwable ex)350 private void displayNetworkSelectionFailed(Throwable ex) { 351 String status; 352 353 if ((ex != null && ex instanceof CommandException) && 354 ((CommandException)ex).getCommandError() 355 == CommandException.Error.ILLEGAL_SIM_OR_ME) 356 { 357 status = getResources().getString(R.string.not_allowed); 358 } else { 359 status = getResources().getString(R.string.connect_later); 360 } 361 362 final PhoneGlobals app = PhoneGlobals.getInstance(); 363 app.notificationMgr.postTransientNotification( 364 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 365 } 366 displayNetworkSelectionSucceeded()367 private void displayNetworkSelectionSucceeded() { 368 String status = getResources().getString(R.string.registration_done); 369 370 final PhoneGlobals app = PhoneGlobals.getInstance(); 371 app.notificationMgr.postTransientNotification( 372 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 373 374 mHandler.postDelayed(new Runnable() { 375 public void run() { 376 finish(); 377 } 378 }, 3000); 379 } 380 loadNetworksList()381 private void loadNetworksList() { 382 if (DBG) log("load networks list..."); 383 384 if (mIsForeground) { 385 showDialog(DIALOG_NETWORK_LIST_LOAD); 386 } 387 388 // delegate query request to the service. 389 try { 390 mNetworkQueryService.startNetworkQuery(mCallback); 391 } catch (RemoteException e) { 392 } 393 394 displayEmptyNetworkList(false); 395 } 396 397 /** 398 * networksListLoaded has been rewritten to take an array of 399 * OperatorInfo objects and a status field, instead of an 400 * AsyncResult. Otherwise, the functionality which takes the 401 * OperatorInfo array and creates a list of preferences from it, 402 * remains unchanged. 403 */ networksListLoaded(List<OperatorInfo> result, int status)404 private void networksListLoaded(List<OperatorInfo> result, int status) { 405 if (DBG) log("networks list loaded"); 406 407 // update the state of the preferences. 408 if (DBG) log("hideProgressPanel"); 409 410 411 // Always try to dismiss the dialog because activity may 412 // be moved to background after dialog is shown. 413 try { 414 dismissDialog(DIALOG_NETWORK_LIST_LOAD); 415 } catch (IllegalArgumentException e) { 416 // It's not a error in following scenario, we just ignore it. 417 // "Load list" dialog will not show, if NetworkQueryService is 418 // connected after this activity is moved to background. 419 if (DBG) log("Fail to dismiss network load list dialog"); 420 } 421 422 getPreferenceScreen().setEnabled(true); 423 clearList(); 424 425 if (status != NetworkQueryService.QUERY_OK) { 426 if (DBG) log("error while querying available networks"); 427 displayNetworkQueryFailed(status); 428 displayEmptyNetworkList(true); 429 } else { 430 if (result != null){ 431 displayEmptyNetworkList(false); 432 433 // create a preference for each item in the list. 434 // just use the operator name instead of the mildly 435 // confusing mcc/mnc. 436 for (OperatorInfo ni : result) { 437 Preference carrier = new Preference(this, null); 438 carrier.setTitle(getNetworkTitle(ni)); 439 carrier.setPersistent(false); 440 mNetworkList.addPreference(carrier); 441 mNetworkMap.put(carrier, ni); 442 443 if (DBG) log(" " + ni); 444 } 445 446 } else { 447 displayEmptyNetworkList(true); 448 } 449 } 450 } 451 452 /** 453 * Returns the title of the network obtained in the manual search. 454 * 455 * @param OperatorInfo contains the information of the network. 456 * 457 * @return Long Name if not null/empty, otherwise Short Name if not null/empty, 458 * else MCCMNC string. 459 */ 460 getNetworkTitle(OperatorInfo ni)461 private String getNetworkTitle(OperatorInfo ni) { 462 if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) { 463 return ni.getOperatorAlphaLong(); 464 } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) { 465 return ni.getOperatorAlphaShort(); 466 } else { 467 return ni.getOperatorNumeric(); 468 } 469 } 470 clearList()471 private void clearList() { 472 for (Preference p : mNetworkMap.keySet()) { 473 mNetworkList.removePreference(p); 474 } 475 mNetworkMap.clear(); 476 } 477 selectNetworkAutomatic()478 private void selectNetworkAutomatic() { 479 if (DBG) log("select network automatically..."); 480 if (mIsForeground) { 481 showDialog(DIALOG_NETWORK_AUTO_SELECT); 482 } 483 484 Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE); 485 mPhone.setNetworkSelectionModeAutomatic(msg); 486 } 487 log(String msg)488 private void log(String msg) { 489 Log.d(LOG_TAG, "[NetworksList] " + msg); 490 } 491 } 492