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