• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }