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 = 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
    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
    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 
    228     public String getNormalizedCarrierName(OperatorInfo ni) {
    229         if (ni != null) {
    230             return ni.getOperatorAlphaLong() + " (" + ni.getOperatorNumeric() + ")";
    231         }
    232         return null;
    233     }
    234 
    235     @Override
    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
    268     public void onResume() {
    269         super.onResume();
    270         mIsForeground = true;
    271     }
    272 
    273     @Override
    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
    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
    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
    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 
    344     private void displayEmptyNetworkList(boolean flag) {
    345         mNetworkList.setTitle(flag ? R.string.empty_networks_list : R.string.label_available);
    346     }
    347 
    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 
    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 
    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 
    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 
    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      */
    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 
    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 
    500     private void clearList() {
    501         for (Preference p : mNetworkMap.keySet()) {
    502             mNetworkList.removePreference(p);
    503         }
    504         mNetworkMap.clear();
    505     }
    506 
    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 
    517     private void log(String msg) {
    518         Log.d(LOG_TAG, "[NetworksList] " + msg);
    519     }
    520 }
    521