• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.R;
20 
21 import android.content.Context;
22 import android.net.NetworkInfo;
23 import android.net.wifi.ScanResult;
24 import android.net.wifi.WifiConfiguration;
25 import android.net.wifi.WifiInfo;
26 import android.net.wifi.WifiConfiguration.AuthAlgorithm;
27 import android.net.wifi.WifiConfiguration.GroupCipher;
28 import android.net.wifi.WifiConfiguration.KeyMgmt;
29 import android.net.wifi.WifiConfiguration.PairwiseCipher;
30 import android.net.wifi.WifiConfiguration.Protocol;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 public final class AccessPointState implements Comparable<AccessPointState>, Parcelable {
37 
38     private static final String TAG = "AccessPointState";
39 
40     // Constants used for different security types
41     public static final String WPA2 = "WPA2";
42     public static final String WPA = "WPA";
43     public static final String WEP = "WEP";
44     public static final String OPEN = "Open";
45 
46     /* For EAP Enterprise fields */
47     public static final String WPA_EAP = "WPA-EAP";
48     public static final String IEEE8021X = "IEEE8021X";
49 
50     public static final String[] EAP_METHOD = { "PEAP", "TLS", "TTLS" };
51 
52     /** String present in capabilities if the scan result is ad-hoc */
53     private static final String ADHOC_CAPABILITY = "[IBSS]";
54     /** String present in capabilities if the scan result is enterprise secured */
55     private static final String ENTERPRISE_CAPABILITY = "-EAP-";
56 
57     public static final String BSSID_ANY = "any";
58     public static final int NETWORK_ID_NOT_SET = -1;
59     /** This should be used with care! */
60     static final int NETWORK_ID_ANY = -2;
61 
62     public static final int MATCH_NONE = 0;
63     public static final int MATCH_WEAK = 1;
64     public static final int MATCH_STRONG = 2;
65     public static final int MATCH_EXACT = 3;
66 
67     // Don't set these directly, use the setters.
68     public int networkId;
69     public int priority;
70     public boolean hiddenSsid;
71     public int linkSpeed;
72     public int ipAddress;
73     public String bssid;
74     public String ssid;
75     public int signal;
76     public boolean primary;
77     public boolean seen;
78     public boolean configured;
79     public NetworkInfo.DetailedState status;
80     public String security;
81     public boolean disabled;
82 
83     /**
84      * Use this for sorting based on signal strength. It is a heavily-damped
85      * time-averaged weighted signal.
86      */
87     private float signalForSorting = Float.MIN_VALUE;
88 
89     private static final float DAMPING_FACTOR = 0.2f;
90 
91     /**
92      * This will be a user entered password, and NOT taken from wpa_supplicant
93      * (since it would give us *)
94      */
95     private String mPassword;
96     private boolean mConfigHadPassword;
97 
98     public static final int WEP_PASSWORD_AUTO = 0;
99     public static final int WEP_PASSWORD_ASCII = 1;
100     public static final int WEP_PASSWORD_HEX = 2;
101     private int mWepPasswordType;
102 
103     /* Enterprise Fields */
104     public static final int IDENTITY = 0;
105     public static final int ANONYMOUS_IDENTITY = 1;
106     public static final int PRIVATE_KEY_PASSWD = 2;
107     public static final int CLIENT_CERT = 3;
108     public static final int CA_CERT = 4;
109     public static final int PRIVATE_KEY = 5;
110     public static final int MAX_ENTRPRISE_FIELD = 6;
111     private String mEnterpriseFields[] = new String[MAX_ENTRPRISE_FIELD];
112     private String mEap;
113     private String mPhase2;
114 
115     private Context mContext;
116 
117     /**
118      * If > 0, don't refresh (changes are being batched), use
119      * {@link #blockRefresh()} and {@link #unblockRefresh()} only.
120      */
121     private int mBlockRefresh;
122     /**
123      * This will be set by {@link #requestRefresh} and shouldn't be written to
124      * elsewhere.
125      */
126     private boolean mNeedsRefresh;
127 
128     private AccessPointStateCallback mCallback;
129 
130     private StringBuilder mSummaryBuilder = new StringBuilder();
131 
132     interface AccessPointStateCallback {
refreshAccessPointState()133         void refreshAccessPointState();
134     }
135 
AccessPointState(Context context)136     public AccessPointState(Context context) {
137         this();
138 
139         setContext(context);
140     }
141 
AccessPointState()142     private AccessPointState() {
143         bssid = BSSID_ANY;
144         ssid = "";
145         networkId = NETWORK_ID_NOT_SET;
146         hiddenSsid = false;
147     }
148 
setContext(Context context)149     void setContext(Context context) {
150         mContext = context;
151     }
152 
setNetworkId(int networkId)153     public void setNetworkId(int networkId) {
154         this.networkId = networkId;
155     }
156 
setBssid(String bssid)157     public void setBssid(String bssid) {
158         if (bssid != null) {
159             // If the BSSID is a wildcard, do NOT let a specific BSSID replace it
160             if (!this.bssid.equals(BSSID_ANY)) {
161                 this.bssid = bssid;
162             }
163         }
164     }
165 
getWpaSupplicantBssid()166     private String getWpaSupplicantBssid() {
167         return bssid.equals(BSSID_ANY) ? null : bssid;
168     }
169 
convertToQuotedString(String string)170     public static String convertToQuotedString(String string) {
171         if (TextUtils.isEmpty(string)) {
172             return "";
173         }
174 
175         final int lastPos = string.length() - 1;
176         if (lastPos < 0 || (string.charAt(0) == '"' && string.charAt(lastPos) == '"')) {
177             return string;
178         }
179 
180         return "\"" + string + "\"";
181     }
182 
setPrimary(boolean primary)183     public void setPrimary(boolean primary) {
184         if (this.primary != primary) {
185             this.primary = primary;
186             requestRefresh();
187         }
188     }
189 
setSeen(boolean seen)190     public void setSeen(boolean seen) {
191         if (this.seen != seen) {
192             this.seen = seen;
193             requestRefresh();
194         }
195     }
196 
setDisabled(boolean disabled)197     public void setDisabled(boolean disabled) {
198         if (this.disabled != disabled) {
199             this.disabled = disabled;
200             requestRefresh();
201         }
202     }
203 
setSignal(int signal)204     public void setSignal(int signal) {
205 
206         if (signalForSorting == Float.MIN_VALUE) {
207             signalForSorting = signal;
208         } else {
209             signalForSorting = (DAMPING_FACTOR * signal) + ((1-DAMPING_FACTOR) * signalForSorting);
210         }
211 
212         if (this.signal != signal) {
213             this.signal = signal;
214             requestRefresh();
215         }
216     }
217 
getHumanReadableSsid()218     public String getHumanReadableSsid() {
219         if (TextUtils.isEmpty(ssid)) {
220             return "";
221         }
222 
223         final int lastPos = ssid.length() - 1;
224         if (ssid.charAt(0) == '"' && ssid.charAt(lastPos) == '"') {
225             return ssid.substring(1, lastPos);
226         }
227 
228         return ssid;
229     }
230 
setSsid(String ssid)231     public void setSsid(String ssid) {
232         if (ssid != null) {
233             this.ssid = convertToQuotedString(ssid);
234             requestRefresh();
235         }
236     }
237 
setPriority(int priority)238     public void setPriority(int priority) {
239         if (this.priority != priority) {
240             this.priority = priority;
241             requestRefresh();
242         }
243     }
244 
setHiddenSsid(boolean hiddenSsid)245     public void setHiddenSsid(boolean hiddenSsid) {
246         if (this.hiddenSsid != hiddenSsid) {
247             this.hiddenSsid = hiddenSsid;
248             requestRefresh();
249         }
250     }
251 
setLinkSpeed(int linkSpeed)252     public void setLinkSpeed(int linkSpeed) {
253         if (this.linkSpeed != linkSpeed) {
254             this.linkSpeed = linkSpeed;
255             requestRefresh();
256         }
257     }
258 
setIpAddress(int address)259     public void setIpAddress(int address) {
260         if (ipAddress != address) {
261             ipAddress = address;
262             requestRefresh();
263         }
264     }
265 
setConfigured(boolean configured)266     public void setConfigured(boolean configured) {
267         if (this.configured != configured) {
268             this.configured = configured;
269             requestRefresh();
270         }
271     }
272 
setStatus(NetworkInfo.DetailedState status)273     public void setStatus(NetworkInfo.DetailedState status) {
274         if (this.status != status) {
275             this.status = status;
276             requestRefresh();
277         }
278     }
279 
isEnterprise()280     public boolean isEnterprise() {
281         return (WPA_EAP.equals(security) ||
282                 AccessPointState.IEEE8021X.equals(security));
283     }
284 
setSecurity(String security)285     public void setSecurity(String security) {
286         if (TextUtils.isEmpty(this.security) || !this.security.equals(security)) {
287             this.security = security;
288             requestRefresh();
289         }
290     }
291 
hasSecurity()292     public boolean hasSecurity() {
293         return security != null && !security.contains(OPEN);
294     }
295 
getHumanReadableSecurity()296     public String getHumanReadableSecurity() {
297         if (security.equals(OPEN)) return mContext.getString(R.string.wifi_security_open);
298         else if (security.equals(WEP)) return mContext.getString(R.string.wifi_security_wep);
299         else if (security.equals(WPA)) return mContext.getString(R.string.wifi_security_wpa);
300         else if (security.equals(WPA2)) return mContext.getString(R.string.wifi_security_wpa2);
301         else if (security.equals(WPA_EAP)) return mContext.getString(R.string.wifi_security_wpa_eap);
302         else if (security.equals(IEEE8021X)) return mContext.getString(R.string.wifi_security_ieee8021x);
303 
304         return mContext.getString(R.string.wifi_security_unknown);
305     }
306 
updateFromScanResult(ScanResult scanResult)307     public void updateFromScanResult(ScanResult scanResult) {
308         blockRefresh();
309 
310         // We don't keep specific AP BSSIDs and instead leave that as wildcard
311 
312         setSeen(true);
313         setSsid(scanResult.SSID);
314         if (networkId == NETWORK_ID_NOT_SET) {
315             // Since ScanResults don't cross-reference network ID, we set it as a wildcard
316             setNetworkId(NETWORK_ID_ANY);
317         }
318         setSignal(scanResult.level);
319         setSecurity(getScanResultSecurity(scanResult));
320         unblockRefresh();
321     }
322 
323     /**
324      * @return The security of a given {@link ScanResult}.
325      */
getScanResultSecurity(ScanResult scanResult)326     public static String getScanResultSecurity(ScanResult scanResult) {
327         final String cap = scanResult.capabilities;
328         final String[] securityModes = { WEP, WPA, WPA2, WPA_EAP, IEEE8021X };
329         for (int i = securityModes.length - 1; i >= 0; i--) {
330             if (cap.contains(securityModes[i])) {
331                 return securityModes[i];
332             }
333         }
334 
335         return OPEN;
336     }
337 
338     /**
339      * @return Whether the given ScanResult represents an adhoc network.
340      */
isAdhoc(ScanResult scanResult)341     public static boolean isAdhoc(ScanResult scanResult) {
342         return scanResult.capabilities.contains(ADHOC_CAPABILITY);
343     }
344 
345     /**
346      * @return Whether the given ScanResult has enterprise security.
347      */
isEnterprise(ScanResult scanResult)348     public static boolean isEnterprise(ScanResult scanResult) {
349         return scanResult.capabilities.contains(ENTERPRISE_CAPABILITY);
350     }
351 
updateFromWifiConfiguration(WifiConfiguration wifiConfig)352     public void updateFromWifiConfiguration(WifiConfiguration wifiConfig) {
353         if (wifiConfig != null) {
354             blockRefresh();
355             setBssid(wifiConfig.BSSID);
356             setNetworkId(wifiConfig.networkId);
357             setPriority(wifiConfig.priority);
358             setHiddenSsid(wifiConfig.hiddenSSID);
359             setSsid(wifiConfig.SSID);
360             setConfigured(true);
361             setDisabled(wifiConfig.status == WifiConfiguration.Status.DISABLED);
362             parseWifiConfigurationSecurity(wifiConfig);
363             unblockRefresh();
364         }
365     }
366 
setPassword(String password)367     public void setPassword(String password) {
368         setPassword(password, WEP_PASSWORD_AUTO);
369     }
370 
setPassword(String password, int wepPasswordType)371     public void setPassword(String password, int wepPasswordType) {
372         mPassword = password;
373         mWepPasswordType = wepPasswordType;
374     }
375 
376     /* For Enterprise Fields */
setEnterpriseField(int field, String value)377     public void setEnterpriseField(int field, String value) {
378         if ((value != null) && (field >= 0) && (field < MAX_ENTRPRISE_FIELD)) {
379             this.mEnterpriseFields[field] = value;
380             requestRefresh();
381         }
382     }
383 
setPhase2(String phase2)384     public void setPhase2(String phase2) {
385         if (!TextUtils.isEmpty(phase2) && (!phase2.equals("None"))) {
386             mPhase2 = phase2;
387         }
388     }
389 
getPhase2()390     public String getPhase2() {
391         return mPhase2;
392     }
393 
setEap(int method)394     public void setEap(int method) {
395         mEap =  EAP_METHOD[method];
396         requestRefresh();
397     }
398 
getEap()399     public String getEap() {
400         return mEap;
401     }
getEnterpriseField(int field)402     public String getEnterpriseField(int field) {
403         if(field >=0 && field < MAX_ENTRPRISE_FIELD) {
404             return mEnterpriseFields[field];
405         }
406         return null;
407     }
408 
hasPassword()409     public boolean hasPassword() {
410         return !TextUtils.isEmpty(mPassword) || mConfigHadPassword;
411     }
412 
hasPassword(WifiConfiguration wifiConfig)413     private static boolean hasPassword(WifiConfiguration wifiConfig) {
414         return !TextUtils.isEmpty(wifiConfig.preSharedKey)
415                 || !TextUtils.isEmpty(wifiConfig.wepKeys[0])
416                 || !TextUtils.isEmpty(wifiConfig.wepKeys[1])
417                 || !TextUtils.isEmpty(wifiConfig.wepKeys[2])
418                 || !TextUtils.isEmpty(wifiConfig.wepKeys[3]);
419     }
420 
parseWifiConfigurationSecurity(WifiConfiguration wifiConfig)421     private void parseWifiConfigurationSecurity(WifiConfiguration wifiConfig) {
422         setSecurity(getWifiConfigurationSecurity(wifiConfig));
423         mConfigHadPassword = hasPassword(wifiConfig);
424     }
425 
426     /**
427      * @return The security of a given {@link WifiConfiguration}.
428      */
getWifiConfigurationSecurity(WifiConfiguration wifiConfig)429     public static String getWifiConfigurationSecurity(WifiConfiguration wifiConfig) {
430 
431         if (wifiConfig.allowedKeyManagement.get(KeyMgmt.NONE)) {
432             // If we never set group ciphers, wpa_supplicant puts all of them.
433             // For open, we don't set group ciphers.
434             // For WEP, we specifically only set WEP40 and WEP104, so CCMP
435             // and TKIP should not be there.
436             if (!wifiConfig.allowedGroupCiphers.get(GroupCipher.CCMP)
437                     && (wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP40)
438                             || wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP104))) {
439                 return WEP;
440             } else {
441                 return OPEN;
442             }
443         } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
444             return WPA_EAP;
445         } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
446             return IEEE8021X;
447         } else if (wifiConfig.allowedProtocols.get(Protocol.RSN)) {
448             return WPA2;
449         } else if (wifiConfig.allowedProtocols.get(Protocol.WPA)) {
450             return WPA;
451         } else {
452             Log.w(TAG, "Unknown security type from WifiConfiguration, falling back on open.");
453             return OPEN;
454         }
455     }
456 
updateFromWifiInfo(WifiInfo wifiInfo, NetworkInfo.DetailedState state)457     public void updateFromWifiInfo(WifiInfo wifiInfo, NetworkInfo.DetailedState state) {
458         if (wifiInfo != null) {
459             blockRefresh();
460             setBssid(wifiInfo.getBSSID());
461             setLinkSpeed(wifiInfo.getLinkSpeed());
462             setNetworkId(wifiInfo.getNetworkId());
463             setIpAddress(wifiInfo.getIpAddress());
464             setSsid(wifiInfo.getSSID());
465             if (state != null) {
466                 setStatus(state);
467             }
468             setHiddenSsid(wifiInfo.getHiddenSSID());
469             unblockRefresh();
470         }
471     }
472 
473     /**
474      * @return Whether this AP can be connected to at the moment.
475      */
isConnectable()476     public boolean isConnectable() {
477         return !primary && seen;
478     }
479 
480     /**
481      * @return Whether this AP can be forgotten at the moment.
482      */
isForgetable()483     public boolean isForgetable() {
484         return configured;
485     }
486 
487     /**
488      * Updates the state as if it were never configured.
489      * <p>
490      * Note: This will not pass the forget call to the Wi-Fi API.
491      */
forget()492     public void forget() {
493         blockRefresh();
494         setConfigured(false);
495         setNetworkId(NETWORK_ID_NOT_SET);
496         setPrimary(false);
497         setStatus(null);
498         setDisabled(false);
499         unblockRefresh();
500     }
501 
updateWifiConfiguration(WifiConfiguration config)502     public void updateWifiConfiguration(WifiConfiguration config) {
503         config.BSSID = getWpaSupplicantBssid();
504         config.priority = priority;
505         config.hiddenSSID = hiddenSsid;
506         config.SSID = convertToQuotedString(ssid);
507         config.eap = mEap;
508 
509         if (!TextUtils.isEmpty(mPhase2)) {
510             config.phase2 = convertToQuotedString("auth=" + mPhase2);
511         } else {
512             config.phase2 = null;
513         }
514         if (!TextUtils.isEmpty(mEnterpriseFields[IDENTITY])) {
515             config.identity =
516                     convertToQuotedString(mEnterpriseFields[IDENTITY]);
517         } else {
518             config.identity = null;
519         }
520         if (!TextUtils.isEmpty(mEnterpriseFields[ANONYMOUS_IDENTITY])) {
521             config.anonymousIdentity = convertToQuotedString(
522                     mEnterpriseFields[ANONYMOUS_IDENTITY]);
523         } else {
524             config.anonymousIdentity = null;
525         }
526         if (!TextUtils.isEmpty(mEnterpriseFields[CLIENT_CERT])) {
527             config.clientCert = convertToQuotedString(
528                     mEnterpriseFields[CLIENT_CERT]);
529         } else {
530             config.clientCert = null;
531         }
532         if (!TextUtils.isEmpty(mEnterpriseFields[CA_CERT])) {
533             config.caCert = convertToQuotedString(
534                     mEnterpriseFields[CA_CERT]);
535         } else {
536             config.caCert = null;
537         }
538         if (!TextUtils.isEmpty(mEnterpriseFields[PRIVATE_KEY])) {
539             config.privateKey = convertToQuotedString(
540                     mEnterpriseFields[PRIVATE_KEY]);
541         } else {
542             config.privateKey = null;
543         }
544         if (!TextUtils.isEmpty(mEnterpriseFields[PRIVATE_KEY_PASSWD])) {
545             config.privateKeyPasswd = convertToQuotedString(
546                     mEnterpriseFields[PRIVATE_KEY_PASSWD]);
547         } else {
548             config.privateKeyPasswd = null;
549         }
550         setupSecurity(config);
551     }
552 
setupSecurity(WifiConfiguration config)553     private void setupSecurity(WifiConfiguration config) {
554         config.allowedAuthAlgorithms.clear();
555         config.allowedGroupCiphers.clear();
556         config.allowedKeyManagement.clear();
557         config.allowedPairwiseCiphers.clear();
558         config.allowedProtocols.clear();
559 
560         if (TextUtils.isEmpty(security)) {
561             security = OPEN;
562             Log.w(TAG, "Empty security, assuming open");
563         }
564 
565         if (security.equals(WEP)) {
566 
567             // If password is empty, it should be left untouched
568             if (!TextUtils.isEmpty(mPassword)) {
569                 if (mWepPasswordType == WEP_PASSWORD_AUTO) {
570                     if (isHexWepKey(mPassword)) {
571                         config.wepKeys[0] = mPassword;
572                     } else {
573                         config.wepKeys[0] = convertToQuotedString(mPassword);
574                     }
575                 } else {
576                     config.wepKeys[0] = mWepPasswordType == WEP_PASSWORD_ASCII
577                             ? convertToQuotedString(mPassword)
578                             : mPassword;
579                 }
580             }
581 
582             config.wepTxKeyIndex = 0;
583 
584             config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
585             config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
586 
587             config.allowedKeyManagement.set(KeyMgmt.NONE);
588 
589             config.allowedGroupCiphers.set(GroupCipher.WEP40);
590             config.allowedGroupCiphers.set(GroupCipher.WEP104);
591 
592         } else if (security.equals(WPA) || security.equals(WPA2)){
593             config.allowedGroupCiphers.set(GroupCipher.TKIP);
594             config.allowedGroupCiphers.set(GroupCipher.CCMP);
595 
596             config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
597 
598             config.allowedPairwiseCiphers.set(PairwiseCipher.CCMP);
599             config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);
600 
601             config.allowedProtocols.set(security.equals(WPA2) ? Protocol.RSN : Protocol.WPA);
602 
603             // If password is empty, it should be left untouched
604             if (!TextUtils.isEmpty(mPassword)) {
605                 if (mPassword.length() == 64 && isHex(mPassword)) {
606                     // Goes unquoted as hex
607                     config.preSharedKey = mPassword;
608                 } else {
609                     // Goes quoted as ASCII
610                     config.preSharedKey = convertToQuotedString(mPassword);
611                 }
612             }
613 
614         } else if (security.equals(OPEN)) {
615             config.allowedKeyManagement.set(KeyMgmt.NONE);
616         } else if (security.equals(WPA_EAP) || security.equals(IEEE8021X)) {
617             config.allowedGroupCiphers.set(GroupCipher.TKIP);
618             config.allowedGroupCiphers.set(GroupCipher.CCMP);
619             if (security.equals(WPA_EAP)) {
620                 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
621             } else {
622                 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
623             }
624             if (!TextUtils.isEmpty(mPassword)) {
625                 config.password = convertToQuotedString(mPassword);
626             }
627         }
628     }
629 
isHexWepKey(String wepKey)630     private static boolean isHexWepKey(String wepKey) {
631         final int len = wepKey.length();
632 
633         // WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?)
634         if (len != 10 && len != 26 && len != 58) {
635             return false;
636         }
637 
638         return isHex(wepKey);
639     }
640 
isHex(String key)641     private static boolean isHex(String key) {
642         for (int i = key.length() - 1; i >= 0; i--) {
643             final char c = key.charAt(i);
644             if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) {
645                 return false;
646             }
647         }
648 
649         return true;
650     }
651 
setCallback(AccessPointStateCallback callback)652     public void setCallback(AccessPointStateCallback callback) {
653         mCallback = callback;
654     }
655 
blockRefresh()656     void blockRefresh() {
657         mBlockRefresh++;
658     }
659 
unblockRefresh()660     void unblockRefresh() {
661         if (--mBlockRefresh == 0 && mNeedsRefresh) {
662             requestRefresh();
663         }
664     }
665 
requestRefresh()666     private void requestRefresh() {
667         if (mBlockRefresh > 0) {
668             mNeedsRefresh = true;
669             return;
670         }
671 
672         if (mCallback != null) {
673             mCallback.refreshAccessPointState();
674         }
675 
676         mNeedsRefresh = false;
677     }
678 
679     /**
680      * {@inheritDoc}
681      * @see #hashCode()
682      * @see #equals(Object)
683      */
matches(int otherNetworkId, String otherBssid, String otherSsid, String otherSecurity)684     public int matches(int otherNetworkId, String otherBssid, String otherSsid,
685             String otherSecurity) {
686 
687         // Whenever this method is touched, please ensure #equals and #hashCode
688         // still work with the changes here!
689 
690         if (otherSsid == null) {
691             if (WifiLayer.LOGV) {
692                 Log.w(TAG, "BSSID: " + otherBssid + ", SSID: " + otherSsid);
693             }
694             return MATCH_NONE;
695         }
696 
697         /*
698          * If we both have 'security' set, it must match (an open network still
699          * has 'security' set to OPEN)
700          */
701         if (security != null && otherSecurity != null) {
702             if (!security.equals(otherSecurity)) {
703                 return MATCH_NONE;
704             }
705         }
706 
707         // WifiConfiguration gives an empty bssid as a BSSID wildcard
708         if (TextUtils.isEmpty(otherBssid)) {
709             otherBssid = AccessPointState.BSSID_ANY;
710         }
711 
712         final boolean networkIdMatches = networkId == otherNetworkId;
713         if (!networkIdMatches && networkId != NETWORK_ID_ANY && otherNetworkId != NETWORK_ID_ANY) {
714             // Network IDs don't match (e.g., 1 & 2 or unset & 1) and neither is a wildcard
715             return MATCH_NONE;
716         }
717 
718         if (networkIdMatches && otherNetworkId != NETWORK_ID_NOT_SET
719                 && otherNetworkId != NETWORK_ID_ANY) {
720             // Network ID matches (they're set to the same ID)
721             return MATCH_EXACT;
722         }
723 
724         // So now, network IDs aren't set or at least one is a wildcard
725 
726         final boolean bssidMatches = bssid.equals(otherBssid);
727         final boolean otherBssidIsWildcard = otherBssid.equals(BSSID_ANY);
728         if (bssidMatches && !otherBssidIsWildcard) {
729             // BSSID matches (and neither is a wildcard)
730             return MATCH_STRONG;
731         }
732 
733         if (!bssidMatches && !bssid.equals(BSSID_ANY) && !otherBssidIsWildcard) {
734             // BSSIDs don't match (e.g., 00:24:21:21:42:12 & 42:12:44:21:22:52)
735             // and neither is a wildcard
736             return MATCH_NONE;
737         }
738 
739         // So now, BSSIDs are both wildcards
740 
741         final boolean ssidMatches = ssid.equals(otherSsid);
742         if (ssidMatches) {
743             // SSID matches
744             return MATCH_WEAK;
745         }
746 
747         return MATCH_NONE;
748     }
749 
750     /**
751      * {@inheritDoc}
752      * @see #matches(int, String, String)
753      * @see #equals(Object)
754      */
755     @Override
hashCode()756     public int hashCode() {
757         // Two equal() objects must have same hashCode.
758         // With Wi-Fi, the broadest match is if two SSIDs are the same.  The finer-grained matches
759         // imply this (for example, the same network IDs means the same WifiConfiguration which
760         // means the same SSID).
761         // See #matches for the exact matching algorithm we use.
762         return ssid != null ? ssid.hashCode() : 0;
763     }
764 
765     /**
766      * {@inheritDoc}
767      * @see #matches(int, String, String)
768      * @see #hashCode()
769      */
770     @Override
equals(Object o)771     public boolean equals(Object o) {
772         if (!o.getClass().equals(getClass())) {
773             return false;
774         }
775 
776         final AccessPointState other = (AccessPointState) o;
777 
778         // To see which conditions cause two AccessPointStates to be equal, see
779         // where #matches returns MATCH_WEAK or greater.
780 
781         return matches(other.networkId, other.bssid, other.ssid, other.security) >= MATCH_WEAK;
782     }
783 
matchesWifiConfiguration(WifiConfiguration wifiConfig)784     public int matchesWifiConfiguration(WifiConfiguration wifiConfig) {
785         String security = getWifiConfigurationSecurity(wifiConfig);
786         return matches(wifiConfig.networkId, wifiConfig.BSSID, wifiConfig.SSID, security);
787     }
788 
getSummarizedStatus()789     String getSummarizedStatus() {
790         StringBuilder sb = mSummaryBuilder;
791         sb.delete(0, sb.length());
792 
793         if (primary && status != null) {
794             buildSummary(sb, WifiStatus.getPrintable(mContext, status), true);
795 
796         } else if (!seen) {
797             buildSummary(sb, mContext.getString(R.string.summary_not_in_range), true);
798 
799             // Remembered comes second in this case
800             if (!primary && configured) {
801                 buildSummary(sb, mContext.getString(R.string.summary_remembered), true);
802             }
803 
804         } else {
805             if (configured && disabled) {
806                 // The connection failure overrides all in this case
807                 return mContext.getString(R.string.summary_connection_failed);
808             }
809 
810             // Remembered comes first in this case
811             if (!primary && configured) {
812                 buildSummary(sb, mContext.getString(R.string.summary_remembered), true);
813             }
814 
815             // If it is seen (and not the primary), show the security type
816             String verboseSecurity = getVerboseSecurity();
817             if (verboseSecurity != null) {
818                 buildSummary(sb, verboseSecurity, true);
819             }
820         }
821 
822         return sb.toString();
823     }
824 
getVerboseSecurity()825     private String getVerboseSecurity() {
826         if (WEP.equals(security)) {
827             return mContext.getString(R.string.wifi_security_verbose_wep);
828         } else if (WPA.equals(security)) {
829             return mContext.getString(R.string.wifi_security_verbose_wpa);
830         } else if (WPA2.equals(security)) {
831             return mContext.getString(R.string.wifi_security_verbose_wpa2);
832         } else if (OPEN.equals(security)) {
833             return mContext.getString(R.string.wifi_security_verbose_open);
834         } else if (WPA_EAP.equals(security)) {
835             return mContext.getString(R.string.wifi_security_verbose_wpa_eap);
836         } else if (IEEE8021X.equals(security)) {
837             return mContext.getString(R.string.wifi_security_verbose_ieee8021x);
838         } else {
839             return null;
840         }
841     }
842 
buildSummary(StringBuilder sb, String string, boolean autoUpperCaseFirstLetter)843     private void buildSummary(StringBuilder sb, String string, boolean autoUpperCaseFirstLetter) {
844         if (sb.length() == 0) {
845             if (autoUpperCaseFirstLetter && string.length() > 1
846                     && Character.isLowerCase(string.charAt(0))
847                     && !Character.isUpperCase(string.charAt(1))) {
848                 sb.append(Character.toUpperCase(string.charAt(0))).append(string, 1,
849                         string.length());
850             } else {
851                 sb.append(string);
852             }
853         } else {
854             sb.append(", ");
855             sb.append(string);
856         }
857     }
858 
compareTo(AccessPointState other)859     public int compareTo(AccessPointState other) {
860         // This ranks the states for displaying in the AP list, not for
861         // connecting to (wpa_supplicant does that using the WifiConfiguration's
862         // priority field).
863 
864         // Clarity > efficiency, of this logic:
865         int comparison;
866 
867         // Primary
868         comparison = (other.primary ? 1 : 0) - (primary ? 1 : 0);
869         if (comparison != 0) return comparison;
870 
871         // Currently seen (similar to, but not always the same as within range)
872         comparison = (other.seen ? 1 : 0) - (seen ? 1 : 0);
873         if (comparison != 0) return comparison;
874 
875         // Configured
876         comparison = (other.configured ? 1 : 0) - (configured ? 1 : 0);
877         if (comparison != 0) return comparison;
878 
879         if (!configured) {
880             // Neither are configured
881 
882             // Open network
883             comparison = (hasSecurity() ? 1 : 0) - (other.hasSecurity() ? 1 : 0);
884             if (comparison != 0) return comparison;
885         }
886 
887         // Signal strength
888         comparison = (int) (other.signalForSorting - signalForSorting);
889         if (comparison != 0) return comparison;
890 
891         // Alphabetical
892         return ssid.compareToIgnoreCase(other.ssid);
893     }
894 
toString()895     public String toString() {
896         return ssid + " (" + bssid + ", " + networkId + ", " + super.toString() + ")";
897     }
898 
899     /** Implement the Parcelable interface */
writeToParcel(Parcel dest, int flags)900     public void writeToParcel(Parcel dest, int flags) {
901         dest.writeString(bssid);
902         dest.writeInt(configured ? 1 : 0);
903         dest.writeInt(ipAddress);
904         dest.writeInt(linkSpeed);
905         dest.writeInt(networkId);
906         dest.writeInt(primary ? 1 : 0);
907         dest.writeInt(priority);
908         dest.writeInt(hiddenSsid ? 1 : 0);
909         dest.writeString(security);
910         dest.writeInt(seen ? 1 : 0);
911         dest.writeInt(disabled ? 1 : 0);
912         dest.writeInt(signal);
913         dest.writeString(ssid);
914         dest.writeString(status != null ? status.toString() : null);
915         dest.writeString(mPassword);
916         dest.writeInt(mConfigHadPassword ? 1 : 0);
917         dest.writeInt(mWepPasswordType);
918     }
919 
920     /** Implement the Parcelable interface */
describeContents()921     public int describeContents() {
922         return 0;
923     }
924 
925     /** Implement the Parcelable interface */
926     public static final Creator<AccessPointState> CREATOR =
927         new Creator<AccessPointState>() {
928             public AccessPointState createFromParcel(Parcel in) {
929                 AccessPointState state = new AccessPointState();
930                 state.bssid = in.readString();
931                 state.configured = in.readInt() == 1;
932                 state.ipAddress = in.readInt();
933                 state.linkSpeed = in.readInt();
934                 state.networkId = in.readInt();
935                 state.primary = in.readInt() == 1;
936                 state.priority = in.readInt();
937                 state.hiddenSsid = in.readInt() == 1;
938                 state.security = in.readString();
939                 state.seen = in.readInt() == 1;
940                 state.disabled = in.readInt() == 1;
941                 state.signal = in.readInt();
942                 state.ssid = in.readString();
943                 String statusStr = in.readString();
944                 if (statusStr != null) {
945                     state.status = NetworkInfo.DetailedState.valueOf(statusStr);
946                 }
947                 state.mPassword = in.readString();
948                 state.mConfigHadPassword = in.readInt() == 1;
949                 state.mWepPasswordType = in.readInt();
950                 return state;
951             }
952 
953             public AccessPointState[] newArray(int size) {
954                 return new AccessPointState[size];
955             }
956         };
957 
958 
959 }
960