WifiSettings.java revision 2215c7f86895ce1400e9d0cde7ec3326713a5f5b
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
17package com.android.settings.wifi;
18
19import com.android.settings.ProgressCategory;
20import com.android.settings.R;
21
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.net.NetworkInfo;
28import android.net.NetworkInfo.DetailedState;
29import android.net.wifi.ScanResult;
30import android.net.wifi.SupplicantState;
31import android.net.wifi.WifiConfiguration;
32import android.net.wifi.WifiConfiguration.KeyMgmt;
33import android.net.wifi.WifiConfiguration.Status;
34import android.net.wifi.WifiInfo;
35import android.net.wifi.WifiManager;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Message;
39import android.preference.CheckBoxPreference;
40import android.preference.Preference;
41import android.preference.PreferenceActivity;
42import android.preference.PreferenceScreen;
43import android.provider.Settings.Secure;
44import android.security.Credentials;
45import android.security.KeyStore;
46import android.text.TextUtils;
47import android.view.ContextMenu;
48import android.view.ContextMenu.ContextMenuInfo;
49import android.view.Menu;
50import android.view.MenuItem;
51import android.view.View;
52import android.widget.AdapterView.AdapterContextMenuInfo;
53import android.widget.Toast;
54
55import java.util.ArrayList;
56import java.util.List;
57
58public 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 = 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            return;
436
437        if (state == DetailedState.OBTAINING_IPADDR) {
438            mScanner.pause();
439        } else {
440            mScanner.resume();
441        }
442
443        mLastInfo = mWifiManager.getConnectionInfo();
444        if (state != null) {
445            mLastState = state;
446        }
447
448        for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
449            ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState);
450        }
451
452        if (mResetNetworks && (state == DetailedState.CONNECTED ||
453                state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) {
454            updateAccessPoints();
455            enableNetworks();
456        }
457    }
458
459    private void updateWifiState(int state) {
460        if (state == WifiManager.WIFI_STATE_ENABLED) {
461            mScanner.resume();
462            updateAccessPoints();
463        } else {
464            mScanner.pause();
465            mAccessPoints.removeAll();
466        }
467    }
468
469    private class Scanner extends Handler {
470        private int mRetry = 0;
471
472        void resume() {
473            if (!hasMessages(0)) {
474                sendEmptyMessage(0);
475            }
476        }
477
478        void pause() {
479            mRetry = 0;
480            mAccessPoints.setProgress(false);
481            removeMessages(0);
482        }
483
484        @Override
485        public void handleMessage(Message message) {
486            if (mWifiManager.startScanActive()) {
487                mRetry = 0;
488            } else if (++mRetry >= 3) {
489                mRetry = 0;
490                Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,
491                        Toast.LENGTH_LONG).show();
492            }
493            mAccessPoints.setProgress(mRetry != 0);
494            sendEmptyMessageDelayed(0, 6000);
495        }
496    }
497}
498