• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.phone;
17 
18 import android.app.ActionBar;
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.AsyncResult;
25 import android.os.AsyncTask;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.preference.Preference;
32 import android.preference.PreferenceCategory;
33 import android.preference.PreferenceFragment;
34 import android.preference.PreferenceScreen;
35 import android.telephony.AccessNetworkConstants;
36 import android.telephony.CellIdentity;
37 import android.telephony.CellInfo;
38 import android.telephony.NetworkRegistrationState;
39 import android.telephony.ServiceState;
40 import android.telephony.SubscriptionManager;
41 import android.telephony.TelephonyManager;
42 import android.util.Log;
43 import android.view.LayoutInflater;
44 import android.view.View;
45 import android.view.ViewGroup;
46 
47 import com.android.internal.logging.MetricsLogger;
48 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
49 import com.android.internal.telephony.OperatorInfo;
50 import com.android.internal.telephony.Phone;
51 import com.android.internal.telephony.PhoneFactory;
52 
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 
59 /**
60  * "Choose network" settings UI for the Phone app.
61  */
62 public class NetworkSelectSetting extends PreferenceFragment {
63 
64     private static final String TAG = "NetworkSelectSetting";
65     private static final boolean DBG = true;
66 
67     private static final int EVENT_NETWORK_SELECTION_DONE = 1;
68     private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
69     private static final int EVENT_NETWORK_SCAN_ERROR = 3;
70     private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
71 
72     private static final String PREF_KEY_CONNECTED_NETWORK_OPERATOR =
73             "connected_network_operator_preference";
74     private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference";
75 
76     // used to add/remove NetworkOperatorsPreference.
77     private PreferenceCategory mNetworkOperatorsPreferences;
78     // used to add/remove connected NetworkOperatorPreference.
79     private PreferenceCategory mConnectedNetworkOperatorsPreference;
80     // manage the progress bar on the top of the page.
81     private View mProgressHeader;
82     private Preference mStatusMessagePreference;
83     private List<CellInfo> mCellInfoList;
84     private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
85     private ViewGroup mFrameLayout;
86     private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
87     private TelephonyManager mTelephonyManager;
88     private NetworkOperators mNetworkOperators;
89     private List<String> mForbiddenPlmns;
90 
91     private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
92         updateNetworkOperatorsPreferenceCategory();
93     };
94 
95     /**
96      * Create a new instance of this fragment.
97      */
newInstance(int phoneId)98     public static NetworkSelectSetting newInstance(int phoneId) {
99         Bundle args = new Bundle();
100         args.putInt(NetworkSelectSettingActivity.KEY_PHONE_ID, phoneId);
101         NetworkSelectSetting fragment = new NetworkSelectSetting();
102         fragment.setArguments(args);
103 
104         return fragment;
105     }
106 
107     @Override
onCreate(Bundle icicle)108     public void onCreate(Bundle icicle) {
109         if (DBG) logd("onCreate");
110         super.onCreate(icicle);
111 
112         mPhoneId = getArguments().getInt(NetworkSelectSettingActivity.KEY_PHONE_ID);
113 
114         addPreferencesFromResource(R.xml.choose_network);
115         mConnectedNetworkOperatorsPreference =
116                 (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_NETWORK_OPERATOR);
117         mNetworkOperatorsPreferences =
118                 (PreferenceCategory) findPreference(PREF_KEY_NETWORK_OPERATORS);
119         mStatusMessagePreference = new Preference(getContext());
120         mSelectedNetworkOperatorPreference = null;
121         mTelephonyManager = (TelephonyManager)
122                 getContext().getSystemService(Context.TELEPHONY_SERVICE);
123         mNetworkOperators = new NetworkOperators(getContext());
124         setRetainInstance(true);
125     }
126 
127     @Override
onViewCreated(View view, Bundle savedInstanceState)128     public void onViewCreated(View view, Bundle savedInstanceState) {
129         if (DBG) logd("onViewCreated");
130         super.onViewCreated(view, savedInstanceState);
131 
132         if (getListView() != null) {
133             getListView().setDivider(null);
134         }
135         // Inflate progress bar
136         final Activity activity = getActivity();
137         if (activity != null) {
138             ActionBar actionBar = activity.getActionBar();
139             if (actionBar != null) {
140                 // android.R.id.home will be triggered in
141                 // {@link NetworkSelectSettingAcitivity#onOptionsItemSelected()}
142                 actionBar.setDisplayHomeAsUpEnabled(true);
143             }
144             mFrameLayout = activity.findViewById(R.id.choose_network_content);
145             final LayoutInflater inflater = activity.getLayoutInflater();
146             final View pinnedHeader =
147                     inflater.inflate(R.layout.choose_network_progress_header, mFrameLayout, false);
148             mFrameLayout.addView(pinnedHeader);
149             mFrameLayout.setVisibility(View.VISIBLE);
150             mProgressHeader = pinnedHeader.findViewById(R.id.progress_bar_animation);
151             setProgressBarVisible(false);
152         }
153         forceConfigConnectedNetworkOperatorsPreferenceCategory();
154     }
155 
156     @Override
onStart()157     public void onStart() {
158         if (DBG) logd("onStart");
159         super.onStart();
160         new AsyncTask<Void, Void, List<String>>() {
161             @Override
162             protected List<String> doInBackground(Void... voids) {
163                 return Arrays.asList(mTelephonyManager.getForbiddenPlmns());
164             }
165 
166             @Override
167             protected void onPostExecute(List<String> result) {
168                 mForbiddenPlmns = result;
169                 bindNetworkQueryService();
170             }
171         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
172     }
173 
174     /**
175      * Invoked on each preference click in this hierarchy, overrides
176      * PreferenceActivity's implementation.  Used to make sure we track the
177      * preference click events.
178      * Since the connected network operator is either faked (when no data connection) or already
179      * connected, we do not allow user to click the connected network operator.
180      */
181     @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)182     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
183                                          Preference preference) {
184         if (DBG) logd("User clicked the screen");
185         stopNetworkQuery();
186         setProgressBarVisible(false);
187         if (preference instanceof  NetworkOperatorPreference) {
188             // Refresh the last selected item in case users reselect network.
189             if (mSelectedNetworkOperatorPreference != null) {
190                 mSelectedNetworkOperatorPreference.setSummary("");
191             }
192 
193             mSelectedNetworkOperatorPreference = (NetworkOperatorPreference) preference;
194             CellInfo cellInfo = mSelectedNetworkOperatorPreference.getCellInfo();
195             if (DBG) logd("User click a NetworkOperatorPreference: " + cellInfo.toString());
196 
197             // Send metrics event
198             MetricsLogger.action(getContext(),
199                     MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK);
200 
201             // Connect to the network
202             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
203             Phone phone = PhoneFactory.getPhone(mPhoneId);
204             if (phone != null) {
205                 if (DBG) {
206                     logd("Connect to the network: " + CellInfoUtil.getNetworkTitle(cellInfo));
207                 }
208                 // Set summary as "Connecting" to the selected network.
209                 mSelectedNetworkOperatorPreference.setSummary(R.string.network_connecting);
210 
211                 // Set summary as "Disconnected" to the previously connected network
212                 if (mConnectedNetworkOperatorsPreference.getPreferenceCount() > 0) {
213                     NetworkOperatorPreference connectedNetworkOperator = (NetworkOperatorPreference)
214                             (mConnectedNetworkOperatorsPreference.getPreference(0));
215                     if (!CellInfoUtil.getNetworkTitle(cellInfo).equals(
216                             CellInfoUtil.getNetworkTitle(connectedNetworkOperator.getCellInfo()))) {
217                         connectedNetworkOperator.setSummary(R.string.network_disconnected);
218                     }
219                 }
220 
221                 // Select network manually via Phone
222                 OperatorInfo operatorInfo = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo);
223                 if (DBG) logd("manually selected network operator: " + operatorInfo.toString());
224                 phone.selectNetworkManually(operatorInfo, true, msg);
225                 setProgressBarVisible(true);
226                 return true;
227             } else {
228                 loge("Error selecting network. phone is null.");
229                 mSelectedNetworkOperatorPreference = null;
230                 return false;
231             }
232 
233         } else {
234             preferenceScreen.setEnabled(false);
235             return false;
236         }
237     }
238 
239     @Override
onAttach(Activity activity)240     public void onAttach(Activity activity) {
241         super.onAttach(activity);
242         if (!(getActivity() instanceof NetworkSelectSettingActivity)) {
243             throw new IllegalStateException("Parent activity is not NetworkSelectSettingActivity");
244         }
245     }
246 
247     @Override
onStop()248     public void onStop() {
249         super.onStop();
250         if (DBG) logd("onStop");
251         getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
252         stopNetworkQuery();
253         // Unbind the NetworkQueryService
254         unbindNetworkQueryService();
255     }
256 
257     private final Handler mHandler = new Handler() {
258         @Override
259         public void handleMessage(Message msg) {
260             AsyncResult ar;
261             switch (msg.what) {
262                 case EVENT_NETWORK_SELECTION_DONE:
263                     if (DBG) logd("network selection done: hide the progress header");
264                     setProgressBarVisible(false);
265 
266                     ar = (AsyncResult) msg.obj;
267                     if (ar.exception != null) {
268                         if (DBG) logd("manual network selection: failed! ");
269                         updateNetworkSelection();
270                         // Set summary as "Couldn't connect" to the selected network.
271                         mSelectedNetworkOperatorPreference.setSummary(
272                                 R.string.network_could_not_connect);
273                     } else {
274                         if (DBG) logd("manual network selection: succeeded! ");
275                         // Set summary as "Connected" to the selected network.
276                         mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
277                     }
278                     break;
279 
280                 case EVENT_NETWORK_SCAN_RESULTS:
281                     List<CellInfo> results = aggregateCellInfoList((List<CellInfo>) msg.obj);
282                     mCellInfoList = new ArrayList<>(results);
283                     if (DBG) logd("after aggregate: " + mCellInfoList.toString());
284                     if (mCellInfoList != null && mCellInfoList.size() != 0) {
285                         updateNetworkOperators();
286                     } else {
287                         addMessagePreference(R.string.empty_networks_list);
288                     }
289 
290                     break;
291 
292                 case EVENT_NETWORK_SCAN_ERROR:
293                     int error = msg.arg1;
294                     if (DBG) logd("error while querying available networks " + error);
295                     stopNetworkQuery();
296                     addMessagePreference(R.string.network_query_error);
297                     break;
298 
299                 case EVENT_NETWORK_SCAN_COMPLETED:
300                     stopNetworkQuery();
301                     if (DBG) logd("scan complete");
302                     if (mCellInfoList == null) {
303                         // In case the scan timeout before getting any results
304                         addMessagePreference(R.string.empty_networks_list);
305                     }
306                     break;
307             }
308             return;
309         }
310     };
311 
loadNetworksList()312     private void loadNetworksList() {
313         if (DBG) logd("load networks list...");
314         setProgressBarVisible(true);
315         try {
316             if (mNetworkQueryService != null) {
317                 if (DBG) logd("start network query");
318                 mNetworkQueryService
319                         .startNetworkQuery(mCallback, mPhoneId, true /* is incremental result */);
320             } else {
321                 if (DBG) logd("unable to start network query, mNetworkQueryService is null");
322                 addMessagePreference(R.string.network_query_error);
323             }
324         } catch (RemoteException e) {
325             loge("loadNetworksList: exception from startNetworkQuery " + e);
326             addMessagePreference(R.string.network_query_error);
327         }
328     }
329 
330     /**
331      * This implementation of INetworkQueryServiceCallback is used to receive
332      * callback notifications from the network query service.
333      */
334     private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
335 
336         /** Returns the scan results to the user, this callback will be called at lease one time. */
337         public void onResults(List<CellInfo> results) {
338             if (DBG) logd("get scan results.");
339             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
340             msg.sendToTarget();
341         }
342 
343         /**
344          * Informs the user that the scan has stopped.
345          *
346          * This callback will be called when the scan is finished or cancelled by the user.
347          * The related NetworkScanRequest will be deleted after this callback.
348          */
349         public void onComplete() {
350             if (DBG) logd("network scan completed.");
351             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
352             msg.sendToTarget();
353         }
354 
355         /**
356          * Informs the user that there is some error about the scan.
357          *
358          * This callback will be called whenever there is any error about the scan, and the scan
359          * will be terminated. onComplete() will NOT be called.
360          */
361         public void onError(int error) {
362             if (DBG) logd("get onError callback with error code: " + error);
363             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
364             msg.sendToTarget();
365         }
366     };
367 
368     /**
369      * Updates network operators from {@link INetworkQueryServiceCallback#onResults()}.
370      */
updateNetworkOperators()371     private void updateNetworkOperators() {
372         if (DBG) logd("updateNetworkOperators");
373         if (getActivity() != null) {
374             final View view = getView();
375             final Handler handler = view.getHandler();
376             if (handler != null && handler.hasCallbacks(mUpdateNetworkOperatorsRunnable)) {
377                 return;
378             }
379             view.post(mUpdateNetworkOperatorsRunnable);
380         }
381     }
382 
383     /**
384      * Update the currently available network operators list, which only contains the unregistered
385      * network operators. So if the device has no data and the network operator in the connected
386      * network operator category shows "Disconnected", it will also exist in the available network
387      * operator category for user to select. On the other hand, if the device has data and the
388      * network operator in the connected network operator category shows "Connected", it will not
389      * exist in the available network category.
390      */
updateNetworkOperatorsPreferenceCategory()391     private void updateNetworkOperatorsPreferenceCategory() {
392         mNetworkOperatorsPreferences.removeAll();
393 
394         configConnectedNetworkOperatorsPreferenceCategory();
395         for (int index = 0; index < mCellInfoList.size(); index++) {
396             if (!mCellInfoList.get(index).isRegistered()) {
397                 NetworkOperatorPreference pref = new NetworkOperatorPreference(
398                         mCellInfoList.get(index), getContext(), mForbiddenPlmns);
399                 pref.setKey(CellInfoUtil.getNetworkTitle(mCellInfoList.get(index)));
400                 pref.setOrder(index);
401                 mNetworkOperatorsPreferences.addPreference(pref);
402             }
403         }
404     }
405 
406     /**
407      * Config the connected network operator preference when the page was created. When user get
408      * into this page, the device might or might not have data connection.
409      *   - If the device has data:
410      *     1. use {@code ServiceState#getNetworkRegistrationStates()} to get the currently
411      *        registered cellIdentity, wrap it into a CellInfo;
412      *     2. set the signal strength level as strong;
413      *     3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
414      *        previously connected network operator, since the CellIdentity got from step 1 only has
415      *        PLMN.
416      *   - If the device has no data, we will remove the connected network operators list from the
417      *     screen.
418      */
forceConfigConnectedNetworkOperatorsPreferenceCategory()419     private void forceConfigConnectedNetworkOperatorsPreferenceCategory() {
420         if (DBG) logd("Force config ConnectedNetworkOperatorsPreferenceCategory");
421         if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
422             // Try to get the network registration states
423             ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mPhoneId);
424             List<NetworkRegistrationState> networkList =
425                     ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
426             if (networkList == null || networkList.size() == 0) {
427                 loge("getNetworkRegistrationStates return null");
428                 // Remove the connected network operators category
429                 removeConnectedNetworkOperatorPreference();
430                 return;
431             }
432             CellIdentity cellIdentity = networkList.get(0).getCellIdentity();
433             CellInfo cellInfo = CellInfoUtil.wrapCellInfoWithCellIdentity(cellIdentity);
434             if (cellInfo != null) {
435                 if (DBG) logd("Currently registered cell: " + cellInfo.toString());
436                 NetworkOperatorPreference pref =
437                         new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
438                 pref.setTitle(mTelephonyManager.getNetworkOperatorName());
439                 pref.setSummary(R.string.network_connected);
440                 // Update the signal strength icon, since the default signalStrength value would be
441                 // zero (it would be quite confusing why the connected network has no signal)
442                 pref.setIcon(NetworkOperatorPreference.NUMBER_OF_LEVELS - 1);
443 
444                 mConnectedNetworkOperatorsPreference.addPreference(pref);
445             } else {
446                 loge("Invalid CellIfno: " + cellInfo.toString());
447                 // Remove the connected network operators category
448                 removeConnectedNetworkOperatorPreference();
449             }
450         } else {
451             if (DBG) logd("No currently registered cell");
452             // Remove the connected network operators category
453             removeConnectedNetworkOperatorPreference();
454         }
455     }
456 
457     /**
458      * Configure the ConnectedNetworkOperatorsPreferenceCategory. The category only need to be
459      * configured if the category is currently empty or the operator network title of the previous
460      * connected network is different from the new one.
461      */
configConnectedNetworkOperatorsPreferenceCategory()462     private void configConnectedNetworkOperatorsPreferenceCategory() {
463         if (DBG) logd("config ConnectedNetworkOperatorsPreferenceCategory");
464         // Remove the category if the CellInfo list is empty or does not have registered cell.
465         if (mCellInfoList.size() == 0) {
466             if (DBG) logd("empty cellinfo list");
467             removeConnectedNetworkOperatorPreference();
468         }
469         CellInfo connectedNetworkOperator = null;
470         for (CellInfo cellInfo: mCellInfoList) {
471             if (cellInfo.isRegistered()) {
472                 connectedNetworkOperator = cellInfo;
473                 break;
474             }
475         }
476         if (connectedNetworkOperator == null) {
477             if (DBG) logd("no registered network");
478             removeConnectedNetworkOperatorPreference();
479             return;
480         }
481 
482         // config the category if it is empty.
483         if (mConnectedNetworkOperatorsPreference.getPreferenceCount() == 0) {
484             if (DBG) logd("ConnectedNetworkSelectList is empty, add one");
485             addConnectedNetworkOperatorPreference(connectedNetworkOperator);
486             return;
487         }
488         NetworkOperatorPreference previousConnectedNetworkOperator = (NetworkOperatorPreference)
489                 (mConnectedNetworkOperatorsPreference.getPreference(0));
490 
491         // config the category if the network title of the previous connected network is different
492         // from the new one.
493         String cTitle = CellInfoUtil.getNetworkTitle(connectedNetworkOperator);
494         String pTitle = CellInfoUtil.getNetworkTitle(
495                 previousConnectedNetworkOperator.getCellInfo());
496         if (!cTitle.equals(pTitle)) {
497             if (DBG) logd("reconfig the category: connected network changed");
498             addConnectedNetworkOperatorPreference(connectedNetworkOperator);
499             return;
500         }
501         if (DBG) logd("same network operator is connected, only refresh the connected network");
502         // Otherwise same network operator is connected, only refresh the connected network
503         // operator preference (first and the only one in this category).
504         ((NetworkOperatorPreference) mConnectedNetworkOperatorsPreference.getPreference(0))
505                 .refresh();
506         return;
507     }
508 
509     /**
510      * Creates a Preference for the given {@link CellInfo} and adds it to the
511      * {@link #mConnectedNetworkOperatorsPreference}.
512      */
addConnectedNetworkOperatorPreference(CellInfo cellInfo)513     private void addConnectedNetworkOperatorPreference(CellInfo cellInfo) {
514         if (DBG) logd("addConnectedNetworkOperatorPreference");
515         // Remove the current ConnectedNetworkOperatorsPreference
516         removeConnectedNetworkOperatorPreference();
517         final NetworkOperatorPreference pref =
518                 new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
519         pref.setSummary(R.string.network_connected);
520         mConnectedNetworkOperatorsPreference.addPreference(pref);
521         PreferenceScreen preferenceScreen = getPreferenceScreen();
522         preferenceScreen.addPreference(mConnectedNetworkOperatorsPreference);
523     }
524 
525     /** Removes all preferences and hide the {@link #mConnectedNetworkOperatorsPreference}. */
removeConnectedNetworkOperatorPreference()526     private void removeConnectedNetworkOperatorPreference() {
527         mConnectedNetworkOperatorsPreference.removeAll();
528         PreferenceScreen preferenceScreen = getPreferenceScreen();
529         preferenceScreen.removePreference(mConnectedNetworkOperatorsPreference);
530     }
531 
setProgressBarVisible(boolean visible)532     protected void setProgressBarVisible(boolean visible) {
533         if (mProgressHeader != null) {
534             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
535         }
536     }
537 
addMessagePreference(int messageId)538     private void addMessagePreference(int messageId) {
539         if (DBG) logd("remove callback");
540         getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
541         setProgressBarVisible(false);
542         if (DBG) logd("addMessagePreference");
543         mStatusMessagePreference.setTitle(messageId);
544         removeConnectedNetworkOperatorPreference();
545         mNetworkOperatorsPreferences.removeAll();
546         mNetworkOperatorsPreferences.addPreference(mStatusMessagePreference);
547     }
548 
549     /**
550      * The Scan results may contains several cell infos with different radio technologies and signal
551      * strength for one network operator. Aggregate the CellInfoList by retaining only the cell info
552      * with the strongest signal strength.
553      */
aggregateCellInfoList(List<CellInfo> cellInfoList)554     private List<CellInfo> aggregateCellInfoList(List<CellInfo> cellInfoList) {
555         if (DBG) logd("before aggregate: " + cellInfoList.toString());
556         Map<String, CellInfo> map = new HashMap<>();
557         for (CellInfo cellInfo: cellInfoList) {
558             String networkTitle = CellInfoUtil.getNetworkTitle(cellInfo);
559             if (cellInfo.isRegistered() || !map.containsKey(networkTitle)) {
560                 map.put(networkTitle, cellInfo);
561             } else {
562                 if (map.get(networkTitle).isRegistered()
563                         || CellInfoUtil.getLevel(map.get(networkTitle))
564                         > CellInfoUtil.getLevel(cellInfo)) {
565                     // Skip if the stored cellInfo is registered or has higher signal strength level
566                     continue;
567                 }
568                 // Otherwise replace it with the new CellInfo
569                 map.put(networkTitle, cellInfo);
570             }
571         }
572         return new ArrayList<>(map.values());
573     }
574 
575     /**
576      * Service connection code for the NetworkQueryService.
577      * Handles the work of binding to a local object so that we can make
578      * the appropriate service calls.
579      */
580 
581     /** Local service interface */
582     private INetworkQueryService mNetworkQueryService = null;
583     /** Flag indicating whether we have called bind on the service. */
584     boolean mShouldUnbind;
585 
586     /** Service connection */
587     private final ServiceConnection mNetworkQueryServiceConnection = new ServiceConnection() {
588 
589         /** Handle the task of binding the local object to the service */
590         public void onServiceConnected(ComponentName className, IBinder service) {
591             if (DBG) logd("connection created, binding local service.");
592             mNetworkQueryService = ((NetworkQueryService.LocalBinder) service).getService();
593             // Load the network list only when the service is well connected.
594             loadNetworksList();
595         }
596 
597         /** Handle the task of cleaning up the local binding */
598         public void onServiceDisconnected(ComponentName className) {
599             if (DBG) logd("connection disconnected, cleaning local binding.");
600             mNetworkQueryService = null;
601         }
602     };
603 
bindNetworkQueryService()604     private void bindNetworkQueryService() {
605         if (DBG) logd("bindNetworkQueryService");
606         getContext().bindService(new Intent(getContext(), NetworkQueryService.class).setAction(
607                 NetworkQueryService.ACTION_LOCAL_BINDER),
608                 mNetworkQueryServiceConnection, Context.BIND_AUTO_CREATE);
609         mShouldUnbind = true;
610     }
611 
unbindNetworkQueryService()612     private void unbindNetworkQueryService() {
613         if (DBG) logd("unbindNetworkQueryService");
614         if (mShouldUnbind) {
615             if (DBG) logd("mShouldUnbind is true");
616             // unbind the service.
617             getContext().unbindService(mNetworkQueryServiceConnection);
618             mShouldUnbind = false;
619         }
620     }
621 
622     /**
623      * Call {@link NotificationMgr#updateNetworkSelection(int, int)} to send notification about
624      * no service of user selected operator
625      */
updateNetworkSelection()626     private void updateNetworkSelection() {
627         if (DBG) logd("Update notification about no service of user selected operator");
628         final PhoneGlobals app = PhoneGlobals.getInstance();
629         Phone phone = PhoneFactory.getPhone(mPhoneId);
630         if (phone != null) {
631             ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(phone.getSubId());
632             if (ss != null) {
633                 app.notificationMgr.updateNetworkSelection(ss.getState(), phone.getSubId());
634             }
635         }
636     }
637 
stopNetworkQuery()638     private void stopNetworkQuery() {
639         // Stop the network query process
640         try {
641             if (mNetworkQueryService != null) {
642                 if (DBG) logd("Stop network query");
643                 mNetworkQueryService.stopNetworkQuery();
644                 mNetworkQueryService.unregisterCallback(mCallback);
645             }
646         } catch (RemoteException e) {
647             loge("Exception from stopNetworkQuery " + e);
648         }
649     }
650 
logd(String msg)651     private void logd(String msg) {
652         Log.d(TAG, msg);
653     }
654 
loge(String msg)655     private void loge(String msg) {
656         Log.e(TAG, msg);
657     }
658 }
659