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