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