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.ProgressDialog; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.os.AsyncResult; 23 import android.os.AsyncTask; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.RemoteException; 29 import android.preference.ListPreference; 30 import android.preference.Preference; 31 import android.telephony.CellInfo; 32 import android.telephony.CellInfoCdma; 33 import android.telephony.CellInfoGsm; 34 import android.telephony.CellInfoLte; 35 import android.telephony.CellInfoWcdma; 36 import android.telephony.CellSignalStrengthCdma; 37 import android.telephony.CellSignalStrengthGsm; 38 import android.telephony.CellSignalStrengthLte; 39 import android.telephony.CellSignalStrengthWcdma; 40 import android.telephony.NetworkScan; 41 import android.telephony.SubscriptionManager; 42 import android.telephony.TelephonyManager; 43 import android.text.BidiFormatter; 44 import android.text.TextDirectionHeuristics; 45 import android.text.TextUtils; 46 import android.util.AttributeSet; 47 import android.util.Log; 48 49 import com.android.internal.logging.MetricsLogger; 50 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 51 import com.android.internal.telephony.OperatorInfo; 52 import com.android.internal.telephony.Phone; 53 import com.android.internal.telephony.PhoneFactory; 54 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 59 60 /** 61 * "Networks" preference in "Mobile network" settings UI for the Phone app. 62 * It's used to manually search and choose mobile network. Enabled only when 63 * autoSelect preference is turned off. 64 */ 65 public class NetworkSelectListPreference extends ListPreference 66 implements DialogInterface.OnCancelListener, 67 Preference.OnPreferenceChangeListener{ 68 69 private static final String LOG_TAG = "networkSelect"; 70 private static final boolean DBG = true; 71 72 private static final int EVENT_NETWORK_SELECTION_DONE = 1; 73 private static final int EVENT_NETWORK_SCAN_RESULTS = 2; 74 private static final int EVENT_NETWORK_SCAN_ERROR = 3; 75 private static final int EVENT_NETWORK_SCAN_COMPLETED = 4; 76 77 //dialog ids 78 private static final int DIALOG_NETWORK_SELECTION = 100; 79 private static final int DIALOG_NETWORK_LIST_LOAD = 200; 80 81 private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; 82 private List<CellInfo> mCellInfoList; 83 private CellInfo mCellInfo; 84 85 private int mSubId; 86 private NetworkOperators mNetworkOperators; 87 private boolean mNeedScanAgain; 88 private List<String> mForbiddenPlmns; 89 90 private ProgressDialog mProgressDialog; NetworkSelectListPreference(Context context, AttributeSet attrs)91 public NetworkSelectListPreference(Context context, AttributeSet attrs) { 92 super(context, attrs); 93 } 94 NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)95 public NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr, 96 int defStyleRes) { 97 super(context, attrs, defStyleAttr, defStyleRes); 98 } 99 100 @Override onClick()101 protected void onClick() { 102 showProgressDialog(DIALOG_NETWORK_LIST_LOAD); 103 TelephonyManager telephonyManager = (TelephonyManager) 104 getContext().getSystemService(Context.TELEPHONY_SERVICE); 105 new AsyncTask<Void, Void, List<String>>() { 106 @Override 107 protected List<String> doInBackground(Void... voids) { 108 return Arrays.asList(telephonyManager.getForbiddenPlmns()); 109 } 110 111 @Override 112 protected void onPostExecute(List<String> result) { 113 mForbiddenPlmns = result; 114 loadNetworksList(true); 115 } 116 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 117 } 118 119 private final Handler mHandler = new Handler() { 120 @Override 121 public void handleMessage(Message msg) { 122 AsyncResult ar; 123 switch (msg.what) { 124 case EVENT_NETWORK_SELECTION_DONE: 125 if (DBG) logd("hideProgressPanel"); 126 try { 127 dismissProgressBar(); 128 } catch (IllegalArgumentException e) { 129 } 130 setEnabled(true); 131 132 ar = (AsyncResult) msg.obj; 133 if (ar.exception != null) { 134 if (DBG) logd("manual network selection: failed!"); 135 mNetworkOperators.displayNetworkSelectionFailed(ar.exception); 136 } else { 137 if (DBG) { 138 logd("manual network selection: succeeded! " 139 + getNetworkTitle(mCellInfo)); 140 } 141 mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1); 142 } 143 mNetworkOperators.getNetworkSelectionMode(); 144 break; 145 146 case EVENT_NETWORK_SCAN_RESULTS: 147 List<CellInfo> results = (List<CellInfo>) msg.obj; 148 results.removeIf(cellInfo -> cellInfo == null); 149 if (results.size() > 0) { 150 boolean isInvalidCellInfoList = true; 151 // Regard the list as invalid only if all the elements in the list are 152 // invalid. 153 for (CellInfo cellInfo : results) { 154 if (!isInvalidCellInfo(cellInfo)) { 155 isInvalidCellInfoList = false; 156 break; 157 } 158 } 159 if (isInvalidCellInfoList) { 160 mNeedScanAgain = true; 161 if (DBG) { 162 logd("Invalid cell info. Stop current network scan " 163 + "and start a new one via old API"); 164 } 165 // Stop current network scan flow. This behavior will result in a 166 // onComplete() callback, after which we will start a new network query 167 // via Phone.getAvailableNetworks(). This behavior might also result in 168 // a onError() callback if the modem did not stop network query 169 // successfully. In this case we will display network query failed 170 // instead of resending a new request. 171 try { 172 if (mNetworkQueryService != null) { 173 mNetworkQueryService.stopNetworkQuery(); 174 } 175 } catch (RemoteException e) { 176 loge("exception from stopNetworkQuery " + e); 177 } 178 } else { 179 // TODO(b/70530820): Display the scan results incrementally after 180 // finalizing the UI desing on Mobile Network Setting page. For now, 181 // just update the CellInfo list when received the onResult callback, 182 // and display the scan result when received the onComplete callback 183 // in the end. 184 mCellInfoList = new ArrayList<>(results); 185 if (DBG) logd("CALLBACK_SCAN_RESULTS" + mCellInfoList.toString()); 186 } 187 } 188 189 break; 190 191 case EVENT_NETWORK_SCAN_ERROR: 192 int error = msg.arg1; 193 if (DBG) logd("error while querying available networks " + error); 194 if (error == NetworkScan.ERROR_UNSUPPORTED) { 195 if (DBG) { 196 logd("Modem does not support: try to scan network again via Phone"); 197 } 198 if (!mNeedScanAgain) { 199 // Avoid blinking while showing the dialog again. 200 showProgressDialog(DIALOG_NETWORK_LIST_LOAD); 201 } 202 loadNetworksList(false); 203 } else { 204 try { 205 if (mNetworkQueryService != null) { 206 mNetworkQueryService.unregisterCallback(mCallback); 207 } 208 } catch (RemoteException e) { 209 loge("onError: exception from unregisterCallback " + e); 210 } 211 displayNetworkQueryFailed(error); 212 } 213 break; 214 215 case EVENT_NETWORK_SCAN_COMPLETED: 216 if (mNeedScanAgain) { 217 logd("CellInfo is invalid to display. Start a new scan via Phone. "); 218 loadNetworksList(false); 219 mNeedScanAgain = false; 220 } else { 221 try { 222 if (mNetworkQueryService != null) { 223 mNetworkQueryService.unregisterCallback(mCallback); 224 } 225 } catch (RemoteException e) { 226 loge("onComplete: exception from unregisterCallback " + e); 227 } 228 if (DBG) logd("scan complete, load the cellInfosList"); 229 // Modify UI to indicate users that the scan has completed. 230 networksListLoaded(); 231 } 232 break; 233 } 234 return; 235 } 236 }; 237 238 INetworkQueryService mNetworkQueryService = null; 239 /** 240 * This implementation of INetworkQueryServiceCallback is used to receive 241 * callback notifications from the network query service. 242 */ 243 private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() { 244 245 /** Returns the scan results to the user, this callback will be called at lease one time. */ 246 public void onResults(List<CellInfo> results) { 247 if (DBG) logd("get scan results: " + results.toString()); 248 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results); 249 msg.sendToTarget(); 250 } 251 252 /** 253 * Informs the user that the scan has stopped. 254 * 255 * This callback will be called when the scan is finished or cancelled by the user. 256 * The related NetworkScanRequest will be deleted after this callback. 257 */ 258 public void onComplete() { 259 if (DBG) logd("network scan completed."); 260 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED); 261 msg.sendToTarget(); 262 } 263 264 /** 265 * Informs the user that there is some error about the scan. 266 * 267 * This callback will be called whenever there is any error about the scan, and the scan 268 * will be terminated. onComplete() will NOT be called. 269 */ 270 public void onError(int error) { 271 if (DBG) logd("get onError callback with error code: " + error); 272 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */); 273 msg.sendToTarget(); 274 } 275 }; 276 277 @Override 278 //implemented for DialogInterface.OnCancelListener onCancel(DialogInterface dialog)279 public void onCancel(DialogInterface dialog) { 280 if (DBG) logd("user manually close the dialog"); 281 // request that the service stop the query with this callback object. 282 try { 283 if (mNetworkQueryService != null) { 284 mNetworkQueryService.stopNetworkQuery(); 285 mNetworkQueryService.unregisterCallback(mCallback); 286 } 287 // If cancelled, we query NetworkSelectMode and update states of AutoSelect button. 288 mNetworkOperators.getNetworkSelectionMode(); 289 } catch (RemoteException e) { 290 loge("onCancel: exception from stopNetworkQuery " + e); 291 } 292 } 293 294 @Override onDialogClosed(boolean positiveResult)295 protected void onDialogClosed(boolean positiveResult) { 296 super.onDialogClosed(positiveResult); 297 // If dismissed, we query NetworkSelectMode and update states of AutoSelect button. 298 if (!positiveResult) { 299 mNetworkOperators.getNetworkSelectionMode(); 300 } 301 } 302 303 // This method is provided besides initialize() because bind to network query service 304 // may be binded after initialize(). In that case this method needs to be called explicitly 305 // to set mNetworkQueryService. Otherwise mNetworkQueryService will remain null. setNetworkQueryService(INetworkQueryService queryService)306 public void setNetworkQueryService(INetworkQueryService queryService) { 307 mNetworkQueryService = queryService; 308 } 309 310 // This initialize method needs to be called for this preference to work properly. initialize(int subId, INetworkQueryService queryService, NetworkOperators networkOperators, ProgressDialog progressDialog)311 protected void initialize(int subId, INetworkQueryService queryService, 312 NetworkOperators networkOperators, ProgressDialog progressDialog) { 313 mSubId = subId; 314 mNetworkQueryService = queryService; 315 mNetworkOperators = networkOperators; 316 // This preference should share the same progressDialog with networkOperators category. 317 mProgressDialog = progressDialog; 318 mNeedScanAgain = false; 319 320 if (SubscriptionManager.isValidSubscriptionId(mSubId)) { 321 mPhoneId = SubscriptionManager.getPhoneId(mSubId); 322 } 323 324 TelephonyManager telephonyManager = (TelephonyManager) 325 getContext().getSystemService(Context.TELEPHONY_SERVICE); 326 327 setSummary(telephonyManager.getNetworkOperatorName()); 328 329 setOnPreferenceChangeListener(this); 330 } 331 332 @Override onPrepareForRemoval()333 protected void onPrepareForRemoval() { 334 destroy(); 335 super.onPrepareForRemoval(); 336 } 337 destroy()338 private void destroy() { 339 try { 340 dismissProgressBar(); 341 } catch (IllegalArgumentException e) { 342 loge("onDestroy: exception from dismissProgressBar " + e); 343 } 344 345 try { 346 if (mNetworkQueryService != null) { 347 // used to un-register callback 348 mNetworkQueryService.unregisterCallback(mCallback); 349 } 350 } catch (RemoteException e) { 351 loge("onDestroy: exception from unregisterCallback " + e); 352 } 353 } 354 displayEmptyNetworkList()355 private void displayEmptyNetworkList() { 356 String status = getContext().getResources().getString(R.string.empty_networks_list); 357 358 final PhoneGlobals app = PhoneGlobals.getInstance(); 359 app.notificationMgr.postTransientNotification( 360 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 361 } 362 displayNetworkSelectionInProgress()363 private void displayNetworkSelectionInProgress() { 364 showProgressDialog(DIALOG_NETWORK_SELECTION); 365 } 366 displayNetworkQueryFailed(int error)367 private void displayNetworkQueryFailed(int error) { 368 String status = getContext().getResources().getString(R.string.network_query_error); 369 370 try { 371 dismissProgressBar(); 372 } catch (IllegalArgumentException e1) { 373 // do nothing 374 } 375 376 final PhoneGlobals app = PhoneGlobals.getInstance(); 377 app.notificationMgr.postTransientNotification( 378 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 379 } 380 loadNetworksList(boolean isIncrementalResult)381 private void loadNetworksList(boolean isIncrementalResult) { 382 if (DBG) logd("load networks list..."); 383 try { 384 if (mNetworkQueryService != null) { 385 mNetworkQueryService.startNetworkQuery(mCallback, mPhoneId, isIncrementalResult); 386 } else { 387 displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION); 388 } 389 } catch (RemoteException e) { 390 loge("loadNetworksList: exception from startNetworkQuery " + e); 391 displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION); 392 } 393 } 394 networksListLoaded()395 private void networksListLoaded() { 396 if (DBG) logd("networks list loaded"); 397 398 // update the state of the preferences. 399 if (DBG) logd("hideProgressPanel"); 400 401 // Always try to dismiss the dialog because activity may 402 // be moved to background after dialog is shown. 403 try { 404 dismissProgressBar(); 405 } catch (IllegalArgumentException e) { 406 // It's not a error in following scenario, we just ignore it. 407 // "Load list" dialog will not show, if NetworkQueryService is 408 // connected after this activity is moved to background. 409 loge("Fail to dismiss network load list dialog " + e); 410 } 411 mNetworkOperators.getNetworkSelectionMode(); 412 if (mCellInfoList != null) { 413 // create a preference for each item in the list. 414 // just use the operator name instead of the mildly 415 // confusing mcc/mnc. 416 List<CharSequence> networkEntriesList = new ArrayList<>(); 417 List<CharSequence> networkEntryValuesList = new ArrayList<>(); 418 for (CellInfo cellInfo: mCellInfoList) { 419 // Display each operator name only once. 420 String networkTitle = getNetworkTitle(cellInfo); 421 if (!networkEntriesList.contains(networkTitle)) { 422 if (CellInfoUtil.isForbidden(cellInfo, mForbiddenPlmns)) { 423 networkTitle += " " 424 + getContext().getResources().getString(R.string.forbidden_network); 425 } 426 networkEntriesList.add(networkTitle); 427 networkEntryValuesList.add(getOperatorNumeric(cellInfo)); 428 } 429 } 430 setEntries(networkEntriesList.toArray(new CharSequence[networkEntriesList.size()])); 431 setEntryValues(networkEntryValuesList.toArray( 432 new CharSequence[networkEntryValuesList.size()])); 433 434 super.onClick(); 435 } else { 436 displayEmptyNetworkList(); 437 } 438 } 439 dismissProgressBar()440 private void dismissProgressBar() { 441 if (mProgressDialog != null && mProgressDialog.isShowing()) { 442 mProgressDialog.dismiss(); 443 } 444 } 445 showProgressDialog(int id)446 private void showProgressDialog(int id) { 447 if (mProgressDialog == null) { 448 mProgressDialog = new ProgressDialog(getContext()); 449 } else { 450 // Dismiss progress bar if it's showing now. 451 dismissProgressBar(); 452 } 453 454 switch (id) { 455 case DIALOG_NETWORK_SELECTION: 456 final String networkSelectMsg = getContext().getResources() 457 .getString(R.string.register_on_network, 458 getNetworkTitle(mCellInfo)); 459 mProgressDialog.setMessage(networkSelectMsg); 460 mProgressDialog.setCanceledOnTouchOutside(false); 461 mProgressDialog.setCancelable(false); 462 mProgressDialog.setIndeterminate(true); 463 break; 464 case DIALOG_NETWORK_LIST_LOAD: 465 mProgressDialog.setMessage( 466 getContext().getResources().getString(R.string.load_networks_progress)); 467 mProgressDialog.setCanceledOnTouchOutside(false); 468 mProgressDialog.setCancelable(true); 469 mProgressDialog.setIndeterminate(false); 470 mProgressDialog.setOnCancelListener(this); 471 break; 472 default: 473 } 474 mProgressDialog.show(); 475 } 476 477 /** 478 * Implemented to support onPreferenceChangeListener to look for preference 479 * changes specifically on this button. 480 * 481 * @param preference is the preference to be changed, should be network select button. 482 * @param newValue should be the value of the selection as index of operators. 483 */ 484 @Override onPreferenceChange(Preference preference, Object newValue)485 public boolean onPreferenceChange(Preference preference, Object newValue) { 486 int operatorIndex = findIndexOfValue((String) newValue); 487 mCellInfo = mCellInfoList.get(operatorIndex); 488 if (DBG) logd("selected network: " + mCellInfo.toString()); 489 490 MetricsLogger.action(getContext(), 491 MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK); 492 493 Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE); 494 Phone phone = PhoneFactory.getPhone(mPhoneId); 495 if (phone != null) { 496 OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo); 497 if (DBG) logd("manually selected network: " + operatorInfo.toString()); 498 phone.selectNetworkManually(operatorInfo, true, msg); 499 displayNetworkSelectionInProgress(); 500 } else { 501 loge("Error selecting network. phone is null."); 502 } 503 return true; 504 } 505 506 /** 507 * Returns the title of the network obtained in the manual search. 508 * 509 * @param cellInfo contains the information of the network. 510 * @return Long Name if not null/empty, otherwise Short Name if not null/empty, 511 * else MCCMNC string. 512 */ getNetworkTitle(CellInfo cellInfo)513 private String getNetworkTitle(CellInfo cellInfo) { 514 OperatorInfo ni = getOperatorInfoFromCellInfo(cellInfo); 515 516 if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) { 517 return ni.getOperatorAlphaLong(); 518 } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) { 519 return ni.getOperatorAlphaShort(); 520 } else { 521 BidiFormatter bidiFormatter = BidiFormatter.getInstance(); 522 return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR); 523 } 524 } 525 526 /** 527 * Returns the operator numeric (MCCMNC) obtained in the manual search. 528 * 529 * @param cellInfo contains the information of the network. 530 * @return MCCMNC string. 531 */ getOperatorNumeric(CellInfo cellInfo)532 private String getOperatorNumeric(CellInfo cellInfo) { 533 return getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric(); 534 } 535 536 /** 537 * Wrap a cell info into an operator info. 538 */ getOperatorInfoFromCellInfo(CellInfo cellInfo)539 private OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) { 540 OperatorInfo oi; 541 if (cellInfo instanceof CellInfoLte) { 542 CellInfoLte lte = (CellInfoLte) cellInfo; 543 oi = new OperatorInfo( 544 (String) lte.getCellIdentity().getOperatorAlphaLong(), 545 (String) lte.getCellIdentity().getOperatorAlphaShort(), 546 lte.getCellIdentity().getMobileNetworkOperator()); 547 } else if (cellInfo instanceof CellInfoWcdma) { 548 CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo; 549 oi = new OperatorInfo( 550 (String) wcdma.getCellIdentity().getOperatorAlphaLong(), 551 (String) wcdma.getCellIdentity().getOperatorAlphaShort(), 552 wcdma.getCellIdentity().getMobileNetworkOperator()); 553 } else if (cellInfo instanceof CellInfoGsm) { 554 CellInfoGsm gsm = (CellInfoGsm) cellInfo; 555 oi = new OperatorInfo( 556 (String) gsm.getCellIdentity().getOperatorAlphaLong(), 557 (String) gsm.getCellIdentity().getOperatorAlphaShort(), 558 gsm.getCellIdentity().getMobileNetworkOperator()); 559 } else if (cellInfo instanceof CellInfoCdma) { 560 CellInfoCdma cdma = (CellInfoCdma) cellInfo; 561 oi = new OperatorInfo( 562 (String) cdma.getCellIdentity().getOperatorAlphaLong(), 563 (String) cdma.getCellIdentity().getOperatorAlphaShort(), 564 "" /* operator numeric */); 565 } else { 566 oi = new OperatorInfo("", "", ""); 567 } 568 return oi; 569 } 570 571 572 /** 573 * Check if the CellInfo is valid to display. If a CellInfo has signal strength but does 574 * not have operator info, it is invalid to display. 575 */ isInvalidCellInfo(CellInfo cellInfo)576 private boolean isInvalidCellInfo(CellInfo cellInfo) { 577 if (DBG) logd("Check isInvalidCellInfo: " + cellInfo.toString()); 578 CharSequence al = null; 579 CharSequence as = null; 580 boolean hasSignalStrength = false; 581 if (cellInfo instanceof CellInfoLte) { 582 CellInfoLte lte = (CellInfoLte) cellInfo; 583 al = lte.getCellIdentity().getOperatorAlphaLong(); 584 as = lte.getCellIdentity().getOperatorAlphaShort(); 585 hasSignalStrength = !lte.getCellSignalStrength().equals(new CellSignalStrengthLte()); 586 } else if (cellInfo instanceof CellInfoWcdma) { 587 CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo; 588 al = wcdma.getCellIdentity().getOperatorAlphaLong(); 589 as = wcdma.getCellIdentity().getOperatorAlphaShort(); 590 hasSignalStrength = !wcdma.getCellSignalStrength().equals( 591 new CellSignalStrengthWcdma()); 592 } else if (cellInfo instanceof CellInfoGsm) { 593 CellInfoGsm gsm = (CellInfoGsm) cellInfo; 594 al = gsm.getCellIdentity().getOperatorAlphaLong(); 595 as = gsm.getCellIdentity().getOperatorAlphaShort(); 596 hasSignalStrength = !gsm.getCellSignalStrength().equals(new CellSignalStrengthGsm()); 597 } else if (cellInfo instanceof CellInfoCdma) { 598 CellInfoCdma cdma = (CellInfoCdma) cellInfo; 599 al = cdma.getCellIdentity().getOperatorAlphaLong(); 600 as = cdma.getCellIdentity().getOperatorAlphaShort(); 601 hasSignalStrength = !cdma.getCellSignalStrength().equals(new CellSignalStrengthCdma()); 602 } else { 603 return true; 604 } 605 if (TextUtils.isEmpty(al) && TextUtils.isEmpty(as) && hasSignalStrength) { 606 return true; 607 } 608 return false; 609 } 610 611 @Override onSaveInstanceState()612 protected Parcelable onSaveInstanceState() { 613 final Parcelable superState = super.onSaveInstanceState(); 614 if (isPersistent()) { 615 // No need to save instance state since it's persistent 616 return superState; 617 } 618 619 final SavedState myState = new SavedState(superState); 620 myState.mDialogListEntries = getEntries(); 621 myState.mDialogListEntryValues = getEntryValues(); 622 myState.mCellInfoList = mCellInfoList; 623 return myState; 624 } 625 626 @Override onRestoreInstanceState(Parcelable state)627 protected void onRestoreInstanceState(Parcelable state) { 628 if (state == null || !state.getClass().equals(SavedState.class)) { 629 // Didn't save state for us in onSaveInstanceState 630 super.onRestoreInstanceState(state); 631 return; 632 } 633 634 SavedState myState = (SavedState) state; 635 636 if (getEntries() == null && myState.mDialogListEntries != null) { 637 setEntries(myState.mDialogListEntries); 638 } 639 if (getEntryValues() == null && myState.mDialogListEntryValues != null) { 640 setEntryValues(myState.mDialogListEntryValues); 641 } 642 if (mCellInfoList == null && myState.mCellInfoList != null) { 643 mCellInfoList = myState.mCellInfoList; 644 } 645 646 super.onRestoreInstanceState(myState.getSuperState()); 647 } 648 649 /** 650 * We save entries, entryValues and operatorInfoList into bundle. 651 * At onCreate of fragment, dialog will be restored if it was open. In this case, 652 * we need to restore entries, entryValues and operatorInfoList. Without those information, 653 * onPreferenceChange will fail if user select network from the dialog. 654 */ 655 private static class SavedState extends BaseSavedState { 656 CharSequence[] mDialogListEntries; 657 CharSequence[] mDialogListEntryValues; 658 List<CellInfo> mCellInfoList; 659 SavedState(Parcel source)660 SavedState(Parcel source) { 661 super(source); 662 final ClassLoader boot = Object.class.getClassLoader(); 663 mDialogListEntries = source.readCharSequenceArray(); 664 mDialogListEntryValues = source.readCharSequenceArray(); 665 mCellInfoList = source.readParcelableList(mCellInfoList, boot); 666 } 667 668 @Override writeToParcel(Parcel dest, int flags)669 public void writeToParcel(Parcel dest, int flags) { 670 super.writeToParcel(dest, flags); 671 dest.writeCharSequenceArray(mDialogListEntries); 672 dest.writeCharSequenceArray(mDialogListEntryValues); 673 dest.writeParcelableList(mCellInfoList, flags); 674 } 675 SavedState(Parcelable superState)676 SavedState(Parcelable superState) { 677 super(superState); 678 } 679 680 public static final Parcelable.Creator<SavedState> CREATOR = 681 new Parcelable.Creator<SavedState>() { 682 public SavedState createFromParcel(Parcel in) { 683 return new SavedState(in); 684 } 685 686 public SavedState[] newArray(int size) { 687 return new SavedState[size]; 688 } 689 }; 690 } 691 logd(String msg)692 private void logd(String msg) { 693 Log.d(LOG_TAG, "[NetworksList] " + msg); 694 } 695 loge(String msg)696 private void loge(String msg) { 697 Log.e(LOG_TAG, "[NetworksList] " + msg); 698 } 699 }