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