summaryrefslogtreecommitdiffstats
path: root/wifi/java/android
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /wifi/java/android
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'wifi/java/android')
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl67
-rw-r--r--wifi/java/android/net/wifi/ScanResult.aidl19
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java116
-rw-r--r--wifi/java/android/net/wifi/SupplicantState.java177
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.aidl19
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java351
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.aidl19
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java281
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java745
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java373
-rw-r--r--wifi/java/android/net/wifi/WifiNative.java123
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java1710
-rw-r--r--wifi/java/android/net/wifi/package.html12
13 files changed, 4012 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
new file mode 100644
index 0000000..5a9f75c
--- /dev/null
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.ScanResult;
+import android.net.DhcpInfo;
+
+/**
+ * Interface that allows controlling and querying Wi-Fi connectivity.
+ *
+ * {@hide}
+ */
+interface IWifiManager
+{
+ List<WifiConfiguration> getConfiguredNetworks();
+
+ int addOrUpdateNetwork(in WifiConfiguration config);
+
+ boolean removeNetwork(int netId);
+
+ boolean enableNetwork(int netId, boolean disableOthers);
+
+ boolean disableNetwork(int netId);
+
+ boolean pingSupplicant();
+
+ boolean startScan();
+
+ List<ScanResult> getScanResults();
+
+ boolean disconnect();
+
+ boolean reconnect();
+
+ boolean reassociate();
+
+ WifiInfo getConnectionInfo();
+
+ boolean setWifiEnabled(boolean enable);
+
+ int getWifiState();
+
+ boolean saveConfiguration();
+
+ DhcpInfo getDhcpInfo();
+
+ boolean acquireWifiLock(IBinder lock, String tag);
+
+ boolean releaseWifiLock(IBinder lock);
+}
+
diff --git a/wifi/java/android/net/wifi/ScanResult.aidl b/wifi/java/android/net/wifi/ScanResult.aidl
new file mode 100644
index 0000000..bb66722
--- /dev/null
+++ b/wifi/java/android/net/wifi/ScanResult.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable ScanResult;
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
new file mode 100644
index 0000000..32261de
--- /dev/null
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Describes information about a detected access point. In addition
+ * to the attributes described here, the supplicant keeps track of
+ * {@code quality}, {@code noise}, and {@code maxbitrate} attributes,
+ * but does not currently report them to external clients.
+ */
+public class ScanResult implements Parcelable {
+ /** The network name. */
+ public String SSID;
+ /** The address of the access point. */
+ public String BSSID;
+ /**
+ * Describes the authentication, key management, and encryption schemes
+ * supported by the access point.
+ */
+ public String capabilities;
+ /**
+ * The detected signal level in dBm. At least those are the units used by
+ * the TI driver.
+ */
+ public int level;
+ /**
+ * The frequency in MHz of the channel over which the client is communicating
+ * with the access point.
+ */
+ public int frequency;
+
+ /**
+ * We'd like to obtain the following attributes,
+ * but they are not reported via the socket
+ * interface, even though they are known
+ * internally by wpa_supplicant.
+ * {@hide}
+ */
+ public ScanResult(String SSID, String BSSID, String caps, int level, int frequency) {
+ this.SSID = SSID;
+ this.BSSID = BSSID;
+ this.capabilities = caps;
+ this.level = level;
+ this.frequency = frequency;
+ //networkConfig = null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String none = "<none>";
+
+ sb.append("SSID: ").
+ append(SSID == null ? none : SSID).
+ append(", BSSID: ").
+ append(BSSID == null ? none : BSSID).
+ append(", capabilities: ").
+ append(capabilities == null ? none : capabilities).
+ append(", level: ").
+ append(level).
+ append(", frequency: ").
+ append(frequency);
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(SSID);
+ dest.writeString(BSSID);
+ dest.writeString(capabilities);
+ dest.writeInt(level);
+ dest.writeInt(frequency);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ScanResult> CREATOR =
+ new Creator<ScanResult>() {
+ public ScanResult createFromParcel(Parcel in) {
+ return new ScanResult(
+ in.readString(),
+ in.readString(),
+ in.readString(),
+ in.readInt(),
+ in.readInt()
+ );
+ }
+
+ public ScanResult[] newArray(int size) {
+ return new ScanResult[size];
+ }
+ };
+
+}
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
new file mode 100644
index 0000000..169b2d6
--- /dev/null
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * From <code>defs.h</code> in <code>wpa_supplicant</code>.
+ * <p/>
+ * These enumeration values are used to indicate the current wpa_supplicant
+ * state. This is more fine-grained than most users will be interested in.
+ * In general, it is better to use
+ * {@link android.net.NetworkInfo.State NetworkInfo.State}.
+ * <p/>
+ * Note, the order of these enum constants must match the numerical values of the
+ * state constants in <code>defs.h</code> in <code>wpa_supplicant</code>.
+ */
+public enum SupplicantState implements Parcelable {
+ /**
+ * This state indicates that client is not associated, but is likely to
+ * start looking for an access point. This state is entered when a
+ * connection is lost.
+ */
+ DISCONNECTED,
+
+ /**
+ * Inactive state (wpa_supplicant disabled).
+ * <p/>
+ * This state is entered if there are no enabled networks in the
+ * configuration. wpa_supplicant is not trying to associate with a new
+ * network and external interaction (e.g., ctrl_iface call to add or
+ * enable a network) is needed to start association.
+ */
+ INACTIVE,
+
+ /**
+ * Scanning for a network.
+ * <p/>
+ * This state is entered when wpa_supplicant starts scanning for a
+ * network.
+ */
+ SCANNING,
+
+ /**
+ * Trying to associate with a BSS/SSID.
+ * <p/>
+ * This state is entered when wpa_supplicant has found a suitable BSS
+ * to associate with and the driver is configured to try to associate
+ * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+ * state is entered when the driver is configured to try to associate
+ * with a network using the configured SSID and security policy.
+ */
+ ASSOCIATING,
+
+ /**
+ * Association completed.
+ * <p/>
+ * This state is entered when the driver reports that association has
+ * been successfully completed with an AP. If IEEE 802.1X is used
+ * (with or without WPA/WPA2), wpa_supplicant remains in this state
+ * until the IEEE 802.1X/EAPOL authentication has been completed.
+ */
+ ASSOCIATED,
+
+ /**
+ * WPA 4-Way Key Handshake in progress.
+ * <p/>
+ * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+ * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+ * frame after association. In case of WPA-EAP, this state is entered
+ * when the IEEE 802.1X/EAPOL authentication has been completed.
+ */
+ FOUR_WAY_HANDSHAKE,
+
+ /**
+ * WPA Group Key Handshake in progress.
+ * <p/>
+ * This state is entered when 4-Way Key Handshake has been completed
+ * (i.e., when the supplicant sends out message 4/4) and when Group
+ * Key rekeying is started by the AP (i.e., when supplicant receives
+ * message 1/2).
+ */
+ GROUP_HANDSHAKE,
+
+ /**
+ * All authentication completed.
+ * <p/>
+ * This state is entered when the full authentication process is
+ * completed. In case of WPA2, this happens when the 4-Way Handshake is
+ * successfully completed. With WPA, this state is entered after the
+ * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+ * completed after dynamic keys are received (or if not used, after
+ * the EAP authentication has been completed). With static WEP keys and
+ * plaintext connections, this state is entered when an association
+ * has been completed.
+ * <p/>
+ * This state indicates that the supplicant has completed its
+ * processing for the association phase and that data connection is
+ * fully configured. Note, however, that there may not be any IP
+ * address associated with the connection yet. Typically, a DHCP
+ * request needs to be sent at this point to obtain an address.
+ */
+ COMPLETED,
+
+ /**
+ * An Android-added state that is reported when a client issues an
+ * explicit DISCONNECT command. In such a case, the supplicant is
+ * not only dissociated from the current access point (as for the
+ * DISCONNECTED state above), but it also does not attempt to connect
+ * to any access point until a RECONNECT or REASSOCIATE command
+ * is issued by the client.
+ */
+ DORMANT,
+
+ /**
+ * No connection to wpa_supplicant.
+ * <p/>
+ * This is an additional pseudo-state to handle the case where
+ * wpa_supplicant is not running and/or we have not been able
+ * to establish a connection to it.
+ */
+ UNINITIALIZED,
+
+ /**
+ * A pseudo-state that should normally never be seen.
+ */
+ INVALID;
+
+ /**
+ * Returns {@code true} if the supplicant state is valid and {@code false}
+ * otherwise.
+ * @param state The supplicant state
+ * @return {@code true} if the supplicant state is valid and {@code false}
+ * otherwise.
+ */
+ public static boolean isValidState(SupplicantState state) {
+ return state != UNINITIALIZED && state != INVALID;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name());
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<SupplicantState> CREATOR =
+ new Creator<SupplicantState>() {
+ public SupplicantState createFromParcel(Parcel in) {
+ return SupplicantState.valueOf(in.readString());
+ }
+
+ public SupplicantState[] newArray(int size) {
+ return new SupplicantState[size];
+ }
+ };
+
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.aidl b/wifi/java/android/net/wifi/WifiConfiguration.aidl
new file mode 100644
index 0000000..237a74d
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable WifiConfiguration;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
new file mode 100644
index 0000000..42a7af7
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.BitSet;
+
+/**
+ * A class representing a configured Wi-Fi network, including the
+ * security configuration. Android will not necessarily support
+ * all of these security schemes initially.
+ */
+public class WifiConfiguration implements Parcelable {
+
+ /** {@hide} */
+ public static final String ssidVarName = "ssid";
+ /** {@hide} */
+ public static final String bssidVarName = "bssid";
+ /** {@hide} */
+ public static final String pskVarName = "psk";
+ /** {@hide} */
+ public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" };
+ /** {@hide} */
+ public static final String wepTxKeyIdxVarName = "wep_tx_keyidx";
+ /** {@hide} */
+ public static final String priorityVarName = "priority";
+ /** {@hide} */
+ public static final String hiddenSSIDVarName = "scan_ssid";
+
+ /**
+ * Recognized key management schemes.
+ */
+ public static class KeyMgmt {
+ private KeyMgmt() { }
+
+ /** WPA is not used; plaintext or static WEP could be used. */
+ public static final int NONE = 0;
+ /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */
+ public static final int WPA_PSK = 1;
+ /** WPA using EAP authentication. Generally used with an external authentication server. */
+ public static final int WPA_EAP = 2;
+ /** IEEE 802.1X using EAP authentication and (optionally) dynamically
+ * generated WEP keys. */
+ public static final int IEEE8021X = 3;
+
+ public static final String varName = "key_mgmt";
+
+ public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X" };
+ }
+
+ /**
+ * Recognized security protocols.
+ */
+ public static class Protocol {
+ private Protocol() { }
+
+ /** WPA/IEEE 802.11i/D3.0 */
+ public static final int WPA = 0;
+ /** WPA2/IEEE 802.11i */
+ public static final int RSN = 1;
+
+ public static final String varName = "proto";
+
+ public static final String[] strings = { "WPA", "RSN" };
+ }
+
+ /**
+ * Recognized IEEE 802.11 authentication algorithms.
+ */
+ public static class AuthAlgorithm {
+ private AuthAlgorithm() { }
+
+ /** Open System authentication (required for WPA/WPA2) */
+ public static final int OPEN = 0;
+ /** Shared Key authentication (requires static WEP keys) */
+ public static final int SHARED = 1;
+ /** LEAP/Network EAP (only used with LEAP) */
+ public static final int LEAP = 2;
+
+ public static final String varName = "auth_alg";
+
+ public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
+ }
+
+ /**
+ * Recognized pairwise ciphers for WPA.
+ */
+ public static class PairwiseCipher {
+ private PairwiseCipher() { }
+
+ /** Use only Group keys (deprecated) */
+ public static final int NONE = 0;
+ /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
+ public static final int TKIP = 1;
+ /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
+ public static final int CCMP = 2;
+
+ public static final String varName = "pairwise";
+
+ public static final String[] strings = { "NONE", "TKIP", "CCMP" };
+ }
+
+ /**
+ * Recognized group ciphers.
+ * <pre>
+ * CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+ * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+ * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
+ * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11)
+ * </pre>
+ */
+ public static class GroupCipher {
+ private GroupCipher() { }
+
+ /** WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) */
+ public static final int WEP40 = 0;
+ /** WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key */
+ public static final int WEP104 = 1;
+ /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
+ public static final int TKIP = 2;
+ /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
+ public static final int CCMP = 3;
+
+ public static final String varName = "group";
+
+ public static final String[] strings = { "WEP40", "WEP104", "TKIP", "CCMP" };
+ }
+
+ /** Possible status of a network configuration. */
+ public static class Status {
+ private Status() { }
+
+ /** this is the network we are currently connected to */
+ public static final int CURRENT = 0;
+ /** supplicant will not attempt to use this network */
+ public static final int DISABLED = 1;
+ /** supplicant will consider this network available for association */
+ public static final int ENABLED = 2;
+
+ public static final String[] strings = { "current", "disabled", "enabled" };
+ }
+
+ /**
+ * The ID number that the supplicant uses to identify this
+ * network configuration entry. This must be passed as an argument
+ * to most calls into the supplicant.
+ */
+ public int networkId;
+
+ /**
+ * The current status of this network configuration entry.
+ * @see Status
+ */
+ public int status;
+ /**
+ * The network's SSID. Can either be an ASCII string,
+ * which must be enclosed in double quotation marks
+ * (e.g., {@code &quot;MyNetwork&quot;}, or a string of
+ * hex digits,which are not enclosed in quotes
+ * (e.g., {@code 01a243f405}).
+ */
+ public String SSID;
+ /**
+ * When set, this network configuration entry should only be used when
+ * associating with the AP having the specified BSSID. The value is
+ * a string in the format of an Ethernet MAC address, e.g.,
+ * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
+ */
+ public String BSSID;
+
+ /**
+ * Pre-shared key for use with WPA-PSK.
+ * <p/>
+ * When the value of this key is read, the actual key is
+ * not returned, just a "*" if the key has a value, or the null
+ * string otherwise.
+ */
+ public String preSharedKey;
+ /**
+ * Up to four WEP keys. Either an ASCII string enclosed in double
+ * quotation marks (e.g., {@code &quot;abcdef&quot;} or a string
+ * of hex digits (e.g., {@code 0102030405}).
+ * <p/>
+ * When the value of one of these keys is read, the actual key is
+ * not returned, just a "*" if the key has a value, or the null
+ * string otherwise.
+ */
+ public String[] wepKeys;
+
+ /** Default WEP key index, ranging from 0 to 3. */
+ public int wepTxKeyIndex;
+
+ /**
+ * Priority determines the preference given to a network by {@code wpa_supplicant}
+ * when choosing an access point with which to associate.
+ */
+ public int priority;
+
+ /**
+ * This is a network that does not broadcast its SSID, so an
+ * SSID-specific probe request must be used for scans.
+ */
+ public boolean hiddenSSID;
+
+ /**
+ * The set of key management protocols supported by this configuration.
+ * See {@link KeyMgmt} for descriptions of the values.
+ * Defaults to WPA-PSK WPA-EAP.
+ */
+ public BitSet allowedKeyManagement;
+ /**
+ * The set of security protocols supported by this configuration.
+ * See {@link Protocol} for descriptions of the values.
+ * Defaults to WPA RSN.
+ */
+ public BitSet allowedProtocols;
+ /**
+ * The set of authentication protocols supported by this configuration.
+ * See {@link AuthAlgorithm} for descriptions of the values.
+ * Defaults to automatic selection.
+ */
+ public BitSet allowedAuthAlgorithms;
+ /**
+ * The set of pairwise ciphers for WPA supported by this configuration.
+ * See {@link PairwiseCipher} for descriptions of the values.
+ * Defaults to CCMP TKIP.
+ */
+ public BitSet allowedPairwiseCiphers;
+ /**
+ * The set of group ciphers supported by this configuration.
+ * See {@link GroupCipher} for descriptions of the values.
+ * Defaults to CCMP TKIP WEP104 WEP40.
+ */
+ public BitSet allowedGroupCiphers;
+
+ public WifiConfiguration() {
+ networkId = -1;
+ SSID = null;
+ BSSID = null;
+ priority = 0;
+ hiddenSSID = false;
+ allowedKeyManagement = new BitSet();
+ allowedProtocols = new BitSet();
+ allowedAuthAlgorithms = new BitSet();
+ allowedPairwiseCiphers = new BitSet();
+ allowedGroupCiphers = new BitSet();
+ wepKeys = new String[4];
+ for (int i = 0; i < wepKeys.length; i++)
+ wepKeys[i] = null;
+ }
+
+ /**
+ * Construct a WifiConfiguration from a scanned network
+ * @param scannedAP the scan result used to construct the config entry
+ * TODO: figure out whether this is a useful way to construct a new entry.
+ *
+ public WifiConfiguration(ScanResult scannedAP) {
+ networkId = -1;
+ SSID = scannedAP.SSID;
+ BSSID = scannedAP.BSSID;
+ }
+ */
+
+ private static BitSet readBitSet(Parcel src) {
+ int cardinality = src.readInt();
+
+ BitSet set = new BitSet();
+ for (int i = 0; i < cardinality; i++)
+ set.set(src.readInt());
+
+ return set;
+ }
+
+ private static void writeBitSet(Parcel dest, BitSet set) {
+ int nextSetBit = -1;
+
+ dest.writeInt(set.cardinality());
+
+ while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
+ dest.writeInt(nextSetBit);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(networkId);
+ dest.writeInt(status);
+ dest.writeString(SSID);
+ dest.writeString(BSSID);
+ dest.writeString(preSharedKey);
+ for (String wepKey : wepKeys)
+ dest.writeString(wepKey);
+ dest.writeInt(wepTxKeyIndex);
+ dest.writeInt(priority);
+ dest.writeInt(hiddenSSID ? 1 : 0);
+
+ writeBitSet(dest, allowedKeyManagement);
+ writeBitSet(dest, allowedProtocols);
+ writeBitSet(dest, allowedAuthAlgorithms);
+ writeBitSet(dest, allowedPairwiseCiphers);
+ writeBitSet(dest, allowedGroupCiphers);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiConfiguration> CREATOR =
+ new Creator<WifiConfiguration>() {
+ public WifiConfiguration createFromParcel(Parcel in) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = in.readInt();
+ config.status = in.readInt();
+ config.SSID = in.readString();
+ config.BSSID = in.readString();
+ config.preSharedKey = in.readString();
+ for (int i = 0; i < config.wepKeys.length; i++)
+ config.wepKeys[i] = in.readString();
+ config.wepTxKeyIndex = in.readInt();
+ config.priority = in.readInt();
+ config.hiddenSSID = in.readInt() != 0;
+ config.allowedKeyManagement = readBitSet(in);
+ config.allowedProtocols = readBitSet(in);
+ config.allowedAuthAlgorithms = readBitSet(in);
+ config.allowedPairwiseCiphers = readBitSet(in);
+ config.allowedGroupCiphers = readBitSet(in);
+ return config;
+ }
+
+ public WifiConfiguration[] newArray(int size) {
+ return new WifiConfiguration[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiInfo.aidl b/wifi/java/android/net/wifi/WifiInfo.aidl
new file mode 100644
index 0000000..db47f0b
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable WifiInfo;
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
new file mode 100644
index 0000000..4312bfd
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.NetworkInfo.DetailedState;
+
+import java.util.EnumMap;
+
+/**
+ * Describes the state of any Wifi connection that is active or
+ * is in the process of being set up.
+ */
+public class WifiInfo implements Parcelable {
+ /**
+ * This is the map described in the Javadoc comment above. The positions
+ * of the elements of the array must correspond to the ordinal values
+ * of <code>DetailedState</code>.
+ */
+ private static final EnumMap<SupplicantState, DetailedState> stateMap =
+ new EnumMap<SupplicantState, DetailedState>(SupplicantState.class);
+
+ static {
+ stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED);
+ stateMap.put(SupplicantState.INACTIVE, DetailedState.IDLE);
+ stateMap.put(SupplicantState.SCANNING, DetailedState.SCANNING);
+ stateMap.put(SupplicantState.ASSOCIATING, DetailedState.CONNECTING);
+ stateMap.put(SupplicantState.ASSOCIATED, DetailedState.CONNECTING);
+ stateMap.put(SupplicantState.FOUR_WAY_HANDSHAKE, DetailedState.AUTHENTICATING);
+ stateMap.put(SupplicantState.GROUP_HANDSHAKE, DetailedState.AUTHENTICATING);
+ stateMap.put(SupplicantState.COMPLETED, DetailedState.OBTAINING_IPADDR);
+ stateMap.put(SupplicantState.DORMANT, DetailedState.DISCONNECTED);
+ stateMap.put(SupplicantState.UNINITIALIZED, DetailedState.IDLE);
+ stateMap.put(SupplicantState.INVALID, DetailedState.FAILED);
+ }
+
+ private SupplicantState mSupplicantState;
+ private String mBSSID;
+ private String mSSID;
+ private int mNetworkId;
+ private boolean mHiddenSSID;
+ /** Received Signal Strength Indicator */
+ private int mRssi;
+
+ /** Link speed in Mbps */
+ public static final String LINK_SPEED_UNITS = "Mbps";
+ private int mLinkSpeed;
+
+ private int mIpAddress;
+
+ private String mMacAddress;
+
+ WifiInfo() {
+ mSSID = null;
+ mBSSID = null;
+ mNetworkId = -1;
+ mSupplicantState = SupplicantState.UNINITIALIZED;
+ mRssi = -9999;
+ mLinkSpeed = -1;
+ mIpAddress = 0;
+ mHiddenSSID = false;
+ }
+
+ void setSSID(String SSID) {
+ mSSID = SSID;
+ // network is considered not hidden by default
+ mHiddenSSID = false;
+ }
+
+ /**
+ * Returns the service set identifier (SSID) of the current 802.11 network.
+ * If the SSID is an ASCII string, it will be returned surrounded by double
+ * quotation marks.Otherwise, it is returned as a string of hex digits. The
+ * SSID may be {@code null} if there is no network currently connected.
+ * @return the SSID
+ */
+ public String getSSID() {
+ return mSSID;
+ }
+
+ void setBSSID(String BSSID) {
+ mBSSID = BSSID;
+ }
+
+ /**
+ * Return the basic service set identifier (BSSID) of the current access point.
+ * The BSSID may be {@code null} if there is no network currently connected.
+ * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
+ */
+ public String getBSSID() {
+ return mBSSID;
+ }
+
+ /**
+ * Returns the received signal strength indicator of the current 802.11
+ * network.
+ * <p><strong>This is not normalized, but should be!</strong></p>
+ * @return the RSSI, in the range ??? to ???
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ void setRssi(int rssi) {
+ mRssi = rssi;
+ }
+
+ /**
+ * Returns the current link speed in {@link #LINK_SPEED_UNITS}.
+ * @return the link speed.
+ * @see #LINK_SPEED_UNITS
+ */
+ public int getLinkSpeed() {
+ return mLinkSpeed;
+ }
+
+ void setLinkSpeed(int linkSpeed) {
+ this.mLinkSpeed = linkSpeed;
+ }
+
+ /**
+ * Record the MAC address of the WLAN interface
+ * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
+ */
+ void setMacAddress(String macAddress) {
+ this.mMacAddress = macAddress;
+ }
+
+ public String getMacAddress() {
+ return mMacAddress;
+ }
+
+ void setNetworkId(int id) {
+ mNetworkId = id;
+ }
+
+ /**
+ * Each configured network has a unique small integer ID, used to identify
+ * the network when performing operations on the supplicant. This method
+ * returns the ID for the currently connected network.
+ * @return the network ID, or -1 if there is no currently connected network
+ */
+ public int getNetworkId() {
+ return mNetworkId;
+ }
+
+ /**
+ * Return the detailed state of the supplicant's negotiation with an
+ * access point, in the form of a {@link SupplicantState SupplicantState} object.
+ * @return the current {@link SupplicantState SupplicantState}
+ */
+ public SupplicantState getSupplicantState() {
+ return mSupplicantState;
+ }
+
+ void setSupplicantState(SupplicantState state) {
+ mSupplicantState = state;
+ }
+
+ void setIpAddress(int address) {
+ mIpAddress = address;
+ }
+
+ public int getIpAddress() {
+ return mIpAddress;
+ }
+
+ /**
+ * @return {@code true} if this network does not broadcast its SSID, so an
+ * SSID-specific probe request must be used for scans.
+ */
+ public boolean getHiddenSSID() {
+ return mHiddenSSID;
+ }
+
+ /** {@hide} */
+ public void setHiddenSSID(boolean hiddenSSID) {
+ mHiddenSSID = hiddenSSID;
+ }
+
+ /**
+ * Map a supplicant state into a fine-grained network connectivity state.
+ * @param suppState the supplicant state
+ * @return the corresponding {@link DetailedState}
+ */
+ public static DetailedState getDetailedStateOf(SupplicantState suppState) {
+ return stateMap.get(suppState);
+ }
+
+ /**
+ * Set the <code>SupplicantState</code> from the string name
+ * of the state.
+ * @param stateName the name of the state, as a <code>String</code> returned
+ * in an event sent by {@code wpa_supplicant}.
+ */
+ void setSupplicantState(String stateName) {
+ mSupplicantState = valueOf(stateName);
+ }
+
+ static SupplicantState valueOf(String stateName) {
+ if ("4WAY_HANDSHAKE".equalsIgnoreCase(stateName))
+ return SupplicantState.FOUR_WAY_HANDSHAKE;
+ else {
+ try {
+ return SupplicantState.valueOf(stateName.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return SupplicantState.INVALID;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String none = "<none>";
+
+ sb.append("SSID: ").append(mSSID == null ? none : mSSID).
+ append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
+ append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
+ append(", Supplicant state: ").
+ append(mSupplicantState == null ? none : mSupplicantState).
+ append(", RSSI: ").append(mRssi).
+ append(", Link speed: ").append(mLinkSpeed).
+ append(", Net ID: ").append(mNetworkId);
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mNetworkId);
+ dest.writeInt(mRssi);
+ dest.writeInt(mLinkSpeed);
+ dest.writeInt(mIpAddress);
+ dest.writeString(getSSID());
+ dest.writeString(mBSSID);
+ dest.writeString(mMacAddress);
+ mSupplicantState.writeToParcel(dest, flags);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiInfo> CREATOR =
+ new Creator<WifiInfo>() {
+ public WifiInfo createFromParcel(Parcel in) {
+ WifiInfo info = new WifiInfo();
+ info.setNetworkId(in.readInt());
+ info.setRssi(in.readInt());
+ info.setLinkSpeed(in.readInt());
+ info.setIpAddress(in.readInt());
+ info.setSSID(in.readString());
+ info.mBSSID = in.readString();
+ info.mMacAddress = in.readString();
+ info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
+ return info;
+ }
+
+ public WifiInfo[] newArray(int size) {
+ return new WifiInfo[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
new file mode 100644
index 0000000..f569d5b
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.net.DhcpInfo;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.internal.os.RuntimeInit;
+
+import java.util.List;
+
+/**
+ * This class provides the primary API for managing all aspects of Wi-Fi
+ * connectivity. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
+
+ * It deals with several categories of items:
+ * <ul>
+ * <li>The list of configured networks. The list can be viewed and updated,
+ * and attributes of individual entries can be modified.</li>
+ * <li>The currently active Wi-Fi network, if any. Connectivity can be
+ * established or torn down, and dynamic information about the state of
+ * the network can be queried.</li>
+ * <li>Results of access point scans, containing enough information to
+ * make decisions about what access point to connect to.</li>
+ * <li>It defines the names of various Intent actions that are broadcast
+ * upon any sort of change in Wi-Fi state.
+ * </ul>
+ * This is the API to use when performing Wi-Fi specific operations. To
+ * perform operations that pertain to network connectivity at an abstract
+ * level, use {@link android.net.ConnectivityManager}.
+ */
+public class WifiManager {
+
+ // Supplicant error codes:
+ /**
+ * The error code if there was a problem authenticating.
+ */
+ public static final int ERROR_AUTHENTICATING = 1;
+
+ /**
+ * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
+ * enabling, disabling, or unknown. One extra provides this state as an int.
+ * Another extra provides the previous state, if available.
+ *
+ * @see #EXTRA_WIFI_STATE
+ * @see #EXTRA_PREVIOUS_WIFI_STATE
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_STATE_CHANGED_ACTION =
+ "android.net.wifi.WIFI_STATE_CHANGED";
+ /**
+ * The lookup key for an int that indicates whether Wi-Fi is enabled,
+ * disabled, enabling, disabling, or unknown. Retrieve it with
+ * {@link android.content.Intent#getIntExtra(String,int)}.
+ *
+ * @see #WIFI_STATE_DISABLED
+ * @see #WIFI_STATE_DISABLING
+ * @see #WIFI_STATE_ENABLED
+ * @see #WIFI_STATE_ENABLING
+ * @see #WIFI_STATE_UNKNOWN
+ */
+ public static final String EXTRA_WIFI_STATE = "wifi_state";
+ /**
+ * The previous Wi-Fi state.
+ *
+ * @see #EXTRA_WIFI_STATE
+ */
+ public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
+
+ /**
+ * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
+ * it finishes successfully.
+ *
+ * @see #WIFI_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ */
+ public static final int WIFI_STATE_DISABLING = 0;
+ /**
+ * Wi-Fi is disabled.
+ *
+ * @see #WIFI_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ */
+ public static final int WIFI_STATE_DISABLED = 1;
+ /**
+ * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
+ * it finishes successfully.
+ *
+ * @see #WIFI_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ */
+ public static final int WIFI_STATE_ENABLING = 2;
+ /**
+ * Wi-Fi is enabled.
+ *
+ * @see #WIFI_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ */
+ public static final int WIFI_STATE_ENABLED = 3;
+ /**
+ * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
+ * or disabling.
+ *
+ * @see #WIFI_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ */
+ public static final int WIFI_STATE_UNKNOWN = 4;
+
+ /**
+ * Broadcast intent action indicating that a connection to the supplicant has
+ * been established (and it is now possible
+ * to perform Wi-Fi operations) or the connection to the supplicant has been
+ * lost. One extra provides the connection state as a boolean, where {@code true}
+ * means CONNECTED.
+ * @see #EXTRA_SUPPLICANT_CONNECTED
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
+ "android.net.wifi.supplicant.CONNECTION_CHANGE";
+ /**
+ * The lookup key for a boolean that indicates whether a connection to
+ * the supplicant daemon has been gained or lost. {@code true} means
+ * a connection now exists.
+ * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ */
+ public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
+ /**
+ * Broadcast intent action indicating that the state of Wi-Fi connectivity
+ * has changed. One extra provides the new state
+ * in the form of a {@link android.net.NetworkInfo} object. If the new state is
+ * CONNECTED, a second extra may provide the BSSID of the access point,
+ * as a {@code String}.
+ * @see #EXTRA_NETWORK_INFO
+ * @see #EXTRA_BSSID
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
+ /**
+ * The lookup key for a {@link android.net.NetworkInfo} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_NETWORK_INFO = "networkInfo";
+ /**
+ * The lookup key for a String giving the BSSID of the access point to which
+ * we are connected. Only present when the new state is CONNECTED.
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_BSSID = "bssid";
+ /**
+ * Broadcast intent action indicating that the state of establishing a connection to
+ * an access point has changed.One extra provides the new
+ * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
+ * is not generally the most useful thing to look at if you are just interested in
+ * the overall state of connectivity.
+ * @see #EXTRA_NEW_STATE
+ * @see #EXTRA_SUPPLICANT_ERROR
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SUPPLICANT_STATE_CHANGED_ACTION =
+ "android.net.wifi.supplicant.STATE_CHANGE";
+ /**
+ * The lookup key for a {@link SupplicantState} describing the new state
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_NEW_STATE = "newState";
+
+ /**
+ * The lookup key for a {@link SupplicantState} describing the supplicant
+ * error code if any
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String, int)}.
+ * @see #ERROR_AUTHENTICATING
+ */
+ public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
+
+ /**
+ * An access point scan has completed, and results are available from the supplicant.
+ * Call {@link #getScanResults()} to obtain the results.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
+ /**
+ * The RSSI (signal strength) has changed.
+ * @see #EXTRA_NEW_RSSI
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
+ /**
+ * The lookup key for an {@code int} giving the new RSSI in dBm.
+ */
+ public static final String EXTRA_NEW_RSSI = "newRssi";
+
+ /**
+ * The network IDs of the configured networks could have changed.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
+
+ /**
+ * Activity Action: Pick a Wi-Fi network to connect to.
+ * <p>Input: Nothing.
+ * <p>Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
+
+ /** Anything worse than or equal to this will show 0 bars. */
+ private static final int MIN_RSSI = -100;
+
+ /** Anything better than or equal to this will show the max bars. */
+ private static final int MAX_RSSI = -55;
+
+ IWifiManager mService;
+ Handler mHandler;
+
+ /** Don't allow use of default constructor */
+ private WifiManager() {
+ }
+
+ /**
+ * Create a new WifiManager instance.
+ * Applications will almost always want to use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+ * @param service the Binder interface
+ * @param handler target for messages
+ * {@hide} - hide this because it takes in a parameter of type IWifiManager, which
+ * is a system private class.
+ */
+ public WifiManager(IWifiManager service, Handler handler) {
+ mService = service;
+ mHandler = handler;
+ }
+
+ /**
+ * Return a list of all the networks configured in the supplicant.
+ * Not all fields of WifiConfiguration are returned. Only the following
+ * fields are filled in:
+ * <ul>
+ * <li>networkId</li>
+ * <li>SSID</li>
+ * <li>BSSID</li>
+ * <li>priority</li>
+ * <li>allowedProtocols</li>
+ * <li>allowedKeyManagement</li>
+ * <li>allowedAuthAlgorithms</li>
+ * <li>allowedPairwiseCiphers</li>
+ * <li>allowedGroupCiphers</li>
+ * </ul>
+ * @return a list of network configurations in the form of a list
+ * of {@link WifiConfiguration} objects.
+ */
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ try {
+ return mService.getConfiguredNetworks();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Add a new network description to the set of configured networks.
+ * The {@code networkId} field of the supplied configuration object
+ * is ignored.
+ * <p/>
+ * The new network will be marked DISABLED by default. To enable it,
+ * called {@link #enableNetwork}.
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @return the ID of the newly created network description. This is used in
+ * other operations to specified the network to be acted upon.
+ * Returns {@code -1} on failure.
+ */
+ public int addNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return -1;
+ }
+ config.networkId = -1;
+ return addOrUpdateNetwork(config);
+ }
+
+ /**
+ * Update the network description of an existing configured network.
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object. It may
+ * be sparse, so that only the items that are being changed
+ * are non-<code>null</code>. The {@code networkId} field
+ * must be set to the ID of the existing network being updated.
+ * @return Returns the {@code networkId} of the supplied
+ * {@code WifiConfiguration} on success.
+ * <br/>
+ * Returns {@code -1} on failure, including when the {@code networkId}
+ * field of the {@code WifiConfiguration} does not refer to an
+ * existing network.
+ */
+ public int updateNetwork(WifiConfiguration config) {
+ if (config == null || config.networkId < 0) {
+ return -1;
+ }
+ return addOrUpdateNetwork(config);
+ }
+
+ /**
+ * Internal method for doing the RPC that creates a new network description
+ * or updates an existing one.
+ *
+ * @param config The possibly sparse object containing the variables that
+ * are to set or updated in the network description.
+ * @return the ID of the network on success, {@code -1} on failure.
+ */
+ private int addOrUpdateNetwork(WifiConfiguration config) {
+ try {
+ return mService.addOrUpdateNetwork(config);
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Remove the specified network from the list of configured networks.
+ * This may result in the asynchronous delivery of state change
+ * events.
+ * @param netId the integer that identifies the network configuration
+ * to the supplicant
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean removeNetwork(int netId) {
+ try {
+ return mService.removeNetwork(netId);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Allow a previously configured network to be associated with. If
+ * <code>disableOthers</code> is true, then all other configured
+ * networks are disabled, and an attempt to connect to the selected
+ * network is initiated. This may result in the asynchronous delivery
+ * of state change events.
+ * @param netId the ID of the network in the list of configured networks
+ * @param disableOthers if true, disable all other networks. The way to
+ * select a particular network to connect to is specify {@code true}
+ * for this parameter.
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean enableNetwork(int netId, boolean disableOthers) {
+ try {
+ return mService.enableNetwork(netId, disableOthers);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disable a configured network. The specified network will not be
+ * a candidate for associating. This may result in the asynchronous
+ * delivery of state change events.
+ * @param netId the ID of the network as returned by {@link #addNetwork}.
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean disableNetwork(int netId) {
+ try {
+ return mService.disableNetwork(netId);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disassociate from the currently active access point. This may result
+ * in the asynchronous delivery of state change events.
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean disconnect() {
+ try {
+ return mService.disconnect();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Reconnect to the currently active access point, if we are currently
+ * disconnected. This may result in the asynchronous delivery of state
+ * change events.
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean reconnect() {
+ try {
+ return mService.reconnect();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Reconnect to the currently active access point, even if we are already
+ * connected. This may result in the asynchronous delivery of state
+ * change events.
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean reassociate() {
+ try {
+ return mService.reassociate();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check that the supplicant daemon is responding to requests.
+ * @return {@code true} if we were able to communicate with the supplicant and
+ * it returned the expected response to the PING message.
+ */
+ public boolean pingSupplicant() {
+ if (mService == null)
+ return false;
+ try {
+ return mService.pingSupplicant();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Request a scan for access points. Returns immediately. The availability
+ * of the results is made known later by means of an asynchronous event sent
+ * on completion of the scan.
+ * @return {@code true} if the operation succeeded, i.e., the scan was initiated
+ */
+ public boolean startScan() {
+ try {
+ return mService.startScan();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Return dynamic information about the current Wi-Fi connection, if any is active.
+ * @return the Wi-Fi information, contained in {@link WifiInfo}.
+ */
+ public WifiInfo getConnectionInfo() {
+ try {
+ return mService.getConnectionInfo();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Return the results of the latest access point scan.
+ * @return the list of access points found in the most recent scan.
+ */
+ public List<ScanResult> getScanResults() {
+ try {
+ return mService.getScanResults();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Tell the supplicant to persist the current list of configured networks.
+ * <p>
+ * Note: It is possible for this method to change the network IDs of
+ * existing networks. You should assume the network IDs can be different
+ * after calling this method.
+ *
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean saveConfiguration() {
+ try {
+ return mService.saveConfiguration();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Return the DHCP-assigned addresses from the last successful DHCP request,
+ * if any.
+ * @return the DHCP information
+ */
+ public DhcpInfo getDhcpInfo() {
+ try {
+ return mService.getDhcpInfo();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Enable or disable Wi-Fi.
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ * @return {@code true} if the operation succeeds (or if the existing state
+ * is the same as the requested state).
+ */
+ public boolean setWifiEnabled(boolean enabled) {
+ try {
+ return mService.setWifiEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the Wi-Fi enabled state.
+ * @return One of {@link #WIFI_STATE_DISABLED},
+ * {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
+ * {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
+ * @see #isWifiEnabled()
+ */
+ public int getWifiState() {
+ try {
+ return mService.getWifiState();
+ } catch (RemoteException e) {
+ return WIFI_STATE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Return whether Wi-Fi is enabled or disabled.
+ * @return {@code true} if Wi-Fi is enabled
+ * @see #getWifiState()
+ */
+ public boolean isWifiEnabled() {
+ return getWifiState() == WIFI_STATE_ENABLED;
+ }
+
+ /**
+ * Calculates the level of the signal. This should be used any time a signal
+ * is being shown.
+ *
+ * @param rssi The power of the signal measured in RSSI.
+ * @param numLevels The number of levels to consider in the calculated
+ * level.
+ * @return A level of the signal, given in the range of 0 to numLevels-1
+ * (both inclusive).
+ */
+ public static int calculateSignalLevel(int rssi, int numLevels) {
+ if (rssi <= MIN_RSSI) {
+ return 0;
+ } else if (rssi >= MAX_RSSI) {
+ return numLevels - 1;
+ } else {
+ int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
+ return (rssi - MIN_RSSI) / partitionSize;
+ }
+ }
+
+ /**
+ * Compares two signal strengths.
+ *
+ * @param rssiA The power of the first signal measured in RSSI.
+ * @param rssiB The power of the second signal measured in RSSI.
+ * @return Returns <0 if the first signal is weaker than the second signal,
+ * 0 if the two signals have the same strength, and >0 if the first
+ * signal is stronger than the second signal.
+ */
+ public static int compareSignalLevel(int rssiA, int rssiB) {
+ return rssiA - rssiB;
+ }
+
+ /**
+ * Allows an application to keep the Wi-Fi radio awake.
+ * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
+ * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
+ * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
+ * WifiLocks are held in any application.
+ *
+ * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
+ * could function over a mobile network, if available. A program that needs to download large
+ * files should hold a WifiLock to ensure that the download will complete, but a program whose
+ * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
+ * affecting battery life.
+ *
+ * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
+ * Mode. They simply keep the radio from turning off when Wi-Fi is already on but the device
+ * is idle.
+ */
+ public class WifiLock {
+ private String mTag;
+ private IBinder mBinder;
+ private int mRefCount;
+ private boolean mRefCounted;
+ private boolean mHeld;
+
+ private WifiLock(String tag) {
+ mTag = tag;
+ mBinder = new Binder();
+ mRefCount = 0;
+ mRefCounted = true;
+ mHeld = false;
+ }
+
+ /**
+ * Locks the Wi-Fi radio on until {@link #release} is called.
+ *
+ * If this WifiLock is reference-counted, each call to {@link #acquire} will increment the
+ * reference count, and the radio will remain locked as long as the reference count is
+ * above zero.
+ *
+ * If this WifiLock is not reference-counted, the first call to {@link #acquire} will lock
+ * the radio, but subsequent calls will be ignored. Only one call to {@link #release}
+ * will be required, regardless of the number of times that {@link #acquire} is called.
+ */
+ public void acquire() {
+ synchronized (mBinder) {
+ if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
+ try {
+ mService.acquireWifiLock(mBinder, mTag);
+ } catch (RemoteException e) {
+ }
+ mHeld = true;
+ }
+ }
+ }
+
+ /**
+ * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
+ *
+ * If this WifiLock is reference-counted, each call to {@link #release} will decrement the
+ * reference count, and the radio will be unlocked only when the reference count reaches
+ * zero. If the reference count goes below zero (that is, if {@link #release} is called
+ * a greater number of times than {@link #acquire}), an exception is thrown.
+ *
+ * If this WifiLock is not reference-counted, the first call to {@link #release} (after
+ * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
+ * calls will be ignored.
+ */
+ public void release() {
+ synchronized (mBinder) {
+ if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
+ try {
+ mService.releaseWifiLock(mBinder);
+ } catch (RemoteException e) {
+ }
+ mHeld = false;
+ }
+ if (mRefCount < 0) {
+ throw new RuntimeException("WifiLock under-locked " + mTag);
+ }
+ }
+ }
+
+ /**
+ * Controls whether this is a reference-counted or non-reference-counted WifiLock.
+ *
+ * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
+ * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
+ * has been balanced with a call to {@link #release}. Non-reference-counted WifiLocks
+ * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
+ * radio whenever {@link #release} is called and it is locked.
+ *
+ * @param refCounted true if this WifiLock should keep a reference count
+ */
+ public void setReferenceCounted(boolean refCounted) {
+ mRefCounted = refCounted;
+ }
+
+ /**
+ * Checks whether this WifiLock is currently held.
+ *
+ * @return true if this WifiLock is held, false otherwise
+ */
+ public boolean isHeld() {
+ synchronized (mBinder) {
+ return mHeld;
+ }
+ }
+
+ public String toString() {
+ String s1, s2, s3;
+ synchronized (mBinder) {
+ s1 = Integer.toHexString(System.identityHashCode(this));
+ s2 = mHeld ? "held; " : "";
+ if (mRefCounted) {
+ s3 = "refcounted: refcount = " + mRefCount;
+ } else {
+ s3 = "not refcounted";
+ }
+ return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ synchronized (mBinder) {
+ if (mHeld) {
+ try {
+ mService.releaseWifiLock(mBinder);
+ } catch (RemoteException e) {
+ }
+ RuntimeInit.crash("WifiLock", new Exception(
+ "WifiLock finalized while still held: " + mTag));
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a new WifiLock.
+ *
+ * @param tag a tag for the WifiLock to identify it in debugging messages. This string is
+ * never shown to the user under normal conditions, but should be descriptive
+ * enough to identify your application and the specific WifiLock within it, if it
+ * holds multiple WifiLocks.
+ *
+ * @return a new, unacquired WifiLock with the given tag.
+ *
+ * @see WifiLock
+ */
+ public WifiLock createWifiLock(String tag) {
+ return new WifiLock(tag);
+ }
+
+}
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
new file mode 100644
index 0000000..32d10a1
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.util.Log;
+import android.util.Config;
+import android.net.NetworkInfo;
+import android.net.NetworkStateTracker;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * Listens for events from the wpa_supplicant server, and passes them on
+ * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ *
+ * {@hide}
+ */
+public class WifiMonitor {
+
+ private static final String TAG = "WifiMonitor";
+
+ /** Events we receive from the supplicant daemon */
+
+ private static final int CONNECTED = 1;
+ private static final int DISCONNECTED = 2;
+ private static final int STATE_CHANGE = 3;
+ private static final int SCAN_RESULTS = 4;
+ private static final int LINK_SPEED = 5;
+ private static final int TERMINATING = 6;
+ private static final int DRIVER_STATE = 7;
+ private static final int UNKNOWN = 8;
+
+ /** All events coming from the supplicant start with this prefix */
+ private static final String eventPrefix = "CTRL-EVENT-";
+ private static final int eventPrefixLen = eventPrefix.length();
+
+ /** All WPA events coming from the supplicant start with this prefix */
+ private static final String wpaEventPrefix = "WPA:";
+ private static final String passwordKeyMayBeIncorrectEvent =
+ "pre-shared key may be incorrect";
+
+ /**
+ * Names of events from wpa_supplicant (minus the prefix). In the
+ * format descriptions, * &quot;<code>x</code>&quot;
+ * designates a dynamic value that needs to be parsed out from the event
+ * string
+ */
+ /**
+ * <pre>
+ * CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed
+ * </pre>
+ * <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
+ */
+ private static final String connectedEvent = "CONNECTED";
+ /**
+ * <pre>
+ * CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
+ * </pre>
+ */
+ private static final String disconnectedEvent = "DISCONNECTED";
+ /**
+ * <pre>
+ * CTRL-EVENT-STATE-CHANGE x
+ * </pre>
+ * <code>x</code> is the numerical value of the new state.
+ */
+ private static final String stateChangeEvent = "STATE-CHANGE";
+ /**
+ * <pre>
+ * CTRL-EVENT-SCAN-RESULTS ready
+ * </pre>
+ */
+ private static final String scanResultsEvent = "SCAN-RESULTS";
+
+ /**
+ * <pre>
+ * CTRL-EVENT-LINK-SPEED x Mb/s
+ * </pre>
+ * {@code x} is the link speed in Mb/sec.
+ */
+ private static final String linkSpeedEvent = "LINK-SPEED";
+ /**
+ * <pre>
+ * CTRL-EVENT-TERMINATING - signal x
+ * </pre>
+ * <code>x</code> is the signal that caused termination.
+ */
+ private static final String terminatingEvent = "TERMINATING";
+ /**
+ * <pre>
+ * CTRL-EVENT-DRIVER-STATE state
+ * </pre>
+ * <code>state</code> is either STARTED or STOPPED
+ */
+ private static final String driverStateEvent = "DRIVER-STATE";
+
+ /**
+ * Regex pattern for extracting an Ethernet-style MAC address from a string.
+ * Matches a strings like the following:<pre>
+ * CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]</pre>
+ */
+ private static Pattern mConnectedEventPattern =
+ Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
+
+ private final WifiStateTracker mWifiStateTracker;
+
+ private boolean supplicantConnected;
+
+ private boolean oneShot;
+
+ public WifiMonitor(WifiStateTracker tracker) {
+ mWifiStateTracker = tracker;
+ supplicantConnected = false;
+ oneShot = true;
+ }
+
+ public void startMonitoring() {
+ new MonitorThread().start();
+ }
+
+ public NetworkStateTracker getNetworkStateTracker() {
+ return mWifiStateTracker;
+ }
+
+ class MonitorThread extends Thread {
+ public MonitorThread() {
+ super("WifiMonitor");
+ }
+
+ public void run() {
+
+ //noinspection InfiniteLoopStatement
+ for (;;) {
+ ensureSupplicantConnection();
+
+ String eventStr = WifiNative.waitForEvent();
+
+ if (Config.LOGD) Log.v(TAG, "Event [" + eventStr +"]");
+ if (eventStr == null) {
+ continue;
+ } else if (!eventStr.startsWith(eventPrefix)) {
+ if (eventStr.startsWith(wpaEventPrefix)) {
+ if (0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) {
+ handlePasswordKeyMayBeIncorrect();
+ }
+ }
+ continue;
+ }
+
+ String eventName = eventStr.substring(eventPrefixLen);
+ int nameEnd = eventName.indexOf(' ');
+ if (nameEnd != -1)
+ eventName = eventName.substring(0, nameEnd);
+ if (eventName.length() == 0) {
+ if (Config.LOGD) Log.i(TAG, "Received wpa_supplicant event with empty event name");
+ continue;
+ }
+ /*
+ * Map event name into event enum
+ */
+ int event;
+ if (eventName.equals(connectedEvent))
+ event = CONNECTED;
+ else if (eventName.equals(disconnectedEvent))
+ event = DISCONNECTED;
+ else if (eventName.equals(stateChangeEvent))
+ event = STATE_CHANGE;
+ else if (eventName.equals(scanResultsEvent))
+ event = SCAN_RESULTS;
+ else if (eventName.equals(linkSpeedEvent))
+ event = LINK_SPEED;
+ else if (eventName.equals(terminatingEvent))
+ event = TERMINATING;
+ else if (eventName.equals(driverStateEvent)) {
+ event = DRIVER_STATE;
+ }
+ else
+ event = UNKNOWN;
+
+ String eventData = eventStr;
+ if (event == DRIVER_STATE || event == LINK_SPEED)
+ eventData = eventData.split(" ")[1];
+ else if (event == STATE_CHANGE) {
+ int ind = eventStr.indexOf(" ");
+ if (ind != -1) {
+ eventData = eventStr.substring(ind + 1);
+ }
+ } else {
+ int ind = eventStr.indexOf(" - ");
+ if (ind != -1) {
+ eventData = eventStr.substring(ind + 3);
+ }
+ }
+
+ if (event == STATE_CHANGE) {
+ handleSupplicantStateChange(eventData);
+ } else if (event == DRIVER_STATE) {
+ handleDriverEvent(eventData);
+ } else {
+ handleEvent(event, eventData);
+ // If supplicant is gone, exit the thread
+ if (event == TERMINATING) {
+ break;
+ }
+ }
+ }
+ }
+
+ private void ensureSupplicantConnection() {
+ while (!supplicantConnected) {
+ boolean connected;
+ synchronized (mWifiStateTracker) {
+ connected = WifiNative.connectToSupplicant();
+ }
+ if (!connected) {
+ /*
+ * If we fail to connect on the very first attempt, send a message
+ * indicating a lost connection to the supplicant, so that the
+ * receiver can initialize to the proper state.
+ */
+ if (oneShot) {
+ oneShot = false;
+ mWifiStateTracker.notifySupplicantLost();
+ }
+ nap(5);
+ } else {
+ supplicantConnected = true;
+ oneShot = false;
+ // Send a message indicating that it is now possible to send commands
+ // to the supplicant
+ mWifiStateTracker.notifySupplicantConnection();
+
+ }
+ }
+ }
+
+ private void handlePasswordKeyMayBeIncorrect() {
+ mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+ }
+
+ private void handleDriverEvent(String state) {
+ if (state == null) {
+ return;
+ }
+ if (state.equals("STOPPED")) {
+ mWifiStateTracker.notifyDriverStopped();
+ } else if (state.equals("STARTED")) {
+ mWifiStateTracker.notifyDriverStarted();
+ }
+ }
+
+ /**
+ * Handle all supplicant events except STATE-CHANGE
+ * @param event the event type
+ * @param remainder the rest of the string following the
+ * event name and &quot;&#8195;&#8212;&#8195;&quot;
+ */
+ void handleEvent(int event, String remainder) {
+ switch (event) {
+ case DISCONNECTED:
+ handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
+ break;
+
+ case CONNECTED:
+ handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
+ break;
+
+ case SCAN_RESULTS:
+ mWifiStateTracker.notifyScanResultsAvailable();
+ break;
+
+ case TERMINATING:
+ supplicantConnected = false;
+ mWifiStateTracker.notifySupplicantLost();
+ break;
+
+ case UNKNOWN:
+ break;
+ }
+ }
+
+ /**
+ * Handle the supplicant STATE-CHANGE event
+ * @param dataString New supplicant state string in the format:
+ * id=network-id state=new-state
+ */
+ private void handleSupplicantStateChange(String dataString) {
+ String[] dataTokens = dataString.split(" ");
+
+ int networkId = -1;
+ int newState = -1;
+ for (String token : dataTokens) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) {
+ continue;
+ }
+
+ int value;
+ try {
+ value = Integer.parseInt(nameValue[1]);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "STATE-CHANGE non-integer parameter: " + token);
+ continue;
+ }
+
+ if (nameValue[0].equals("id")) {
+ networkId = value;
+ } else if (nameValue[0].equals("state")) {
+ newState = value;
+ }
+ }
+
+ if (newState == -1) return;
+
+ SupplicantState newSupplicantState = SupplicantState.INVALID;
+ for (SupplicantState state : SupplicantState.values()) {
+ if (state.ordinal() == newState) {
+ newSupplicantState = state;
+ break;
+ }
+ }
+ if (newSupplicantState == SupplicantState.INVALID) {
+ Log.w(TAG, "Invalid supplicant state: " + newState);
+ }
+ mWifiStateTracker.notifyStateChange(networkId, newSupplicantState);
+ }
+ }
+
+ private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
+ String BSSID = null;
+ int networkId = -1;
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ Matcher match = mConnectedEventPattern.matcher(data);
+ if (!match.find()) {
+ if (Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
+ } else {
+ BSSID = match.group(1);
+ try {
+ networkId = Integer.parseInt(match.group(2));
+ } catch (NumberFormatException e) {
+ networkId = -1;
+ }
+ }
+ }
+ mWifiStateTracker.notifyStateChange(newState, BSSID, networkId);
+ }
+
+ /**
+ * Sleep for a period of time.
+ * @param secs the number of seconds to sleep
+ */
+ private static void nap(int secs) {
+ try {
+ Thread.sleep(secs * 1000);
+ } catch (InterruptedException e) {
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
new file mode 100644
index 0000000..78bf52e
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.DhcpInfo;
+
+/**
+ * Native calls for sending requests to the supplicant daemon, and for
+ * receiving asynchronous events. All methods of the form "xxxxCommand()"
+ * must be single-threaded, to avoid requests and responses initiated
+ * from multiple threads from being intermingled.
+ * <p/>
+ * Note that methods whose names are not of the form "xxxCommand()" do
+ * not talk to the supplicant daemon.
+ *
+ * {@hide}
+ */
+public class WifiNative {
+
+ static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
+ static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
+ static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
+
+ public native static String getErrorString(int errorCode);
+
+ public native static boolean loadDriver();
+
+ public native static boolean unloadDriver();
+
+ public native static boolean startSupplicant();
+
+ public native static boolean stopSupplicant();
+
+ public native static boolean connectToSupplicant();
+
+ public native static void closeSupplicantConnection();
+
+ public native static boolean pingCommand();
+
+ public native static boolean scanCommand();
+
+ public native static boolean setScanModeCommand(boolean setActive);
+
+ public native static String listNetworksCommand();
+
+ public native static int addNetworkCommand();
+
+ public native static boolean setNetworkVariableCommand(int netId, String name, String value);
+
+ public native static String getNetworkVariableCommand(int netId, String name);
+
+ public native static boolean removeNetworkCommand(int netId);
+
+ public native static boolean enableNetworkCommand(int netId, boolean disableOthers);
+
+ public native static boolean disableNetworkCommand(int netId);
+
+ public native static boolean reconnectCommand();
+
+ public native static boolean reassociateCommand();
+
+ public native static boolean disconnectCommand();
+
+ public native static String statusCommand();
+
+ public native static int getRssiCommand();
+
+ public native static int getLinkSpeedCommand();
+
+ public native static String getMacAddressCommand();
+
+ public native static String scanResultsCommand();
+
+ public native static boolean startDriverCommand();
+
+ public native static boolean stopDriverCommand();
+
+ public native static boolean setPowerModeCommand(int mode);
+
+ /**
+ * Sets the bluetooth coexistence mode.
+ *
+ * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
+ * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
+ * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
+ * @return Whether the mode was successfully set.
+ */
+ public native static boolean setBluetoothCoexistenceModeCommand(int mode);
+
+ public native static boolean saveConfigCommand();
+
+ public native static boolean reloadConfigCommand();
+
+ public native static boolean setScanResultHandlingCommand(int mode);
+
+ public native static boolean addToBlacklistCommand(String bssid);
+
+ public native static boolean clearBlacklistCommand();
+
+ public native static boolean doDhcpRequest(DhcpInfo results);
+
+ public native static String getDhcpError();
+
+ /**
+ * Wait for the supplicant to send an event, returning the event string.
+ * @return the event string sent by the supplicant.
+ */
+ public native static String waitForEvent();
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
new file mode 100644
index 0000000..24cebac
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -0,0 +1,1710 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.NetworkInfo;
+import android.net.NetworkStateTracker;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemProperties;
+import android.os.Looper;
+import android.provider.Settings;
+import android.provider.Settings.Gservices;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Config;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothHeadset;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.database.ContentObserver;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.net.UnknownHostException;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * {@hide}
+ */
+public class WifiStateTracker extends NetworkStateTracker {
+
+ private static final boolean LOCAL_LOGD = Config.LOGD || false;
+
+ private static final String TAG = "WifiStateTracker";
+
+ // Event log tags (must be in sync with event-log-tags)
+ private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
+ private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
+ private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
+ private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
+ private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
+
+ // Event codes
+ private static final int EVENT_SUPPLICANT_CONNECTION = 1;
+ private static final int EVENT_SUPPLICANT_DISCONNECT = 2;
+ private static final int EVENT_SUPPLICANT_STATE_CHANGED = 3;
+ private static final int EVENT_NETWORK_STATE_CHANGED = 4;
+ private static final int EVENT_SCAN_RESULTS_AVAILABLE = 5;
+ private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
+ private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7;
+ private static final int EVENT_POLL_INTERVAL = 8;
+ private static final int EVENT_DHCP_START = 9;
+ private static final int EVENT_DEFERRED_DISCONNECT = 10;
+ private static final int EVENT_DEFERRED_RECONNECT = 11;
+ /**
+ * The driver is started or stopped. The object will be the state: true for
+ * started, false for stopped.
+ */
+ private static final int EVENT_DRIVER_STATE_CHANGED = 12;
+ private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13;
+
+ /**
+ * Interval in milliseconds between polling for connection
+ * status items that are not sent via asynchronous events.
+ * An example is RSSI (signal strength).
+ */
+ private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
+
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+
+ /**
+ * When a DISCONNECT event is received, we defer handling it to
+ * allow for the possibility that the DISCONNECT is about to
+ * be followed shortly by a CONNECT to the same network we were
+ * just connected to. In such a case, we don't want to report
+ * the network as down, nor do we want to reconfigure the network
+ * interface, etc. If we get a CONNECT event for another network
+ * within the delay window, we immediately handle the pending
+ * disconnect before processing the CONNECT.<p/>
+ * The five second delay is chosen somewhat arbitrarily, but is
+ * meant to cover most of the cases where a DISCONNECT/CONNECT
+ * happens to a network.
+ */
+ private static final int DISCONNECT_DELAY_MSECS = 5000;
+ /**
+ * When the supplicant goes idle after we do an explicit disconnect
+ * following a DHCP failure, we need to kick the supplicant into
+ * trying to associate with access points.
+ */
+ private static final int RECONNECT_DELAY_MSECS = 2000;
+
+ /**
+ * The maximum number of times we will retry a connection to an access point
+ * for which we have failed in acquiring an IP address from DHCP. A value of
+ * N means that we will make N+1 connection attempts in all.
+ * <p>
+ * See {@link Gservices#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+ * value if a Gservices value is not present.
+ */
+ private int mMaxReconnectAttempts = 2;
+
+ private static final int DRIVER_POWER_MODE_AUTO = 0;
+ private static final int DRIVER_POWER_MODE_ACTIVE = 1;
+
+ /**
+ * The current WPA supplicant loop state (used to detect looping behavior):
+ */
+ private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
+
+ /**
+ * The current number of WPA supplicant loop iterations:
+ */
+ private int mNumSupplicantLoopIterations = 0;
+
+ /**
+ * True if we received an event that that a password-key may be incorrect.
+ * If the next incoming supplicant state change event is DISCONNECT,
+ * broadcast a message that we have a possible password error and disable
+ * the network.
+ */
+ private boolean mPasswordKeyMayBeIncorrect = false;
+
+ public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
+ public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
+
+ private WifiMonitor mWifiMonitor;
+ private WifiInfo mWifiInfo;
+ private List<ScanResult> mScanResults;
+ private WifiManager mWM;
+ private boolean mHaveIPAddress;
+ private boolean mObtainingIPAddress;
+ private boolean mExplicitlyDisabled;
+ private boolean mDisconnectPending;
+ private DhcpHandler mDhcpTarget;
+ private DhcpInfo mDhcpInfo;
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private String mLastSsid;
+ private int mLastNetworkId = -1;
+ private boolean mUseStaticIp = false;
+ private int mReconnectCount;
+
+ // Variables relating to the 'available networks' notification
+
+ /**
+ * The icon to show in the 'available networks' notification. This will also
+ * be the ID of the Notification given to the NotificationManager.
+ */
+ private static final int ICON_NETWORKS_AVAILABLE =
+ com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ /**
+ * When a notification is shown, we wait this amount before possibly showing it again.
+ */
+ private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /**
+ * Whether the user has set the setting to show the 'available networks' notification.
+ */
+ private boolean mNotificationEnabled;
+ /**
+ * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+ */
+ private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+ /**
+ * The {@link System#currentTimeMillis()} must be at least this value for us
+ * to show the notification again.
+ */
+ private long mNotificationRepeatTime;
+ /**
+ * The Notification object given to the NotificationManager.
+ */
+ private Notification mNotification;
+ /**
+ * Whether the notification is being shown, as set by us. That is, if the
+ * user cancels the notification, we will not receive the callback so this
+ * will still be true. We only guarantee if this is false, then the
+ * notification is not showing.
+ */
+ private boolean mNotificationShown;
+ /**
+ * The number of continuous scans that must occur before consider the
+ * supplicant in a scanning state. This allows supplicant to associate with
+ * remembered networks that are in the scan results.
+ */
+ private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+ /**
+ * The number of scans since the last network state change. When this
+ * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+ * supplicant to actually be scanning. When the network state changes to
+ * something other than scanning, we reset this to 0.
+ */
+ private int mNumScansSinceNetworkStateChange;
+ /**
+ * Observes the static IP address settings.
+ */
+ private StaticIpSettingObserver mStaticIpSettingObserver;
+
+ private boolean mIsScanModeActive;
+ private boolean mIsScanModeSetDueToAHiddenNetwork;
+ private boolean mDriverIsStopped;
+ private String mInterfaceName;
+ private static String LS = System.getProperty("line.separator");
+
+ private int[] mSavedConfiguration;
+
+ private Runnable mReleaseWakeLockCallback;
+
+ private static String[] sDnsPropNames;
+
+ private Context mContext;
+
+ /**
+ * A structure for supplying information about a supplicant state
+ * change in the STATE_CHANGE event message that comes from the
+ * WifiMonitor
+ * thread.
+ */
+ private static class SupplicantStateChangeResult {
+ SupplicantStateChangeResult(int networkId, SupplicantState state) {
+ this.state = state;
+ this.networkId = networkId;
+ }
+ int networkId;
+ SupplicantState state;
+ }
+
+ /**
+ * A structure for supplying information about a connection in
+ * the CONNECTED event message that comes from the WifiMonitor
+ * thread.
+ */
+ private static class NetworkStateChangeResult {
+ NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ DetailedState state;
+ String BSSID;
+ int networkId;
+ }
+
+ public WifiStateTracker(Context context, Handler target) {
+ super(context, target, ConnectivityManager.TYPE_WIFI);
+
+ mMaxReconnectAttempts = Gservices.getInt(context.getContentResolver(),
+ Gservices.WIFI_MAX_DHCP_RETRY_COUNT, mMaxReconnectAttempts);
+
+ mContext = context;
+ mWifiInfo = new WifiInfo();
+ mWifiMonitor = new WifiMonitor(this);
+ mHaveIPAddress = false;
+ mObtainingIPAddress = false;
+ setExplicitlyDisabled(false);
+ mDisconnectPending = false;
+ mScanResults = new ArrayList<ScanResult>();
+ // Allocate DHCP info object once, and fill it in on each request
+ mDhcpInfo = new DhcpInfo();
+ mIsScanModeSetDueToAHiddenNetwork = false;
+
+ // Setting is in seconds
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.System.getInt(context.getContentResolver(),
+ Settings.System.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+ mNotificationEnabledSettingObserver.register();
+
+ mStaticIpSettingObserver = new StaticIpSettingObserver(new Handler());
+
+ mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+ sDnsPropNames = new String[] {
+ "dhcp." + mInterfaceName + ".dns1",
+ "dhcp." + mInterfaceName + ".dns2"
+ };
+ }
+
+ /**
+ * Helper method: sets the supplicant state and keeps the network
+ * info updated.
+ * @param state the new state
+ */
+ private void setSupplicantState(SupplicantState state) {
+ mWifiInfo.setSupplicantState(state);
+ updateNetworkInfo();
+ }
+
+ public SupplicantState getSupplicantState() {
+ return mWifiInfo.getSupplicantState();
+ }
+
+ /**
+ * Helper method: sets the supplicant state and keeps the network
+ * info updated (string version).
+ * @param stateName the string name of the new state
+ */
+ private void setSupplicantState(String stateName) {
+ mWifiInfo.setSupplicantState(stateName);
+ updateNetworkInfo();
+ }
+
+ /**
+ * Helper method: sets the explicitly-disabled state and keeps the
+ * network info updated.
+ * @param explicitlyDisabled {@code true} if explicitly disabled.
+ */
+ private void setExplicitlyDisabled(boolean explicitlyDisabled) {
+ mExplicitlyDisabled = explicitlyDisabled;
+ updateNetworkInfo();
+ }
+
+ /**
+ * Return the IP addresses of the DNS servers available for the WLAN
+ * network interface.
+ * @return a list of DNS addresses, with no holes.
+ */
+ public String[] getNameServers() {
+ return getNameServerList(sDnsPropNames);
+ }
+
+ /**
+ * Return the system properties name associated with the tcp buffer sizes
+ * for this network.
+ */
+ public String getTcpBufferSizesPropName() {
+ return "net.tcp.buffersize.wifi";
+ }
+
+ public void startMonitoring() {
+ /*
+ * Get a handle on the WifiManager. This cannot be done in our
+ * constructor, because the Wifi service is not yet registered.
+ */
+ mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ public void startEventLoop() {
+ mWifiMonitor.startMonitoring();
+ }
+
+ /**
+ * Wi-Fi is considered available as long as we have a connection to the
+ * supplicant daemon and there is at least one enabled network.
+ * @return {@code true} if Wi-Fi connections are possible
+ */
+ public boolean isAvailable() {
+ /*
+ * TODO: Should we also look at scan results to see whether we're
+ * in range of any access points?
+ */
+ SupplicantState suppState = mWifiInfo.getSupplicantState();
+ return suppState != SupplicantState.UNINITIALIZED &&
+ (suppState != SupplicantState.INACTIVE || mExplicitlyDisabled);
+ }
+
+ /**
+ * Helper method: updates the network info object to keep it in sync with
+ * the Wi-Fi state tracker.
+ */
+ private void updateNetworkInfo() {
+ mNetworkInfo.setIsAvailable(isAvailable());
+ }
+
+ /**
+ * Report whether the Wi-Fi connection is fully configured for data.
+ * @return {@code true} if the {@link SupplicantState} is
+ * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
+ */
+ public boolean isConnectionCompleted() {
+ return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
+ }
+
+ /**
+ * Send the tracker a notification that a user-entered password key
+ * may be incorrect (i.e., caused authentication to fail).
+ */
+ void notifyPasswordKeyMayBeIncorrect() {
+ Message.obtain(this, EVENT_PASSWORD_KEY_MAY_BE_INCORRECT).sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantConnection() {
+ Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that the state of the supplicant
+ * has changed.
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifyStateChange(int networkId, SupplicantState newState) {
+ Message msg = Message.obtain(
+ this, EVENT_SUPPLICANT_STATE_CHANGED,
+ new SupplicantStateChangeResult(networkId, newState));
+ msg.sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that the state of Wifi connectivity
+ * has changed.
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
+ Message msg = Message.obtain(
+ this, EVENT_NETWORK_STATE_CHANGED,
+ new NetworkStateChangeResult(newState, BSSID, networkId));
+ msg.sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that a scan has completed, and results
+ * are available.
+ */
+ void notifyScanResultsAvailable() {
+ // reset the supplicant's handling of scan results to "normal" mode
+ synchronized (this) {
+ WifiNative.setScanResultHandlingCommand(SUPPL_SCAN_HANDLING_NORMAL);
+ }
+ Message.obtain(this, EVENT_SCAN_RESULTS_AVAILABLE).sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that we can no longer communicate with
+ * the supplicant daemon.
+ */
+ void notifySupplicantLost() {
+ Message.obtain(this, EVENT_SUPPLICANT_DISCONNECT).sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that the Wi-Fi driver has been stopped.
+ * The notification is in the form of a synthesized DISCONNECTED event.
+ */
+ void notifyDriverStopped() {
+ setDriverStopped(true);
+ Message msg = Message.obtain(
+ this, EVENT_SUPPLICANT_STATE_CHANGED,
+ new SupplicantStateChangeResult(-1, SupplicantState.DISCONNECTED));
+ msg.sendToTarget();
+
+ // Send a driver stopped message to our handler
+ Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, Boolean.valueOf(false))
+ .sendToTarget();
+ }
+
+ /**
+ * Send the tracker a notification that the Wi-Fi driver has been restarted after
+ * having been stopped.
+ */
+ void notifyDriverStarted() {
+ setDriverStopped(false);
+
+ // Send a driver started message to our handler
+ Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, Boolean.valueOf(true))
+ .sendToTarget();
+ }
+
+ /**
+ * Set the interval timer for polling connection information
+ * that is not delivered asynchronously.
+ */
+ private synchronized void setPollTimer () {
+ if (!hasMessages(EVENT_POLL_INTERVAL)) {
+ sendMessageDelayed(
+ Message.obtain(this, EVENT_POLL_INTERVAL),
+ POLL_STATUS_INTERVAL_MSECS);
+ }
+ }
+
+ private synchronized void setDriverStopped(boolean isStopped) {
+ mDriverIsStopped = isStopped;
+ }
+
+ private synchronized boolean isDriverStopped() {
+ return mDriverIsStopped;
+ }
+
+ @Override
+ public void releaseWakeLock() {
+ if (mReleaseWakeLockCallback != null) {
+ mReleaseWakeLockCallback.run();
+ }
+ }
+
+ public void setReleaseWakeLockCallback(Runnable callback) {
+ mReleaseWakeLockCallback = callback;
+ }
+
+ /**
+ * Tracks the WPA supplicant states to detect "loop" situations.
+ * @param newSupplicantState The new WPA supplicant state.
+ * @return {@code true} if the supplicant loop should be stopped
+ * and {@code false} if it should continue.
+ */
+ private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
+ if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
+ && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
+ if (mSupplicantLoopState != newSupplicantState) {
+ if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
+ ++mNumSupplicantLoopIterations;
+ }
+
+ mSupplicantLoopState = newSupplicantState;
+ }
+ } else if (newSupplicantState == SupplicantState.COMPLETED) {
+ resetSupplicantLoopState();
+ }
+
+ return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
+ }
+
+ /**
+ * Resets the WPA supplicant loop state.
+ */
+ private void resetSupplicantLoopState() {
+ mNumSupplicantLoopIterations = 0;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Intent intent;
+
+ switch (msg.what) {
+ case EVENT_SUPPLICANT_CONNECTION:
+ checkUseStaticIp();
+ /*
+ * DHCP requests are blocking, so run them in a separate thread.
+ */
+ HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
+ dhcpThread.start();
+ mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
+ mIsScanModeActive = true;
+ mLastBssid = null;
+ mLastSsid = null;
+ requestConnectionInfo();
+ SupplicantState supplState = mWifiInfo.getSupplicantState();
+ /**
+ * The MAC address isn't going to change, so just request it
+ * once here.
+ */
+ String macaddr;
+ synchronized (this) {
+ macaddr = WifiNative.getMacAddressCommand();
+ }
+ if (macaddr != null) {
+ mWifiInfo.setMacAddress(macaddr);
+ }
+ if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
+ supplState);
+ // Wi-Fi supplicant connection state changed:
+ // [31- 1] Reserved for future use
+ // [ 0- 0] Connected to supplicant (1) or disconnected from supplicant (0)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
+ /*
+ * The COMPLETED state change from the supplicant may have occurred
+ * in between polling for supplicant availability, in which case
+ * we didn't perform a DHCP request to get an IP address.
+ */
+ if (supplState == SupplicantState.COMPLETED) {
+ mLastBssid = mWifiInfo.getBSSID();
+ mLastSsid = mWifiInfo.getSSID();
+ configureInterface();
+ }
+ intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
+ mContext.sendBroadcast(intent);
+ if (supplState == SupplicantState.COMPLETED && mHaveIPAddress) {
+ setDetailedState(DetailedState.CONNECTED);
+ } else {
+ setDetailedState(WifiInfo.getDetailedStateOf(supplState));
+ }
+ break;
+
+ case EVENT_SUPPLICANT_DISCONNECT:
+ if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
+ // Wi-Fi supplicant connection state changed:
+ // [31- 1] Reserved for future use
+ // [ 0- 0] Connected to supplicant (1) or disconnected from supplicant (0)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 0);
+ synchronized (this) {
+ WifiNative.closeSupplicantConnection();
+ }
+ // When supplicant dies, kill the DHCP thread
+ if (mDhcpTarget != null) {
+ mDhcpTarget.getLooper().quit();
+ mDhcpTarget = null;
+ }
+ mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+ intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
+ mContext.sendBroadcast(intent);
+ setDetailedState(DetailedState.DISCONNECTED);
+ setSupplicantState(SupplicantState.UNINITIALIZED);
+ mHaveIPAddress = false;
+ mObtainingIPAddress = false;
+ break;
+
+ case EVENT_SUPPLICANT_STATE_CHANGED:
+ SupplicantStateChangeResult supplicantStateResult =
+ (SupplicantStateChangeResult) msg.obj;
+ SupplicantState newState = supplicantStateResult.state;
+ SupplicantState currentState = mWifiInfo.getSupplicantState();
+
+ // Wi-Fi supplicant state changed:
+ // [31- 6] Reserved for future use
+ // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
+ int eventLogParam = (newState.ordinal() & 0x3f);
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
+
+ if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
+ + currentState +
+ " ==> " + newState);
+
+ int networkId = supplicantStateResult.networkId;
+
+ /*
+ * Did we get to DISCONNECTED state due to an
+ * authentication (password) failure?
+ */
+ boolean failedToAuthenticate = false;
+ if (newState == SupplicantState.DISCONNECTED) {
+ failedToAuthenticate = mPasswordKeyMayBeIncorrect;
+ }
+ mPasswordKeyMayBeIncorrect = false;
+
+ /*
+ * Keep track of the supplicant state and check if we should
+ * disable the network
+ */
+ boolean disabledNetwork = false;
+ if (isSupplicantLooping(newState)) {
+ if (LOCAL_LOGD) {
+ Log.v(TAG,
+ "Stop WPA supplicant loop and disable network");
+ }
+ disabledNetwork = wifiManagerDisableNetwork(networkId);
+ }
+
+ if (disabledNetwork) {
+ /*
+ * Reset the loop state if we disabled the network
+ */
+ resetSupplicantLoopState();
+ } else if (newState != currentState ||
+ (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
+ setSupplicantState(newState);
+ if (newState == SupplicantState.DORMANT) {
+ handleDisconnectedState(DetailedState.FAILED);
+ sendNetworkStateChangeBroadcast();
+ sendEmptyMessageDelayed(EVENT_DEFERRED_RECONNECT, RECONNECT_DELAY_MSECS);
+ } else if (newState == SupplicantState.DISCONNECTED) {
+ if (isDriverStopped()) {
+ handleDisconnectedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast();
+ } else {
+ scheduleDisconnect();
+ }
+ } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
+ /**
+ * Ignore events that don't change the connectivity state,
+ * such as WPA rekeying operations.
+ */
+ if (!(currentState == SupplicantState.COMPLETED &&
+ (newState == SupplicantState.ASSOCIATING ||
+ newState == SupplicantState.ASSOCIATED ||
+ newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
+ newState == SupplicantState.GROUP_HANDSHAKE))) {
+ setDetailedState(WifiInfo.getDetailedStateOf(newState));
+ }
+ }
+
+ intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState);
+ if (failedToAuthenticate) {
+ wifiManagerDisableNetwork(networkId);
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+ break;
+
+ case EVENT_NETWORK_STATE_CHANGED:
+ /*
+ * Each CONNECT or DISCONNECT generates a pair of events.
+ * One is a supplicant state change event, and the other
+ * is a network state change event. For connects, the
+ * supplicant event always arrives first, followed by
+ * the network state change event. Only the latter event
+ * has the BSSID, which we are interested in capturing.
+ * For disconnects, the order is the opposite -- the
+ * network state change event comes first, followed by
+ * the supplicant state change event.
+ */
+ NetworkStateChangeResult result =
+ (NetworkStateChangeResult) msg.obj;
+
+ // Wi-Fi network state changed:
+ // [31- 6] Reserved for future use
+ // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
+ eventLogParam = (result.state.ordinal() & 0x3f);
+ EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
+
+ if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
+ if (result.state != DetailedState.SCANNING) {
+ /*
+ * Reset the scan count since there was a network state
+ * change. This could be from supplicant trying to associate
+ * with a network.
+ */
+ mNumScansSinceNetworkStateChange = 0;
+ }
+ /*
+ * If the supplicant sent us a CONNECTED event, we don't
+ * want to send out an indication of overall network
+ * connectivity until we have our IP address. If the
+ * supplicant sent us a DISCONNECTED event, we delay
+ * sending a notification in case a reconnection to
+ * the same access point occurs within a short time.
+ */
+ if (result.state == DetailedState.DISCONNECTED) {
+ if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
+ scheduleDisconnect();
+ }
+ break;
+ }
+ requestConnectionStatus(mWifiInfo);
+ if (!(result.state == DetailedState.CONNECTED &&
+ (!mHaveIPAddress || mDisconnectPending))) {
+ setDetailedState(result.state);
+ }
+
+ if (result.state == DetailedState.CONNECTED) {
+ /*
+ * Remove the 'available networks' notification when we
+ * successfully connect to a network.
+ */
+ setNotificationVisible(false, 0, false, 0);
+ boolean wasDisconnectPending = mDisconnectPending;
+ cancelDisconnect();
+ if (!TextUtils.equals(mWifiInfo.getSSID(), mLastSsid)) {
+ /*
+ * The connection is fully configured as far as link-level
+ * connectivity is concerned, but we may still need to obtain
+ * an IP address. But do this only if we are connecting to
+ * a different access point than we were connected to previously.
+ */
+ if (wasDisconnectPending) {
+ DetailedState saveState = getNetworkInfo().getDetailedState();
+ handleDisconnectedState(DetailedState.DISCONNECTED);
+ setDetailedStateInternal(saveState);
+ }
+ configureInterface();
+ }
+ mLastBssid = result.BSSID;
+ mLastSsid = mWifiInfo.getSSID();
+ mLastNetworkId = result.networkId;
+ if (mHaveIPAddress) {
+ setDetailedState(DetailedState.CONNECTED);
+ } else {
+ setDetailedState(DetailedState.OBTAINING_IPADDR);
+ }
+ }
+ sendNetworkStateChangeBroadcast();
+ break;
+
+ case EVENT_SCAN_RESULTS_AVAILABLE:
+
+ mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ sendScanResultsAvailable();
+ /**
+ * On receiving the first scan results after connecting to
+ * the supplicant, switch scan mode over to passive.
+ */
+ if (!mIsScanModeSetDueToAHiddenNetwork) {
+ // This is the only place at the moment where we set
+ // the scan mode NOT due to a hidden network. This is
+ // what the second parameter value (false) stands for.
+ setScanMode(false, false);
+ }
+ break;
+
+ case EVENT_POLL_INTERVAL:
+ if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
+ requestPolledInfo(mWifiInfo);
+ if (mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
+ setPollTimer();
+ }
+ }
+ break;
+
+ case EVENT_DEFERRED_DISCONNECT:
+ if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
+ handleDisconnectedState(DetailedState.DISCONNECTED);
+ }
+ break;
+
+ case EVENT_DEFERRED_RECONNECT:
+ /*
+ * If we've exceeded the maximum number of retries for reconnecting
+ * to a given network, disable the network so that the supplicant
+ * will try some other network, if any is available.
+ * TODO: network ID may have changed since we stored it.
+ */
+ if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
+ if (++mReconnectCount > mMaxReconnectAttempts) {
+ mWM.disableNetwork(mLastNetworkId);
+ }
+ WifiNative.reconnectCommand();
+ }
+ break;
+
+ case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
+ /**
+ * Since this event is sent from another thread, it might have been
+ * sent after we closed our connection to the supplicant in the course
+ * of disabling Wi-Fi. In that case, we should just ignore the event.
+ */
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
+ break;
+ }
+ mReconnectCount = 0;
+ mHaveIPAddress = true;
+ mObtainingIPAddress = false;
+ mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+ mLastSignalLevel = -1; // force update of signal strength
+ if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
+ setDetailedState(DetailedState.CONNECTED);
+ intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ mContext.sendStickyBroadcast(intent);
+ } else {
+ Message.obtain(mTarget, EVENT_CONFIGURATION_CHANGED).sendToTarget();
+ }
+ if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
+ // Wi-Fi interface configuration state changed:
+ // [31- 1] Reserved for future use
+ // [ 0- 0] Interface configuration succeeded (1) or failed (0)
+ EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
+
+ // We've connected successfully, so allow the notification again in the future
+ resetNotificationTimer();
+ break;
+
+ case EVENT_INTERFACE_CONFIGURATION_FAILED:
+ if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
+ // Wi-Fi interface configuration state changed:
+ // [31- 1] Reserved for future use
+ // [ 0- 0] Interface configuration succeeded (1) or failed (0)
+ EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
+
+ mHaveIPAddress = false;
+ mWifiInfo.setIpAddress(0);
+ mObtainingIPAddress = false;
+ WifiNative.disconnectCommand();
+ }
+ break;
+
+ case EVENT_DRIVER_STATE_CHANGED:
+ boolean driverStarted = Boolean.TRUE.equals(msg.obj);
+
+ // Wi-Fi driver state changed:
+ // [31- 1] Reserved for future use
+ // [ 0- 0] Driver start (1) or stopped (0)
+ eventLogParam = driverStarted ? 1 : 0;
+ EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, eventLogParam);
+
+ if (driverStarted) {
+ synchronized (this) {
+ // In some situations, supplicant needs to be kickstarted to
+ // start the background scanning
+ WifiNative.scanCommand();
+ }
+ }
+ break;
+
+ case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
+ mPasswordKeyMayBeIncorrect = true;
+ break;
+ }
+ }
+
+ private boolean wifiManagerDisableNetwork(int networkId) {
+ boolean disabledNetwork = false;
+ if (0 <= networkId) {
+ disabledNetwork = mWM.disableNetwork(networkId);
+ if (LOCAL_LOGD) {
+ if (disabledNetwork) {
+ Log.v(TAG, "Disabled network: " + networkId);
+ }
+ }
+ }
+ if (LOCAL_LOGD) {
+ if (!disabledNetwork) {
+ Log.e(TAG, "Failed to disable network:" +
+ " invalid network id: " + networkId);
+ }
+ }
+ return disabledNetwork;
+ }
+
+ public synchronized void setScanMode(
+ boolean isScanModeActive, boolean setDueToAHiddenNetwork) {
+ mIsScanModeSetDueToAHiddenNetwork = setDueToAHiddenNetwork;
+ if (mIsScanModeActive != isScanModeActive) {
+ WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
+ }
+ }
+
+ private void configureInterface() {
+ setPollTimer();
+ mLastSignalLevel = -1;
+ if (!mUseStaticIp) {
+ if (!mHaveIPAddress && !mObtainingIPAddress) {
+ mObtainingIPAddress = true;
+ mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();
+ }
+ } else {
+ int event;
+ if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
+ mHaveIPAddress = true;
+ event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
+ if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
+ } else {
+ mHaveIPAddress = false;
+ event = EVENT_INTERFACE_CONFIGURATION_FAILED;
+ if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
+ }
+ Message.obtain(this, event).sendToTarget();
+ }
+ }
+
+ /**
+ * Reset our IP state and send out broadcasts following a disconnect.
+ * @param newState the {@code DetailedState} to set. Should be either
+ * {@code DISCONNECTED} or {@code FAILED}.
+ */
+ private void handleDisconnectedState(DetailedState newState) {
+ if (LOCAL_LOGD) Log.d(TAG, "Deconfiguring interface and stopping DHCP");
+ if (mDisconnectPending) {
+ cancelDisconnect();
+ }
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ if (mLastBssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, mLastBssid);
+ mWifiInfo.setBSSID(null);
+ mLastBssid = null;
+ mLastSsid = null;
+ resetInterface();
+ mContext.sendStickyBroadcast(intent);
+ setDetailedState(newState);
+ mDisconnectPending = false;
+ }
+
+ /**
+ * Resets the Wi-Fi interface by clearing any state, resetting any sockets
+ * using the interface, stopping DHCP, and disabling the interface.
+ */
+ public void resetInterface() {
+ mHaveIPAddress = false;
+ mObtainingIPAddress = false;
+ mWifiInfo.setIpAddress(0);
+
+ /*
+ * Reset connection depends on both the interface and the IP assigned,
+ * so it should be done before any chance of the IP being lost.
+ */
+ NetworkUtils.resetConnections(mInterfaceName);
+
+ // Stop DHCP
+ if (mDhcpTarget != null) {
+ mDhcpTarget.setCancelCallback(true);
+ }
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Could not stop DHCP");
+ }
+
+ NetworkUtils.disableInterface(mInterfaceName);
+ }
+
+ /**
+ * The supplicant is reporting that we are disconnected from the current
+ * access point. Often, however, a disconnect will be followed very shortly
+ * by a reconnect to the same access point. Therefore, we delay resetting
+ * the connection's IP state for a bit.
+ */
+ private void scheduleDisconnect() {
+ mDisconnectPending = true;
+ if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
+ sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
+ }
+ }
+
+ private void cancelDisconnect() {
+ mDisconnectPending = false;
+ removeMessages(EVENT_DEFERRED_DISCONNECT);
+ }
+
+ public DhcpInfo getDhcpInfo() {
+ return mDhcpInfo;
+ }
+
+ public synchronized List<ScanResult> getScanResultsList() {
+ return mScanResults;
+ }
+
+ public synchronized void setScanResultsList(List<ScanResult> scanList) {
+ mScanResults = scanList;
+ }
+
+ /**
+ * Get status information for the current connection, if any.
+ * @return a {@link WifiInfo} object containing information about the current connection
+ */
+ public WifiInfo requestConnectionInfo() {
+ requestConnectionStatus(mWifiInfo);
+ requestPolledInfo(mWifiInfo);
+
+ WifiInfo info = mWifiInfo;
+ if (false) Log.v(TAG, "Status from supplicant: NID=" + info.getNetworkId() +
+ " SSID=" + (info.getSSID() == null ? "<none>" : info.getSSID()) +
+ " BSSID=" + (info.getBSSID() == null ? "<none>" : info.getBSSID()) +
+ " WPA_STATE=" + (info.getSupplicantState() == null ? "<none>" : info.getSupplicantState()) +
+ " RSSI=" + info.getRssi());
+
+ return mWifiInfo;
+ }
+
+ private void requestConnectionStatus(WifiInfo info) {
+ String reply;
+ synchronized (this) {
+ reply = WifiNative.statusCommand();
+ }
+ if (reply == null) {
+ return;
+ }
+ /*
+ * Parse the reply from the supplicant to the status command, and update
+ * local state accordingly. The reply is a series of lines of the form
+ * "name=value".
+ */
+ String SSID = null;
+ String BSSID = null;
+ String suppState = null;
+ int netId = -1;
+ String[] lines = reply.split("\n");
+ for (String line : lines) {
+ String[] prop = line.split(" *= *");
+ if (prop.length < 2)
+ continue;
+ String name = prop[0];
+ String value = prop[1];
+ if (name.equalsIgnoreCase("id"))
+ netId = Integer.parseInt(value);
+ else if (name.equalsIgnoreCase("ssid"))
+ SSID = value;
+ else if (name.equalsIgnoreCase("bssid"))
+ BSSID = value;
+ else if (name.equalsIgnoreCase("wpa_state"))
+ suppState = value;
+ }
+ info.setNetworkId(netId);
+ info.setSSID(SSID);
+ info.setBSSID(BSSID);
+ /*
+ * We only set the supplicant state if the previous state was
+ * UNINITIALIZED. This should only happen when we first connect to
+ * the supplicant. Once we're connected, we should always receive
+ * an event upon any state change, but in this case, we want to
+ * make sure any listeners are made aware of the state change.
+ */
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
+ setSupplicantState(suppState);
+ }
+
+ /**
+ * Get the dynamic information that is not reported via events.
+ */
+ private synchronized void requestPolledInfo(WifiInfo info)
+ {
+ int newSignalLevel;
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 100) { // screen out invalid values
+ info.setRssi(newRssi);
+ /**
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not informing others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The "3" below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ info.setRssi(-200);
+ newSignalLevel = mLastSignalLevel;
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ info.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendNetworkStateChangeBroadcast() {
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ if (mWifiInfo.getBSSID() != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, mWifiInfo.getBSSID());
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /**
+ * Disable Wi-Fi connectivity. We do this by going through
+ * all the configured Wi-Fi networks and disabling them, so
+ * that the supplicant will not attempt to connect to any
+ * network. We save the disabled/enabled state of the
+ * original list, so that
+ * {@link android.net.NetworkStateTracker#reconnect()} can
+ * restore it.
+ */
+ public synchronized boolean teardown() {
+
+ // Take down any open network notifications
+ setNotificationVisible(false, 0, false, 0);
+
+ if (!mNetworkInfo.isConnectedOrConnecting())
+ return false;
+
+ List<WifiConfiguration> networks = mWM.getConfiguredNetworks();
+
+ if (networks == null || networks.size() == 0) {
+ return false;
+ }
+
+ /*
+ * Save the network status in a simple array, whose size is equal
+ * to the largest network ID in the configuration list. The list
+ * is always sorted in network ID order, so we only need to look
+ * at the last element to determine the required size of the
+ * array. The network IDs are not necessarily contiguous, so the
+ * saved configuration may have gaps, indicated by placing
+ * nulls in those positions.
+ */
+ int largest = networks.get(networks.size()-1).networkId;
+ mSavedConfiguration = new int[largest+1];
+ for (int i = 0; i < mSavedConfiguration.length; i++)
+ mSavedConfiguration[i] = -1;
+ for (WifiConfiguration conf : networks) {
+ mSavedConfiguration[conf.networkId] = conf.status;
+ }
+ /*
+ * Now disable the networks. We first disable all but the currently
+ * connected network. If we disabled that network before disabling
+ * all the others, the supplicant would try to connect to one of
+ * the other enabled networks, and we don't want that to happen.
+ */
+ int currentNetwork = -1;
+ for (WifiConfiguration conf : networks) {
+ if (conf.status == WifiConfiguration.Status.CURRENT)
+ currentNetwork = conf.networkId;
+ else if (conf.status == WifiConfiguration.Status.ENABLED) {
+ mWM.disableNetwork(conf.networkId);
+ }
+ }
+ if (currentNetwork != -1)
+ mWM.disableNetwork(currentNetwork);
+ setExplicitlyDisabled(true);
+ return true;
+ }
+
+ /**
+ * Reenable Wi-Fi connectivity, by iterating through the list of saved
+ * network statuses, and reenabling each network that had been enabled
+ * before {@link #teardown()} was called. The network that had been
+ * active at the time of the teardown() might not be the one chosen
+ * by the supplicant to connect to after being re-enabled.
+ */
+ public synchronized boolean reconnect() {
+ /*
+ * If there's no saved configuration, there must not have been
+ * a teardown(). Just tell the supplicant to try to reconnect.
+ */
+ setExplicitlyDisabled(false);
+ if (mSavedConfiguration == null) {
+ return mWM.reconnect();
+ }
+
+ List<WifiConfiguration> networks = mWM.getConfiguredNetworks();
+
+ if (networks == null || networks.size() == 0) {
+ return false;
+ }
+
+ /*
+ * We know that the IDs of existing networks never change, even when
+ * networks are added or removed. So there's no danger that we're
+ * enabling a network different than the one that was disabled in
+ * teardown().
+ */
+ for (WifiConfiguration conf : networks) {
+ if (conf.networkId >= 0 && conf.networkId < mSavedConfiguration.length) {
+ int status = mSavedConfiguration[conf.networkId];
+ if (status != conf.status) {
+ if (status == WifiConfiguration.Status.ENABLED || status == WifiConfiguration.Status.CURRENT)
+ mWM.enableNetwork(conf.networkId, false);
+ else
+ // this case shouldn't occur
+ mWM.disableNetwork(conf.networkId);
+ }
+ }
+ }
+ mSavedConfiguration = null;
+ return true;
+ }
+
+ public boolean setRadio(boolean turnOn) {
+ return mWM.setWifiEnabled(turnOn);
+ }
+
+ /**
+ * {@inheritDoc}
+ * There are currently no Wi-Fi-specific features supported.
+ * @param feature the name of the feature
+ * @return {@code -1} indicating failure, always
+ */
+ public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+ return -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ * There are currently no Wi-Fi-specific features supported.
+ * @param feature the name of the feature
+ * @return {@code -1} indicating failure, always
+ */
+ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+ return -1;
+ }
+
+ @Override
+ public void interpretScanResultsAvailable() {
+
+ // If we shouldn't place a notification on available networks, then
+ // don't bother doing any of the following
+ if (!mNotificationEnabled) return;
+
+ NetworkInfo networkInfo = getNetworkInfo();
+
+ State state = networkInfo.getState();
+ if ((state == NetworkInfo.State.DISCONNECTED)
+ || (state == NetworkInfo.State.UNKNOWN)) {
+
+ // Look for an open network
+ List<ScanResult> scanResults = getScanResultsList();
+ if (scanResults != null) {
+ int numOpenNetworks = 0;
+ for (int i = scanResults.size() - 1; i >= 0; i--) {
+ ScanResult scanResult = scanResults.get(i);
+
+ if (TextUtils.isEmpty(scanResult.capabilities)) {
+ numOpenNetworks++;
+ }
+ }
+
+ if (numOpenNetworks > 0) {
+ if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+ /*
+ * We've scanned continuously at least
+ * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+ * probably does not have a remembered network in range,
+ * since otherwise supplicant would have tried to
+ * associate and thus resetting this counter.
+ */
+ setNotificationVisible(true, numOpenNetworks, false, 0);
+ }
+ return;
+ }
+ }
+ }
+
+ // No open networks in range, remove the notification
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * {@hide}
+ */
+ public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
+
+ // Since we use auto cancel on the notification, when the
+ // mNetworksAvailableNotificationShown is true, the notification may
+ // have actually been canceled. However, when it is false we know
+ // for sure that it is not being shown (it will not be shown any other
+ // place than here)
+
+ // If it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown && !force) {
+ return;
+ }
+
+ Message message = null;
+ if (visible) {
+
+ // Not enough time has passed to show the notification again
+ if (System.currentTimeMillis() < mNotificationRepeatTime) {
+ return;
+ }
+
+ if (mNotification == null) {
+ // Cache the Notification mainly so we can remove the
+ // EVENT_NOTIFICATION_CHANGED message with this Notification from
+ // the queue later
+ mNotification = new Notification();
+ mNotification.when = 0;
+ mNotification.icon = ICON_NETWORKS_AVAILABLE;
+ mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+ }
+
+ CharSequence title = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available, numNetworks);
+ CharSequence details = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+ mNotification.tickerText = title;
+ mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+ mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+ message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
+ ICON_NETWORKS_AVAILABLE, mNotification);
+
+ } else {
+
+ // Remove any pending messages to show the notification
+ mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
+
+ message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
+ }
+
+ mTarget.sendMessageDelayed(message, delay);
+
+ mNotificationShown = visible;
+ }
+
+ /**
+ * Clears variables related to tracking whether a notification has been
+ * shown recently.
+ * <p>
+ * After calling this method, the timer that prevents notifications from
+ * being shown too often will be cleared.
+ */
+ private void resetNotificationTimer() {
+ mNotificationRepeatTime = 0;
+ mNumScansSinceNetworkStateChange = 0;
+ }
+
+ public synchronized boolean addToBlacklist(String bssid) {
+ return WifiNative.addToBlacklistCommand(bssid);
+ }
+
+ public synchronized boolean clearBlacklist() {
+ return WifiNative.clearBlacklistCommand();
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(mWifiInfo).append(LS);
+ sb.append("interface ").append(mInterfaceName).append(LS);
+ sb.append(mDhcpInfo).append(LS);
+ sb.append("haveIpAddress=").append(mHaveIPAddress).
+ append(", obtainingIpAddress=").append(mObtainingIPAddress).
+ append(", scanModeActive=").append(mIsScanModeActive).append(LS).
+ append("lastSignalLevel=").append(mLastSignalLevel).
+ append(", explicitlyDisabled=").append(mExplicitlyDisabled);
+ if (mSavedConfiguration != null) {
+ sb.append(LS);
+ sb.append("Saved network configurations:").append(LS);
+ for (int i = 0; i < mSavedConfiguration.length; i++) {
+ int cs = mSavedConfiguration[i];
+ sb.append(" ").append(i).append(": ").append(cs).append(LS);
+ }
+ }
+ return sb.toString();
+ }
+
+ private class DhcpHandler extends Handler {
+
+ private Handler mTarget;
+
+ /**
+ * Whether to skip the DHCP result callback to the target. For example,
+ * this could be set if the network we were requesting an IP for has
+ * since been disconnected.
+ * <p>
+ * Note: There is still a chance where the client's intended DHCP
+ * request not being canceled. For example, we are request for IP on
+ * A, and he queues request for IP on B, and then cancels the request on
+ * B while we're still requesting from A.
+ */
+ private boolean mCancelCallback;
+
+ /**
+ * Instance of the bluetooth headset helper. This needs to be created
+ * early because there is a delay before it actually 'connects', as
+ * noted by its javadoc. If we check before it is connected, it will be
+ * in an error state and we will not disable coexistence.
+ */
+ private BluetoothHeadset mBluetoothHeadset;
+
+ public DhcpHandler(Looper looper, Handler target) {
+ super(looper);
+ mTarget = target;
+
+ mBluetoothHeadset = new BluetoothHeadset(mContext);
+ }
+
+ public void handleMessage(Message msg) {
+ int event;
+
+ switch (msg.what) {
+ case EVENT_DHCP_START:
+
+ boolean modifiedBluetoothCoexistenceMode = false;
+ if (shouldDisableCoexistenceMode()) {
+ /*
+ * There are problems setting the Wi-Fi driver's power
+ * mode to active when bluetooth coexistence mode is
+ * enabled or sense.
+ * <p>
+ * We set Wi-Fi to active mode when
+ * obtaining an IP address because we've found
+ * compatibility issues with some routers with low power
+ * mode.
+ * <p>
+ * In order for this active power mode to properly be set,
+ * we disable coexistence mode until we're done with
+ * obtaining an IP address. One exception is if we
+ * are currently connected to a headset, since disabling
+ * coexistence would interrupt that connection.
+ */
+ modifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ synchronized (WifiStateTracker.this) {
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+ }
+
+ synchronized (WifiStateTracker.this) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
+ }
+ synchronized (this) {
+ // A new request is being made, so assume we will callback
+ mCancelCallback = false;
+ }
+ Log.d(TAG, "DhcpHandler: DHCP request started");
+ if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
+ event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
+ if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
+ } else {
+ event = EVENT_INTERFACE_CONFIGURATION_FAILED;
+ Log.i(TAG, "DhcpHandler: DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ }
+ synchronized (WifiStateTracker.this) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
+ }
+
+ if (modifiedBluetoothCoexistenceMode) {
+ // Set the coexistence mode back to its default value
+ synchronized (WifiStateTracker.this) {
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ }
+ }
+
+ synchronized (this) {
+ if (!mCancelCallback) {
+ Message.obtain(mTarget, event).sendToTarget();
+ }
+ }
+ break;
+ }
+ }
+
+ public synchronized void setCancelCallback(boolean cancelCallback) {
+ mCancelCallback = cancelCallback;
+ }
+
+ /**
+ * Whether to disable coexistence mode while obtaining IP address. This
+ * logic will return true only if the current bluetooth
+ * headset/handsfree state is disconnected. This means if it is in an
+ * error state, we will NOT disable coexistence mode to err on the side
+ * of safety.
+ *
+ * @return Whether to disable coexistence mode.
+ */
+ private boolean shouldDisableCoexistenceMode() {
+ int state = mBluetoothHeadset.getState();
+ return state == BluetoothHeadset.STATE_DISCONNECTED;
+ }
+ }
+
+ private void checkUseStaticIp() {
+ mUseStaticIp = false;
+ final ContentResolver cr = mContext.getContentResolver();
+ try {
+ if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
+ return;
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ return;
+ }
+
+ try {
+ String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
+ if (addr != null) {
+ mDhcpInfo.ipAddress = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
+ if (addr != null) {
+ mDhcpInfo.gateway = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
+ if (addr != null) {
+ mDhcpInfo.netmask = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
+ if (addr != null) {
+ mDhcpInfo.dns1 = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
+ if (addr != null) {
+ mDhcpInfo.dns2 = stringToIpAddr(addr);
+ } else {
+ mDhcpInfo.dns2 = 0;
+ }
+ } catch (UnknownHostException e) {
+ return;
+ }
+ mUseStaticIp = true;
+ }
+
+ private static int stringToIpAddr(String addrString) throws UnknownHostException {
+ try {
+ String[] parts = addrString.split("\\.");
+ if (parts.length != 4) {
+ throw new UnknownHostException(addrString);
+ }
+
+ int a = Integer.parseInt(parts[0]) ;
+ int b = Integer.parseInt(parts[1]) << 8;
+ int c = Integer.parseInt(parts[2]) << 16;
+ int d = Integer.parseInt(parts[3]) << 24;
+
+ return a | b | c | d;
+ } catch (NumberFormatException ex) {
+ throw new UnknownHostException(addrString);
+ }
+ }
+
+ private class StaticIpSettingObserver extends ContentObserver {
+ public StaticIpSettingObserver(Handler handler) {
+ super(handler);
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_USE_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_GATEWAY), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_NETMASK), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS1), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS2), false, this);
+ }
+
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
+ return;
+ }
+
+ boolean wasStaticIp = mUseStaticIp;
+ int oIp, oGw, oMsk, oDns1, oDns2;
+ oIp = oGw = oMsk = oDns1 = oDns2 = 0;
+ if (wasStaticIp) {
+ oIp = mDhcpInfo.ipAddress;
+ oGw = mDhcpInfo.gateway;
+ oMsk = mDhcpInfo.netmask;
+ oDns1 = mDhcpInfo.dns1;
+ oDns2 = mDhcpInfo.dns2;
+ }
+ checkUseStaticIp();
+ boolean changed =
+ (wasStaticIp != mUseStaticIp) ||
+ (wasStaticIp && (
+ oIp != mDhcpInfo.ipAddress ||
+ oGw != mDhcpInfo.gateway ||
+ oMsk != mDhcpInfo.netmask ||
+ oDns1 != mDhcpInfo.dns1 ||
+ oDns2 != mDhcpInfo.dns2));
+
+ if (changed) {
+ resetInterface();
+ configureInterface();
+ if (mUseStaticIp) {
+ mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED).sendToTarget();
+ }
+ }
+ }
+ }
+
+ private class NotificationEnabledSettingObserver extends ContentObserver {
+
+ public NotificationEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.System.getUriFor(Settings.System.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ mNotificationEnabled = getValue();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mNotificationEnabled = getValue();
+ if (!mNotificationEnabled) {
+ // Remove any notification that may be showing
+ setNotificationVisible(false, 0, true, 0);
+ }
+
+ resetNotificationTimer();
+ }
+
+ private boolean getValue() {
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/package.html b/wifi/java/android/net/wifi/package.html
new file mode 100644
index 0000000..530313d
--- /dev/null
+++ b/wifi/java/android/net/wifi/package.html
@@ -0,0 +1,12 @@
+<HTML>
+<BODY>
+Provides classes to manage Wi-Fi functionality on the device.
+<p>The Wi-Fi APIs provide a means by which applications can communicate
+with the lower-level wireless stack that provides Wi-Fi network access. Almost all
+information from the device supplicant is available, including the connected network's
+link speed, IP address, negotiation state, and more, plus information about other
+networks that are available. Some other API features include the ability to
+scan, add, save, terminate and initiate Wi-Fi connections.</p>
+<p>Remember, not all Android devices are guaranteed to have Wi-Fi functionality.</p>
+</BODY>
+</HTML>