AccessPoint.java revision de58555d25cd38b4ed0dfa5d2baf444f85ab7742
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 android.content.Context;
20import android.net.NetworkInfo.DetailedState;
21import android.net.wifi.ScanResult;
22import android.net.wifi.WifiConfiguration;
23import android.net.wifi.WifiConfiguration.KeyMgmt;
24import android.net.wifi.WifiInfo;
25import android.net.wifi.WifiManager;
26import android.os.Bundle;
27import android.preference.Preference;
28import android.util.Log;
29import android.view.View;
30import android.widget.ImageView;
31
32import com.android.settings.R;
33
34class AccessPoint extends Preference {
35    static final String TAG = "Settings.AccessPoint";
36
37    private static final String KEY_DETAILEDSTATE = "key_detailedstate";
38    private static final String KEY_WIFIINFO = "key_wifiinfo";
39    private static final String KEY_SCANRESULT = "key_scanresult";
40    private static final String KEY_CONFIG = "key_config";
41
42    private static final int[] STATE_SECURED = {
43        R.attr.state_encrypted
44    };
45    private static final int[] STATE_NONE = {};
46
47    /** These values are matched in string arrays -- changes must be kept in sync */
48    static final int SECURITY_NONE = 0;
49    static final int SECURITY_WEP = 1;
50    static final int SECURITY_PSK = 2;
51    static final int SECURITY_EAP = 3;
52
53    enum PskType {
54        UNKNOWN,
55        WPA,
56        WPA2,
57        WPA_WPA2
58    }
59
60    String ssid;
61    String bssid;
62    int security;
63    int networkId;
64    boolean wpsAvailable = false;
65
66    PskType pskType = PskType.UNKNOWN;
67
68    private WifiConfiguration mConfig;
69    /* package */ScanResult mScanResult;
70
71    private int mRssi;
72    private WifiInfo mInfo;
73    private DetailedState mState;
74
75    static int getSecurity(WifiConfiguration config) {
76        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
77            return SECURITY_PSK;
78        }
79        if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
80                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
81            return SECURITY_EAP;
82        }
83        return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
84    }
85
86    private static int getSecurity(ScanResult result) {
87        if (result.capabilities.contains("WEP")) {
88            return SECURITY_WEP;
89        } else if (result.capabilities.contains("PSK")) {
90            return SECURITY_PSK;
91        } else if (result.capabilities.contains("EAP")) {
92            return SECURITY_EAP;
93        }
94        return SECURITY_NONE;
95    }
96
97    public String getSecurityString(boolean concise) {
98        Context context = getContext();
99        switch(security) {
100            case SECURITY_EAP:
101                return concise ? context.getString(R.string.wifi_security_short_eap) :
102                    context.getString(R.string.wifi_security_eap);
103            case SECURITY_PSK:
104                switch (pskType) {
105                    case WPA:
106                        return concise ? context.getString(R.string.wifi_security_short_wpa) :
107                            context.getString(R.string.wifi_security_wpa);
108                    case WPA2:
109                        return concise ? context.getString(R.string.wifi_security_short_wpa2) :
110                            context.getString(R.string.wifi_security_wpa2);
111                    case WPA_WPA2:
112                        return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
113                            context.getString(R.string.wifi_security_wpa_wpa2);
114                    case UNKNOWN:
115                    default:
116                        return concise ? context.getString(R.string.wifi_security_short_psk_generic)
117                                : context.getString(R.string.wifi_security_psk_generic);
118                }
119            case SECURITY_WEP:
120                return concise ? context.getString(R.string.wifi_security_short_wep) :
121                    context.getString(R.string.wifi_security_wep);
122            case SECURITY_NONE:
123            default:
124                return concise ? "" : context.getString(R.string.wifi_security_none);
125        }
126    }
127
128    private static PskType getPskType(ScanResult result) {
129        boolean wpa = result.capabilities.contains("WPA-PSK");
130        boolean wpa2 = result.capabilities.contains("WPA2-PSK");
131        if (wpa2 && wpa) {
132            return PskType.WPA_WPA2;
133        } else if (wpa2) {
134            return PskType.WPA2;
135        } else if (wpa) {
136            return PskType.WPA;
137        } else {
138            Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
139            return PskType.UNKNOWN;
140        }
141    }
142
143    AccessPoint(Context context, WifiConfiguration config) {
144        super(context);
145        setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
146        loadConfig(config);
147        refresh();
148    }
149
150    AccessPoint(Context context, ScanResult result) {
151        super(context);
152        setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
153        loadResult(result);
154        refresh();
155    }
156
157    AccessPoint(Context context, Bundle savedState) {
158        super(context);
159        setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
160
161        mConfig = savedState.getParcelable(KEY_CONFIG);
162        if (mConfig != null) {
163            loadConfig(mConfig);
164        }
165        mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT);
166        if (mScanResult != null) {
167            loadResult(mScanResult);
168        }
169        mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
170        if (savedState.containsKey(KEY_DETAILEDSTATE)) {
171            mState = DetailedState.valueOf(savedState.getString(KEY_DETAILEDSTATE));
172        }
173        update(mInfo, mState);
174    }
175
176    public void saveWifiState(Bundle savedState) {
177        savedState.putParcelable(KEY_CONFIG, mConfig);
178        savedState.putParcelable(KEY_SCANRESULT, mScanResult);
179        savedState.putParcelable(KEY_WIFIINFO, mInfo);
180        if (mState != null) {
181            savedState.putString(KEY_DETAILEDSTATE, mState.toString());
182        }
183    }
184
185    private void loadConfig(WifiConfiguration config) {
186        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
187        bssid = config.BSSID;
188        security = getSecurity(config);
189        networkId = config.networkId;
190        mRssi = Integer.MAX_VALUE;
191        mConfig = config;
192    }
193
194    private void loadResult(ScanResult result) {
195        ssid = result.SSID;
196        bssid = result.BSSID;
197        security = getSecurity(result);
198        wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS");
199        if (security == SECURITY_PSK)
200            pskType = getPskType(result);
201        networkId = -1;
202        mRssi = result.level;
203        mScanResult = result;
204    }
205
206    @Override
207    protected void onBindView(View view) {
208        super.onBindView(view);
209        ImageView signal = (ImageView) view.findViewById(R.id.signal);
210        if (mRssi == Integer.MAX_VALUE) {
211            signal.setImageDrawable(null);
212        } else {
213            signal.setImageLevel(getLevel());
214            signal.setImageResource(R.drawable.wifi_signal);
215            signal.setImageState((security != SECURITY_NONE) ?
216                    STATE_SECURED : STATE_NONE, true);
217        }
218    }
219
220    @Override
221    public int compareTo(Preference preference) {
222        if (!(preference instanceof AccessPoint)) {
223            return 1;
224        }
225        AccessPoint other = (AccessPoint) preference;
226        // Active one goes first.
227        if (mInfo != other.mInfo) {
228            return (mInfo != null) ? -1 : 1;
229        }
230        // Reachable one goes before unreachable one.
231        if ((mRssi ^ other.mRssi) < 0) {
232            return (mRssi != Integer.MAX_VALUE) ? -1 : 1;
233        }
234        // Configured one goes before unconfigured one.
235        if ((networkId ^ other.networkId) < 0) {
236            return (networkId != -1) ? -1 : 1;
237        }
238        // Sort by signal strength.
239        int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi);
240        if (difference != 0) {
241            return difference;
242        }
243        // Sort by ssid.
244        return ssid.compareToIgnoreCase(other.ssid);
245    }
246
247    boolean update(ScanResult result) {
248        if (ssid.equals(result.SSID) && security == getSecurity(result)) {
249            if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) {
250                int oldLevel = getLevel();
251                mRssi = result.level;
252                if (getLevel() != oldLevel) {
253                    notifyChanged();
254                }
255            }
256            // This flag only comes from scans, is not easily saved in config
257            if (security == SECURITY_PSK) {
258                pskType = getPskType(result);
259            }
260            refresh();
261            return true;
262        }
263        return false;
264    }
265
266    void update(WifiInfo info, DetailedState state) {
267        boolean reorder = false;
268        if (info != null && networkId != WifiConfiguration.INVALID_NETWORK_ID
269                && networkId == info.getNetworkId()) {
270            reorder = (mInfo == null);
271            mRssi = info.getRssi();
272            mInfo = info;
273            mState = state;
274            refresh();
275        } else if (mInfo != null) {
276            reorder = true;
277            mInfo = null;
278            mState = null;
279            refresh();
280        }
281        if (reorder) {
282            notifyHierarchyChanged();
283        }
284    }
285
286    int getLevel() {
287        if (mRssi == Integer.MAX_VALUE) {
288            return -1;
289        }
290        return WifiManager.calculateSignalLevel(mRssi, 4);
291    }
292
293    WifiConfiguration getConfig() {
294        return mConfig;
295    }
296
297    WifiInfo getInfo() {
298        return mInfo;
299    }
300
301    DetailedState getState() {
302        return mState;
303    }
304
305    static String removeDoubleQuotes(String string) {
306        int length = string.length();
307        if ((length > 1) && (string.charAt(0) == '"')
308                && (string.charAt(length - 1) == '"')) {
309            return string.substring(1, length - 1);
310        }
311        return string;
312    }
313
314    static String convertToQuotedString(String string) {
315        return "\"" + string + "\"";
316    }
317
318    /** Updates the title and summary; may indirectly call notifyChanged()  */
319    private void refresh() {
320        setTitle(ssid);
321
322        Context context = getContext();
323        if (mState != null) { // This is the active connection
324            setSummary(Summary.get(context, mState));
325        } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
326            setSummary(context.getString(R.string.wifi_not_in_range));
327        } else if (mConfig != null && mConfig.status == WifiConfiguration.Status.DISABLED) {
328            switch (mConfig.disableReason) {
329                case WifiConfiguration.DISABLED_AUTH_FAILURE:
330                    setSummary(context.getString(R.string.wifi_disabled_password_failure));
331                    break;
332                case WifiConfiguration.DISABLED_DHCP_FAILURE:
333                case WifiConfiguration.DISABLED_DNS_FAILURE:
334                    setSummary(context.getString(R.string.wifi_disabled_network_failure));
335                    break;
336                case WifiConfiguration.DISABLED_UNKNOWN_REASON:
337                    setSummary(context.getString(R.string.wifi_disabled_generic));
338            }
339        } else { // In range, not disabled.
340            StringBuilder summary = new StringBuilder();
341            if (mConfig != null) { // Is saved network
342                summary.append(context.getString(R.string.wifi_remembered));
343            }
344
345            if (security != SECURITY_NONE) {
346                String securityStrFormat;
347                if (summary.length() == 0) {
348                    securityStrFormat = context.getString(R.string.wifi_secured_first_item);
349                } else {
350                    securityStrFormat = context.getString(R.string.wifi_secured_second_item);
351                }
352                summary.append(String.format(securityStrFormat, getSecurityString(true)));
353            }
354
355            if (mConfig == null && wpsAvailable) { // Only list WPS available for unsaved networks
356                if (summary.length() == 0) {
357                    summary.append(context.getString(R.string.wifi_wps_available_first_item));
358                } else {
359                    summary.append(context.getString(R.string.wifi_wps_available_second_item));
360                }
361            }
362            setSummary(summary.toString());
363        }
364    }
365
366    /**
367     * Generate and save a default wifiConfiguration with common values.
368     * Can only be called for unsecured networks.
369     * @hide
370     */
371    protected void generateOpenNetworkConfig() {
372        if (security != SECURITY_NONE)
373            throw new IllegalStateException();
374        if (mConfig != null)
375            return;
376        mConfig = new WifiConfiguration();
377        mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
378        mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
379    }
380}
381