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