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