Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2010 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.settings.wifi;
     18 
     19 import com.android.settings.ProgressCategory;
     20 import com.android.settings.R;
     21 
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.net.NetworkInfo;
     28 import android.net.NetworkInfo.DetailedState;
     29 import android.net.wifi.ScanResult;
     30 import android.net.wifi.SupplicantState;
     31 import android.net.wifi.WifiConfiguration;
     32 import android.net.wifi.WifiConfiguration.KeyMgmt;
     33 import android.net.wifi.WifiConfiguration.Status;
     34 import android.net.wifi.WifiInfo;
     35 import android.net.wifi.WifiManager;
     36 import android.os.Bundle;
     37 import android.os.Handler;
     38 import android.os.Message;
     39 import android.preference.CheckBoxPreference;
     40 import android.preference.Preference;
     41 import android.preference.PreferenceActivity;
     42 import android.preference.PreferenceScreen;
     43 import android.provider.Settings.Secure;
     44 import android.security.Credentials;
     45 import android.security.KeyStore;
     46 import android.text.TextUtils;
     47 import android.view.ContextMenu;
     48 import android.view.ContextMenu.ContextMenuInfo;
     49 import android.view.Menu;
     50 import android.view.MenuItem;
     51 import android.view.View;
     52 import android.widget.AdapterView.AdapterContextMenuInfo;
     53 import android.widget.Toast;
     54 
     55 import java.util.ArrayList;
     56 import java.util.List;
     57 
     58 public class WifiSettings extends PreferenceActivity implements DialogInterface.OnClickListener {
     59     private static final int MENU_ID_SCAN = Menu.FIRST;
     60     private static final int MENU_ID_ADVANCED = Menu.FIRST + 1;
     61     private static final int MENU_ID_CONNECT = Menu.FIRST + 2;
     62     private static final int MENU_ID_FORGET = Menu.FIRST + 3;
     63     private static final int MENU_ID_MODIFY = Menu.FIRST + 4;
     64 
     65     private final IntentFilter mFilter;
     66     private final BroadcastReceiver mReceiver;
     67     private final Scanner mScanner;
     68 
     69     private WifiManager mWifiManager;
     70     private WifiEnabler mWifiEnabler;
     71     private CheckBoxPreference mNotifyOpenNetworks;
     72     private ProgressCategory mAccessPoints;
     73     private Preference mAddNetwork;
     74 
     75     private DetailedState mLastState;
     76     private WifiInfo mLastInfo;
     77     private int mLastPriority;
     78 
     79     private boolean mResetNetworks = false;
     80     private int mKeyStoreNetworkId = -1;
     81 
     82     private AccessPoint mSelected;
     83     private WifiDialog mDialog;
     84 
     85     public WifiSettings() {
     86         mFilter = new IntentFilter();
     87         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
     88         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
     89         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
     90         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
     91         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
     92         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
     93 
     94         mReceiver = new BroadcastReceiver() {
     95             @Override
     96             public void onReceive(Context context, Intent intent) {
     97                 handleEvent(intent);
     98             }
     99         };
    100 
    101         mScanner = new Scanner();
    102     }
    103 
    104     @Override
    105     protected void onCreate(Bundle savedInstanceState) {
    106         super.onCreate(savedInstanceState);
    107 
    108         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    109 
    110         if (getIntent().getBooleanExtra("only_access_points", false)) {
    111             addPreferencesFromResource(R.xml.wifi_access_points);
    112         } else {
    113             addPreferencesFromResource(R.xml.wifi_settings);
    114             mWifiEnabler = new WifiEnabler(this,
    115                     (CheckBoxPreference) findPreference("enable_wifi"));
    116             mNotifyOpenNetworks =
    117                     (CheckBoxPreference) findPreference("notify_open_networks");
    118             mNotifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(),
    119                     Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
    120         }
    121 
    122         mAccessPoints = (ProgressCategory) findPreference("access_points");
    123         mAccessPoints.setOrderingAsAdded(false);
    124         mAddNetwork = findPreference("add_network");
    125 
    126         registerForContextMenu(getListView());
    127     }
    128 
    129     @Override
    130     protected void onResume() {
    131         super.onResume();
    132         if (mWifiEnabler != null) {
    133             mWifiEnabler.resume();
    134         }
    135         registerReceiver(mReceiver, mFilter);
    136         if (mKeyStoreNetworkId != -1 && KeyStore.getInstance().test() == KeyStore.NO_ERROR) {
    137             connect(mKeyStoreNetworkId);
    138         }
    139         mKeyStoreNetworkId = -1;
    140     }
    141 
    142     @Override
    143     protected void onPause() {
    144         super.onPause();
    145         if (mWifiEnabler != null) {
    146             mWifiEnabler.pause();
    147         }
    148         unregisterReceiver(mReceiver);
    149         mScanner.pause();
    150         if (mDialog != null) {
    151             mDialog.dismiss();
    152             mDialog = null;
    153         }
    154         if (mResetNetworks) {
    155             enableNetworks();
    156         }
    157     }
    158 
    159     @Override
    160     public boolean onCreateOptionsMenu(Menu menu) {
    161         menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
    162                 .setIcon(R.drawable.ic_menu_scan_network);
    163         menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
    164                 .setIcon(android.R.drawable.ic_menu_manage);
    165         return super.onCreateOptionsMenu(menu);
    166     }
    167 
    168     @Override
    169     public boolean onOptionsItemSelected(MenuItem item) {
    170         switch (item.getItemId()) {
    171             case MENU_ID_SCAN:
    172                 if (mWifiManager.isWifiEnabled()) {
    173                     mScanner.resume();
    174                 }
    175                 return true;
    176             case MENU_ID_ADVANCED:
    177                 startActivity(new Intent(this, AdvancedSettings.class));
    178                 return true;
    179         }
    180         return super.onOptionsItemSelected(item);
    181     }
    182 
    183     @Override
    184     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
    185         if (info instanceof AdapterContextMenuInfo) {
    186             Preference preference = (Preference) getListView().getItemAtPosition(
    187                     ((AdapterContextMenuInfo) info).position);
    188 
    189             if (preference instanceof AccessPoint) {
    190                 mSelected = (AccessPoint) preference;
    191                 menu.setHeaderTitle(mSelected.ssid);
    192                 if (mSelected.getLevel() != -1 && mSelected.getState() == null) {
    193                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
    194                 }
    195                 if (mSelected.networkId != -1) {
    196                     menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
    197                     if (mSelected.security != AccessPoint.SECURITY_NONE) {
    198                         menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
    199                     }
    200                 }
    201             }
    202         }
    203     }
    204 
    205     @Override
    206     public boolean onContextItemSelected(MenuItem item) {
    207         if (mSelected == null) {
    208             return super.onContextItemSelected(item);
    209         }
    210         switch (item.getItemId()) {
    211             case MENU_ID_CONNECT:
    212                 if (mSelected.networkId != -1) {
    213                     if (!requireKeyStore(mSelected.getConfig())) {
    214                         connect(mSelected.networkId);
    215                     }
    216                 } else if (mSelected.security == AccessPoint.SECURITY_NONE) {
    217                     // Shortcut for open networks.
    218                     WifiConfiguration config = new WifiConfiguration();
    219                     config.SSID = AccessPoint.convertToQuotedString(mSelected.ssid);
    220                     config.allowedKeyManagement.set(KeyMgmt.NONE);
    221                     int networkId = mWifiManager.addNetwork(config);
    222                     mWifiManager.enableNetwork(networkId, false);
    223                     connect(networkId);
    224                 } else {
    225                     showDialog(mSelected, false);
    226                 }
    227                 return true;
    228             case MENU_ID_FORGET:
    229                 forget(mSelected.networkId);
    230                 return true;
    231             case MENU_ID_MODIFY:
    232                 showDialog(mSelected, true);
    233                 return true;
    234         }
    235         return super.onContextItemSelected(item);
    236     }
    237 
    238     @Override
    239     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
    240         if (preference instanceof AccessPoint) {
    241             mSelected = (AccessPoint) preference;
    242             showDialog(mSelected, false);
    243         } else if (preference == mAddNetwork) {
    244             mSelected = null;
    245             showDialog(null, true);
    246         } else if (preference == mNotifyOpenNetworks) {
    247             Secure.putInt(getContentResolver(),
    248                     Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
    249                     mNotifyOpenNetworks.isChecked() ? 1 : 0);
    250         } else {
    251             return super.onPreferenceTreeClick(screen, preference);
    252         }
    253         return true;
    254     }
    255 
    256     public void onClick(DialogInterface dialogInterface, int button) {
    257         if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {
    258             forget(mSelected.networkId);
    259         } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) {
    260             WifiConfiguration config = mDialog.getConfig();
    261 
    262             if (config == null) {
    263                 if (mSelected != null && !requireKeyStore(mSelected.getConfig())) {
    264                     connect(mSelected.networkId);
    265                 }
    266             } else if (config.networkId != -1) {
    267                 if (mSelected != null) {
    268                     mWifiManager.updateNetwork(config);
    269                     saveNetworks();
    270                 }
    271             } else {
    272                 int networkId = mWifiManager.addNetwork(config);
    273                 if (networkId != -1) {
    274                     mWifiManager.enableNetwork(networkId, false);
    275                     config.networkId = networkId;
    276                     if (mDialog.edit || requireKeyStore(config)) {
    277                         saveNetworks();
    278                     } else {
    279                         connect(networkId);
    280                     }
    281                 }
    282             }
    283         }
    284     }
    285 
    286     private void showDialog(AccessPoint accessPoint, boolean edit) {
    287         if (mDialog != null) {
    288             mDialog.dismiss();
    289         }
    290         mDialog = new WifiDialog(this, this, accessPoint, edit);
    291         mDialog.show();
    292     }
    293 
    294     private boolean requireKeyStore(WifiConfiguration config) {
    295         if (WifiDialog.requireKeyStore(config) &&
    296                 KeyStore.getInstance().test() != KeyStore.NO_ERROR) {
    297             mKeyStoreNetworkId = config.networkId;
    298             Credentials.getInstance().unlock(this);
    299             return true;
    300         }
    301         return false;
    302     }
    303 
    304     private void forget(int networkId) {
    305         mWifiManager.removeNetwork(networkId);
    306         saveNetworks();
    307     }
    308 
    309     private void connect(int networkId) {
    310         if (networkId == -1) {
    311             return;
    312         }
    313 
    314         // Reset the priority of each network if it goes too high.
    315         if (mLastPriority > 1000000) {
    316             for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
    317                 AccessPoint accessPoint = (AccessPoint) mAccessPoints.getPreference(i);
    318                 if (accessPoint.networkId != -1) {
    319                     WifiConfiguration config = new WifiConfiguration();
    320                     config.networkId = accessPoint.networkId;
    321                     config.priority = 0;
    322                     mWifiManager.updateNetwork(config);
    323                 }
    324             }
    325             mLastPriority = 0;
    326         }
    327 
    328         // Set to the highest priority and save the configuration.
    329         WifiConfiguration config = new WifiConfiguration();
    330         config.networkId = networkId;
    331         config.priority = ++mLastPriority;
    332         mWifiManager.updateNetwork(config);
    333         saveNetworks();
    334 
    335         // Connect to network by disabling others.
    336         mWifiManager.enableNetwork(networkId, true);
    337         mWifiManager.reconnect();
    338         mResetNetworks = true;
    339     }
    340 
    341     private void enableNetworks() {
    342         for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
    343             WifiConfiguration config = ((AccessPoint) mAccessPoints.getPreference(i)).getConfig();
    344             if (config != null && config.status != Status.ENABLED) {
    345                 mWifiManager.enableNetwork(config.networkId, false);
    346             }
    347         }
    348         mResetNetworks = false;
    349     }
    350 
    351     private void saveNetworks() {
    352         // Always save the configuration with all networks enabled.
    353         enableNetworks();
    354         mWifiManager.saveConfiguration();
    355         updateAccessPoints();
    356     }
    357 
    358     private void updateAccessPoints() {
    359         List<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
    360 
    361         List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    362         if (configs != null) {
    363             mLastPriority = 0;
    364             for (WifiConfiguration config : configs) {
    365                 if (config.priority > mLastPriority) {
    366                     mLastPriority = config.priority;
    367                 }
    368 
    369                 // Shift the status to make enableNetworks() more efficient.
    370                 if (config.status == Status.CURRENT) {
    371                     config.status = Status.ENABLED;
    372                 } else if (mResetNetworks && config.status == Status.DISABLED) {
    373                     config.status = Status.CURRENT;
    374                 }
    375 
    376                 AccessPoint accessPoint = new AccessPoint(this, config);
    377                 accessPoint.update(mLastInfo, mLastState);
    378                 accessPoints.add(accessPoint);
    379             }
    380         }
    381 
    382         List<ScanResult> results = mWifiManager.getScanResults();
    383         if (results != null) {
    384             for (ScanResult result : results) {
    385                 // Ignore hidden and ad-hoc networks.
    386                 if (result.SSID == null || result.SSID.length() == 0 ||
    387                         result.capabilities.contains("[IBSS]")) {
    388                     continue;
    389                 }
    390 
    391                 boolean found = false;
    392                 for (AccessPoint accessPoint : accessPoints) {
    393                     if (accessPoint.update(result)) {
    394                         found = true;
    395                     }
    396                 }
    397                 if (!found) {
    398                     accessPoints.add(new AccessPoint(this, result));
    399                 }
    400             }
    401         }
    402 
    403         mAccessPoints.removeAll();
    404         for (AccessPoint accessPoint : accessPoints) {
    405             mAccessPoints.addPreference(accessPoint);
    406         }
    407     }
    408 
    409     private void handleEvent(Intent intent) {
    410         String action = intent.getAction();
    411         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
    412             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    413                     WifiManager.WIFI_STATE_UNKNOWN));
    414         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
    415             updateAccessPoints();
    416         } else if (WifiManager.NETWORK_IDS_CHANGED_ACTION.equals(action)) {
    417             if (mSelected != null && mSelected.networkId != -1) {
    418                 mSelected = null;
    419             }
    420             updateAccessPoints();
    421         } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
    422             updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
    423                     intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
    424         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
    425             updateConnectionState(((NetworkInfo) intent.getParcelableExtra(
    426                     WifiManager.EXTRA_NETWORK_INFO)).getDetailedState());
    427         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
    428             updateConnectionState(null);
    429         }
    430     }
    431 
    432     private void updateConnectionState(DetailedState state) {
    433         /* sticky broadcasts can call this when wifi is disabled */
    434         if (!mWifiManager.isWifiEnabled()) {
    435             mScanner.pause();
    436             return;
    437         }
    438 
    439         if (state == DetailedState.OBTAINING_IPADDR) {
    440             mScanner.pause();
    441         } else {
    442             mScanner.resume();
    443         }
    444 
    445         mLastInfo = mWifiManager.getConnectionInfo();
    446         if (state != null) {
    447             mLastState = state;
    448         }
    449 
    450         for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
    451             ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState);
    452         }
    453 
    454         if (mResetNetworks && (state == DetailedState.CONNECTED ||
    455                 state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) {
    456             updateAccessPoints();
    457             enableNetworks();
    458         }
    459     }
    460 
    461     private void updateWifiState(int state) {
    462         if (state == WifiManager.WIFI_STATE_ENABLED) {
    463             mScanner.resume();
    464             updateAccessPoints();
    465         } else {
    466             mScanner.pause();
    467             mAccessPoints.removeAll();
    468         }
    469     }
    470 
    471     private class Scanner extends Handler {
    472         private int mRetry = 0;
    473 
    474         void resume() {
    475             if (!hasMessages(0)) {
    476                 sendEmptyMessage(0);
    477             }
    478         }
    479 
    480         void pause() {
    481             mRetry = 0;
    482             mAccessPoints.setProgress(false);
    483             removeMessages(0);
    484         }
    485 
    486         @Override
    487         public void handleMessage(Message message) {
    488             if (mWifiManager.startScanActive()) {
    489                 mRetry = 0;
    490             } else if (++mRetry >= 3) {
    491                 mRetry = 0;
    492                 Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,
    493                         Toast.LENGTH_LONG).show();
    494                 return;
    495             }
    496             mAccessPoints.setProgress(mRetry != 0);
    497             sendEmptyMessageDelayed(0, 6000);
    498         }
    499     }
    500 }
    501