summaryrefslogtreecommitdiffstats
path: root/wifi/java/android/net
diff options
context:
space:
mode:
Diffstat (limited to 'wifi/java/android/net')
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/StateChangeResult.java5
-rw-r--r--wifi/java/android/net/wifi/SupplicantState.java4
-rw-r--r--wifi/java/android/net/wifi/SupplicantStateTracker.java16
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java708
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java35
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java24
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java401
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java192
-rw-r--r--wifi/java/android/net/wifi/WifiNative.java607
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java1145
-rw-r--r--wifi/java/android/net/wifi/WifiWatchdogStateMachine.java1095
-rw-r--r--wifi/java/android/net/wifi/WpsInfo.java30
-rw-r--r--wifi/java/android/net/wifi/WpsStateMachine.java206
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pConfig.java12
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pDevice.java153
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java56
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pGroup.java43
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pManager.java627
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java161
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pService.java1446
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java229
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequest.java107
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceResponse.java313
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl19
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java182
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl19
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java277
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl19
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java389
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java110
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java80
-rw-r--r--wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceResponse.java159
33 files changed, 6161 insertions, 2712 deletions
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 61dfebf..6b08074 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -101,7 +101,9 @@ interface IWifiManager
void clearBlacklist();
- Messenger getMessenger();
+ Messenger getWifiServiceMessenger();
+
+ Messenger getWifiStateMachineMessenger();
String getConfigFile();
}
diff --git a/wifi/java/android/net/wifi/StateChangeResult.java b/wifi/java/android/net/wifi/StateChangeResult.java
index 8ab5982..b15c4a6 100644
--- a/wifi/java/android/net/wifi/StateChangeResult.java
+++ b/wifi/java/android/net/wifi/StateChangeResult.java
@@ -23,12 +23,15 @@
* @hide
*/
public class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, SupplicantState state) {
+ StateChangeResult(int networkId, String SSID, String BSSID, SupplicantState state) {
this.state = state;
+ this.SSID = SSID;
this.BSSID = BSSID;
this.networkId = networkId;
}
+
int networkId;
+ String SSID;
String BSSID;
SupplicantState state;
}
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 509b02c..4a2037d 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -171,8 +171,8 @@ public enum SupplicantState implements Parcelable {
}
- /* Supplicant associating or authenticating is considered a handshake state */
- static boolean isHandshakeState(SupplicantState state) {
+ /** Supplicant associating or authenticating is considered a handshake state {@hide} */
+ public static boolean isHandshakeState(SupplicantState state) {
switch(state) {
case AUTHENTICATING:
case ASSOCIATING:
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index cbd284c..6aeac5f 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -39,6 +39,7 @@ class SupplicantStateTracker extends StateMachine {
private static final boolean DBG = false;
private WifiStateMachine mWifiStateMachine;
+ private WifiConfigStore mWifiConfigStore;
private int mAuthenticationFailuresCount = 0;
/* Indicates authentication failure in supplicant broadcast.
* TODO: enhance auth failure reporting to include notification
@@ -62,11 +63,12 @@ class SupplicantStateTracker extends StateMachine {
private State mCompletedState = new CompletedState();
private State mDormantState = new DormantState();
- public SupplicantStateTracker(Context context, WifiStateMachine wsm, Handler target) {
- super(TAG, target.getLooper());
+ public SupplicantStateTracker(Context c, WifiStateMachine wsm, WifiConfigStore wcs, Handler t) {
+ super(TAG, t.getLooper());
- mContext = context;
+ mContext = c;
mWifiStateMachine = wsm;
+ mWifiConfigStore = wcs;
addState(mDefaultState);
addState(mUninitializedState, mDefaultState);
addState(mInactiveState, mDefaultState);
@@ -85,11 +87,11 @@ class SupplicantStateTracker extends StateMachine {
private void handleNetworkConnectionFailure(int netId) {
/* If other networks disabled during connection, enable them */
if (mNetworksDisabledDuringConnect) {
- WifiConfigStore.enableAllNetworks();
+ mWifiConfigStore.enableAllNetworks();
mNetworksDisabledDuringConnect = false;
}
/* Disable failed network */
- WifiConfigStore.disableNetwork(netId, WifiConfiguration.DISABLED_AUTH_FAILURE);
+ mWifiConfigStore.disableNetwork(netId, WifiConfiguration.DISABLED_AUTH_FAILURE);
}
private void transitionOnSupplicantStateChange(StateChangeResult stateChangeResult) {
@@ -173,7 +175,7 @@ class SupplicantStateTracker extends StateMachine {
case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
transitionTo(mUninitializedState);
break;
- case WifiStateMachine.CMD_CONNECT_NETWORK:
+ case WifiManager.CONNECT_NETWORK:
mNetworksDisabledDuringConnect = true;
break;
default:
@@ -285,7 +287,7 @@ class SupplicantStateTracker extends StateMachine {
/* Reset authentication failure count */
mAuthenticationFailuresCount = 0;
if (mNetworksDisabledDuringConnect) {
- WifiConfigStore.enableAllNetworks();
+ mWifiConfigStore.enableAllNetworks();
mNetworksDisabledDuringConnect = false;
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 26f7199..3c761c8 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -22,8 +22,10 @@ import android.net.DhcpInfoInternal;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
+import android.net.NetworkInfo.DetailedState;
import android.net.ProxyProperties;
import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration.EnterpriseField;
import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.ProxySettings;
@@ -31,6 +33,9 @@ import android.net.wifi.WifiConfiguration.Status;
import android.net.wifi.NetworkUpdateResult;
import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
import android.os.Environment;
+import android.os.Message;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
@@ -50,6 +55,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* This class provides the API to manage configured
@@ -98,12 +104,12 @@ import java.util.List;
*/
class WifiConfigStore {
- private static Context sContext;
+ private Context mContext;
private static final String TAG = "WifiConfigStore";
private static final boolean DBG = false;
/* configured networks with network id as the key */
- private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
+ private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
new HashMap<Integer, WifiConfiguration>();
/* A network id is a unique identifier for a network configured in the
@@ -113,11 +119,11 @@ class WifiConfigStore {
* that is generated from SSID and security type of the network. A mapping
* from the generated unique id to network id of the network is needed to
* map supplicant config to IP configuration. */
- private static HashMap<Integer, Integer> sNetworkIds =
+ private HashMap<Integer, Integer> mNetworkIds =
new HashMap<Integer, Integer>();
/* Tracks the highest priority of configured networks */
- private static int sLastPriority = -1;
+ private int mLastPriority = -1;
private static final String ipConfigFile = Environment.getDataDirectory() +
"/misc/wifi/ipconfig.txt";
@@ -136,13 +142,19 @@ class WifiConfigStore {
private static final String EXCLUSION_LIST_KEY = "exclusionList";
private static final String EOS = "eos";
+ private WifiNative mWifiNative;
+
+ WifiConfigStore(Context c, WifiNative wn) {
+ mContext = c;
+ mWifiNative = wn;
+ }
+
/**
- * Initialize context, fetch the list of configured networks
+ * Fetch the list of configured networks
* and enable all stored networks in supplicant.
*/
- static void initialize(Context context) {
+ void initialize() {
if (DBG) log("Loading config and enabling all networks");
- sContext = context;
loadConfiguredNetworks();
enableAllNetworks();
}
@@ -151,12 +163,10 @@ class WifiConfigStore {
* Fetch the list of currently configured networks
* @return List of networks
*/
- static List<WifiConfiguration> getConfiguredNetworks() {
+ List<WifiConfiguration> getConfiguredNetworks() {
List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
- synchronized (sConfiguredNetworks) {
- for(WifiConfiguration config : sConfiguredNetworks.values()) {
- networks.add(new WifiConfiguration(config));
- }
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ networks.add(new WifiConfiguration(config));
}
return networks;
}
@@ -165,23 +175,21 @@ class WifiConfigStore {
* enable all networks and save config. This will be a no-op if the list
* of configured networks indicates all networks as being enabled
*/
- static void enableAllNetworks() {
+ void enableAllNetworks() {
boolean networkEnabledStateChanged = false;
- synchronized (sConfiguredNetworks) {
- for(WifiConfiguration config : sConfiguredNetworks.values()) {
- if(config != null && config.status == Status.DISABLED) {
- if(WifiNative.enableNetworkCommand(config.networkId, false)) {
- networkEnabledStateChanged = true;
- config.status = Status.ENABLED;
- } else {
- loge("Enable network failed on " + config.networkId);
- }
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ if(config != null && config.status == Status.DISABLED) {
+ if(mWifiNative.enableNetwork(config.networkId, false)) {
+ networkEnabledStateChanged = true;
+ config.status = Status.ENABLED;
+ } else {
+ loge("Enable network failed on " + config.networkId);
}
}
}
if (networkEnabledStateChanged) {
- WifiNative.saveConfigCommand();
+ mWifiNative.saveConfig();
sendConfiguredNetworksChangedBroadcast();
}
}
@@ -198,7 +206,7 @@ class WifiConfigStore {
* @param config The configuration details in WifiConfiguration
* @return the networkId now associated with the specified configuration
*/
- static int selectNetwork(WifiConfiguration config) {
+ int selectNetwork(WifiConfiguration config) {
if (config != null) {
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
int netId = result.getNetworkId();
@@ -223,27 +231,25 @@ class WifiConfigStore {
*
* @param netId network to select for connection
*/
- static void selectNetwork(int netId) {
+ void selectNetwork(int netId) {
// Reset the priority of each network at start or if it goes too high.
- if (sLastPriority == -1 || sLastPriority > 1000000) {
- synchronized (sConfiguredNetworks) {
- for(WifiConfiguration config : sConfiguredNetworks.values()) {
- if (config.networkId != INVALID_NETWORK_ID) {
- config.priority = 0;
- addOrUpdateNetworkNative(config);
- }
+ if (mLastPriority == -1 || mLastPriority > 1000000) {
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ if (config.networkId != INVALID_NETWORK_ID) {
+ config.priority = 0;
+ addOrUpdateNetworkNative(config);
}
}
- sLastPriority = 0;
+ mLastPriority = 0;
}
// Set to the highest priority and save the configuration.
WifiConfiguration config = new WifiConfiguration();
config.networkId = netId;
- config.priority = ++sLastPriority;
+ config.priority = ++mLastPriority;
addOrUpdateNetworkNative(config);
- WifiNative.saveConfigCommand();
+ mWifiNative.saveConfig();
/* Enable the given network while disabling all other networks */
enableNetworkWithoutBroadcast(netId, true);
@@ -256,46 +262,67 @@ class WifiConfigStore {
* Add/update the specified configuration and save config
*
* @param config WifiConfiguration to be saved
+ * @return network update result
*/
- static NetworkUpdateResult saveNetwork(WifiConfiguration config) {
+ NetworkUpdateResult saveNetwork(WifiConfiguration config) {
boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
int netId = result.getNetworkId();
/* enable a new network */
if (newNetwork && netId != INVALID_NETWORK_ID) {
- WifiNative.enableNetworkCommand(netId, false);
- synchronized (sConfiguredNetworks) {
- sConfiguredNetworks.get(netId).status = Status.ENABLED;
- }
+ mWifiNative.enableNetwork(netId, false);
+ mConfiguredNetworks.get(netId).status = Status.ENABLED;
}
- WifiNative.saveConfigCommand();
+ mWifiNative.saveConfig();
sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
return result;
}
+ void updateStatus(int netId, DetailedState state) {
+ if (netId != INVALID_NETWORK_ID) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config == null) return;
+ switch (state) {
+ case CONNECTED:
+ config.status = Status.CURRENT;
+ break;
+ case DISCONNECTED:
+ //If network is already disabled, keep the status
+ if (config.status == Status.CURRENT) {
+ config.status = Status.ENABLED;
+ }
+ break;
+ default:
+ //do nothing, retain the existing state
+ break;
+ }
+ }
+ }
+
/**
* Forget the specified network and save config
*
* @param netId network to forget
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static void forgetNetwork(int netId) {
- if (WifiNative.removeNetworkCommand(netId)) {
- WifiNative.saveConfigCommand();
+ boolean forgetNetwork(int netId) {
+ if (mWifiNative.removeNetwork(netId)) {
+ mWifiNative.saveConfig();
WifiConfiguration target = null;
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null) {
- target = sConfiguredNetworks.remove(netId);
- sNetworkIds.remove(configKey(config));
- }
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null) {
+ target = mConfiguredNetworks.remove(netId);
+ mNetworkIds.remove(configKey(config));
}
if (target != null) {
writeIpAndProxyConfigurations();
sendConfiguredNetworksChangedBroadcast(target, WifiManager.CHANGE_REASON_REMOVED);
}
+ return true;
} else {
loge("Failed to remove network " + netId);
+ return false;
}
}
@@ -306,11 +333,12 @@ class WifiConfigStore {
* state machine
*
* @param config wifi configuration to add/update
+ * @return network Id
*/
- static int addOrUpdateNetwork(WifiConfiguration config) {
+ int addOrUpdateNetwork(WifiConfiguration config) {
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
- sendConfiguredNetworksChangedBroadcast(sConfiguredNetworks.get(result.getNetworkId()),
+ sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()),
result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
@@ -324,17 +352,16 @@ class WifiConfigStore {
* state machine for network removal
*
* @param netId network to be removed
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static boolean removeNetwork(int netId) {
- boolean ret = WifiNative.removeNetworkCommand(netId);
+ boolean removeNetwork(int netId) {
+ boolean ret = mWifiNative.removeNetwork(netId);
WifiConfiguration config = null;
- synchronized (sConfiguredNetworks) {
- if (ret) {
- config = sConfiguredNetworks.get(netId);
- if (config != null) {
- config = sConfiguredNetworks.remove(netId);
- sNetworkIds.remove(configKey(config));
- }
+ if (ret) {
+ config = mConfiguredNetworks.get(netId);
+ if (config != null) {
+ config = mConfiguredNetworks.remove(netId);
+ mNetworkIds.remove(configKey(config));
}
}
if (config != null) {
@@ -350,15 +377,16 @@ class WifiConfigStore {
* state machine for connecting to a network
*
* @param netId network to be enabled
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static boolean enableNetwork(int netId, boolean disableOthers) {
+ boolean enableNetwork(int netId, boolean disableOthers) {
boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
if (disableOthers) {
sendConfiguredNetworksChangedBroadcast();
} else {
WifiConfiguration enabledNetwork = null;
- synchronized(sConfiguredNetworks) {
- enabledNetwork = sConfiguredNetworks.get(netId);
+ synchronized(mConfiguredNetworks) {
+ enabledNetwork = mConfiguredNetworks.get(netId);
}
// check just in case the network was removed by someone else.
if (enabledNetwork != null) {
@@ -369,13 +397,11 @@ class WifiConfigStore {
return ret;
}
- static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
- boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
+ boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
+ boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null) config.status = Status.ENABLED;
- }
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null) config.status = Status.ENABLED;
if (disableOthers) {
markAllNetworksDisabledExcept(netId);
@@ -386,8 +412,9 @@ class WifiConfigStore {
/**
* Disable a network. Note that there is no saveConfig operation.
* @param netId network to be disabled
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static boolean disableNetwork(int netId) {
+ boolean disableNetwork(int netId) {
return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
}
@@ -395,18 +422,17 @@ class WifiConfigStore {
* Disable a network. Note that there is no saveConfig operation.
* @param netId network to be disabled
* @param reason reason code network was disabled
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static boolean disableNetwork(int netId, int reason) {
- boolean ret = WifiNative.disableNetworkCommand(netId);
+ boolean disableNetwork(int netId, int reason) {
+ boolean ret = mWifiNative.disableNetwork(netId);
WifiConfiguration network = null;
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- /* Only change the reason if the network was not previously disabled */
- if (config != null && config.status != Status.DISABLED) {
- config.status = Status.DISABLED;
- config.disableReason = reason;
- network = config;
- }
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ /* Only change the reason if the network was not previously disabled */
+ if (config != null && config.status != Status.DISABLED) {
+ config.status = Status.DISABLED;
+ config.disableReason = reason;
+ network = config;
}
if (network != null) {
sendConfiguredNetworksChangedBroadcast(network,
@@ -417,18 +443,21 @@ class WifiConfigStore {
/**
* Save the configured networks in supplicant to disk
+ * @return {@code true} if it succeeds, {@code false} otherwise
*/
- static boolean saveConfig() {
- return WifiNative.saveConfigCommand();
+ boolean saveConfig() {
+ return mWifiNative.saveConfig();
}
/**
* Start WPS pin method configuration with pin obtained
* from the access point
+ * @param config WPS configuration
+ * @return Wps result containing status and pin
*/
- static WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
+ WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
WpsResult result = new WpsResult();
- if (WifiNative.startWpsWithPinFromAccessPointCommand(config.BSSID, config.pin)) {
+ if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
/* WPS leaves all networks disabled */
markAllNetworksDisabled();
result.status = WpsResult.Status.SUCCESS;
@@ -444,9 +473,9 @@ class WifiConfigStore {
* from the device
* @return WpsResult indicating status and pin
*/
- static WpsResult startWpsWithPinFromDevice(WpsInfo config) {
+ WpsResult startWpsWithPinFromDevice(WpsInfo config) {
WpsResult result = new WpsResult();
- result.pin = WifiNative.startWpsWithPinFromDeviceCommand(config.BSSID);
+ result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
/* WPS leaves all networks disabled */
if (!TextUtils.isEmpty(result.pin)) {
markAllNetworksDisabled();
@@ -460,10 +489,12 @@ class WifiConfigStore {
/**
* Start WPS push button configuration
+ * @param config WPS configuration
+ * @return WpsResult indicating status and pin
*/
- static WpsResult startWpsPbc(WpsInfo config) {
+ WpsResult startWpsPbc(WpsInfo config) {
WpsResult result = new WpsResult();
- if (WifiNative.startWpsPbcCommand(config.BSSID)) {
+ if (mWifiNative.startWpsPbc(config.BSSID)) {
/* WPS leaves all networks disabled */
markAllNetworksDisabled();
result.status = WpsResult.Status.SUCCESS;
@@ -476,12 +507,11 @@ class WifiConfigStore {
/**
* Fetch the link properties for a given network id
+ * @return LinkProperties for the given network id
*/
- static LinkProperties getLinkProperties(int netId) {
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null) return new LinkProperties(config.linkProperties);
- }
+ LinkProperties getLinkProperties(int netId) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null) return new LinkProperties(config.linkProperties);
return null;
}
@@ -491,8 +521,9 @@ class WifiConfigStore {
* right now until NetworkUtils is fixed. When we do
* that, we should remove handling DhcpInfo and move
* to using LinkProperties
+ * @return DhcpInfoInternal for the given network id
*/
- static DhcpInfoInternal getIpConfiguration(int netId) {
+ DhcpInfoInternal getIpConfiguration(int netId) {
DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
LinkProperties linkProperties = getLinkProperties(netId);
@@ -518,41 +549,40 @@ class WifiConfigStore {
/**
* set IP configuration for a given network id
*/
- static void setIpConfiguration(int netId, DhcpInfoInternal dhcpInfo) {
+ void setIpConfiguration(int netId, DhcpInfoInternal dhcpInfo) {
LinkProperties linkProperties = dhcpInfo.makeLinkProperties();
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null) {
- // add old proxy details
- if(config.linkProperties != null) {
- linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
- }
- config.linkProperties = linkProperties;
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null) {
+ // add old proxy details
+ if(config.linkProperties != null) {
+ linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
}
+ config.linkProperties = linkProperties;
}
}
/**
* clear IP configuration for a given network id
+ * @param network id
*/
- static void clearIpConfiguration(int netId) {
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null && config.linkProperties != null) {
- // Clear everything except proxy
- ProxyProperties proxy = config.linkProperties.getHttpProxy();
- config.linkProperties.clear();
- config.linkProperties.setHttpProxy(proxy);
- }
+ void clearIpConfiguration(int netId) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null && config.linkProperties != null) {
+ // Clear everything except proxy
+ ProxyProperties proxy = config.linkProperties.getHttpProxy();
+ config.linkProperties.clear();
+ config.linkProperties.setHttpProxy(proxy);
}
}
/**
* Fetch the proxy properties for a given network id
+ * @param network id
+ * @return ProxyProperties for the network id
*/
- static ProxyProperties getProxyProperties(int netId) {
+ ProxyProperties getProxyProperties(int netId) {
LinkProperties linkProperties = getLinkProperties(netId);
if (linkProperties != null) {
return new ProxyProperties(linkProperties.getHttpProxy());
@@ -562,13 +592,13 @@ class WifiConfigStore {
/**
* Return if the specified network is using static IP
+ * @param network id
+ * @return {@code true} if using static ip for netId
*/
- static boolean isUsingStaticIp(int netId) {
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null && config.ipAssignment == IpAssignment.STATIC) {
- return true;
- }
+ boolean isUsingStaticIp(int netId) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+ return true;
}
return false;
}
@@ -579,111 +609,133 @@ class WifiConfigStore {
* @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
* WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
*/
- private static void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
+ private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
int reason) {
Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
- sContext.sendBroadcast(intent);
+ mContext.sendBroadcast(intent);
}
/**
* Should be called when multiple network configuration changes are made.
*/
- private static void sendConfiguredNetworksChangedBroadcast() {
+ private void sendConfiguredNetworksChangedBroadcast() {
Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
- sContext.sendBroadcast(intent);
+ mContext.sendBroadcast(intent);
}
- static void loadConfiguredNetworks() {
- String listStr = WifiNative.listNetworksCommand();
- sLastPriority = 0;
+ void loadConfiguredNetworks() {
+ String listStr = mWifiNative.listNetworks();
+ mLastPriority = 0;
- synchronized (sConfiguredNetworks) {
- sConfiguredNetworks.clear();
- sNetworkIds.clear();
+ mConfiguredNetworks.clear();
+ mNetworkIds.clear();
- if (listStr == null)
- return;
+ if (listStr == null)
+ return;
- String[] lines = listStr.split("\n");
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- continue;
- }
- if (result.length > 3) {
- if (result[3].indexOf("[CURRENT]") != -1)
- config.status = WifiConfiguration.Status.CURRENT;
- else if (result[3].indexOf("[DISABLED]") != -1)
- config.status = WifiConfiguration.Status.DISABLED;
- else
- config.status = WifiConfiguration.Status.ENABLED;
- } else {
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ // network-id | ssid | bssid | flags
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ config.networkId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ continue;
+ }
+ if (result.length > 3) {
+ if (result[3].indexOf("[CURRENT]") != -1)
+ config.status = WifiConfiguration.Status.CURRENT;
+ else if (result[3].indexOf("[DISABLED]") != -1)
+ config.status = WifiConfiguration.Status.DISABLED;
+ else
config.status = WifiConfiguration.Status.ENABLED;
- }
- readNetworkVariables(config);
- if (config.priority > sLastPriority) {
- sLastPriority = config.priority;
- }
- sConfiguredNetworks.put(config.networkId, config);
- sNetworkIds.put(configKey(config), config.networkId);
+ } else {
+ config.status = WifiConfiguration.Status.ENABLED;
}
+ readNetworkVariables(config);
+ if (config.priority > mLastPriority) {
+ mLastPriority = config.priority;
+ }
+ mConfiguredNetworks.put(config.networkId, config);
+ mNetworkIds.put(configKey(config), config.networkId);
}
+
readIpAndProxyConfigurations();
sendConfiguredNetworksChangedBroadcast();
}
- static void updateIpAndProxyFromWpsConfig(int netId, WpsInfo wpsConfig) {
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(netId);
- if (config != null) {
- config.ipAssignment = wpsConfig.ipAssignment;
- config.proxySettings = wpsConfig.proxySettings;
- config.linkProperties = wpsConfig.linkProperties;
- writeIpAndProxyConfigurations();
- }
- }
- }
-
/* Mark all networks except specified netId as disabled */
- private static void markAllNetworksDisabledExcept(int netId) {
- synchronized (sConfiguredNetworks) {
- for(WifiConfiguration config : sConfiguredNetworks.values()) {
- if(config != null && config.networkId != netId) {
- if (config.status != Status.DISABLED) {
- config.status = Status.DISABLED;
- config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
- }
+ private void markAllNetworksDisabledExcept(int netId) {
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ if(config != null && config.networkId != netId) {
+ if (config.status != Status.DISABLED) {
+ config.status = Status.DISABLED;
+ config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
}
}
}
}
- private static void markAllNetworksDisabled() {
+ private void markAllNetworksDisabled() {
markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
}
- private static void writeIpAndProxyConfigurations() {
+ private void writeIpAndProxyConfigurations() {
- DataOutputStream out = null;
- try {
- out = new DataOutputStream(new BufferedOutputStream(
- new FileOutputStream(ipConfigFile)));
+ /* Make a copy */
+ List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ networks.add(new WifiConfiguration(config));
+ }
+
+ DelayedDiskWrite.write(networks);
+ }
+
+ private static class DelayedDiskWrite {
+
+ private static HandlerThread sDiskWriteHandlerThread;
+ private static Handler sDiskWriteHandler;
+ /* Tracks multiple writes on the same thread */
+ private static int sWriteSequence = 0;
+ private static final String TAG = "DelayedDiskWrite";
+
+ static void write (final List<WifiConfiguration> networks) {
+
+ /* Do a delayed write to disk on a seperate handler thread */
+ synchronized (DelayedDiskWrite.class) {
+ if (++sWriteSequence == 1) {
+ sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread");
+ sDiskWriteHandlerThread.start();
+ sDiskWriteHandler = new Handler(sDiskWriteHandlerThread.getLooper());
+ }
+ }
+
+ sDiskWriteHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onWriteCalled(networks);
+ }
+ });
+ }
- out.writeInt(IPCONFIG_FILE_VERSION);
+ private static void onWriteCalled(List<WifiConfiguration> networks) {
- synchronized (sConfiguredNetworks) {
- for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ DataOutputStream out = null;
+ try {
+ out = new DataOutputStream(new BufferedOutputStream(
+ new FileOutputStream(ipConfigFile)));
+
+ out.writeInt(IPCONFIG_FILE_VERSION);
+
+ for(WifiConfiguration config : networks) {
boolean writeToFile = false;
try {
@@ -756,7 +808,7 @@ class WifiConfigStore {
/* Ignore */
break;
default:
- loge("Ignore invalid proxy settings while writing");
+ loge("Ignthisore invalid proxy settings while writing");
break;
}
if (writeToFile) {
@@ -768,20 +820,33 @@ class WifiConfigStore {
}
out.writeUTF(EOS);
}
- }
- } catch (IOException e) {
- loge("Error writing data file");
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (Exception e) {}
+ } catch (IOException e) {
+ loge("Error writing data file");
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (Exception e) {}
+ }
+
+ //Quit if no more writes sent
+ synchronized (DelayedDiskWrite.class) {
+ if (--sWriteSequence == 0) {
+ sDiskWriteHandler.getLooper().quit();
+ sDiskWriteHandler = null;
+ sDiskWriteHandlerThread = null;
+ }
+ }
}
}
+
+ private static void loge(String s) {
+ Log.e(TAG, s);
+ }
}
- private static void readIpAndProxyConfigurations() {
+ private void readIpAndProxyConfigurations() {
DataInputStream in = null;
try {
@@ -854,48 +919,46 @@ class WifiConfigStore {
} while (true);
if (id != -1) {
- synchronized (sConfiguredNetworks) {
- WifiConfiguration config = sConfiguredNetworks.get(
- sNetworkIds.get(id));
+ WifiConfiguration config = mConfiguredNetworks.get(
+ mNetworkIds.get(id));
- if (config == null) {
- loge("configuration found for missing network, ignored");
- } else {
- config.linkProperties = linkProperties;
- switch (ipAssignment) {
- case STATIC:
- case DHCP:
- config.ipAssignment = ipAssignment;
- break;
- case UNASSIGNED:
- //Ignore
- break;
- default:
- loge("Ignore invalid ip assignment while reading");
- break;
- }
+ if (config == null) {
+ loge("configuration found for missing network, ignored");
+ } else {
+ config.linkProperties = linkProperties;
+ switch (ipAssignment) {
+ case STATIC:
+ case DHCP:
+ config.ipAssignment = ipAssignment;
+ break;
+ case UNASSIGNED:
+ //Ignore
+ break;
+ default:
+ loge("Ignore invalid ip assignment while reading");
+ break;
+ }
- switch (proxySettings) {
- case STATIC:
- config.proxySettings = proxySettings;
- ProxyProperties proxyProperties =
- new ProxyProperties(proxyHost, proxyPort, exclusionList);
- linkProperties.setHttpProxy(proxyProperties);
- break;
- case NONE:
- config.proxySettings = proxySettings;
- break;
- case UNASSIGNED:
- //Ignore
- break;
- default:
- loge("Ignore invalid proxy settings while reading");
- break;
- }
+ switch (proxySettings) {
+ case STATIC:
+ config.proxySettings = proxySettings;
+ ProxyProperties proxyProperties =
+ new ProxyProperties(proxyHost, proxyPort, exclusionList);
+ linkProperties.setHttpProxy(proxyProperties);
+ break;
+ case NONE:
+ config.proxySettings = proxySettings;
+ break;
+ case UNASSIGNED:
+ //Ignore
+ break;
+ default:
+ loge("Ignore invalid proxy settings while reading");
+ break;
}
}
} else {
- loge("Missing id while parsing configuration");
+ if (DBG) log("Missing id while parsing configuration");
}
}
} catch (EOFException ignore) {
@@ -910,7 +973,7 @@ class WifiConfigStore {
}
}
- private static NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
+ private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
/*
* If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
* network configuration. Otherwise, the networkId should
@@ -920,12 +983,12 @@ class WifiConfigStore {
boolean newNetwork = false;
// networkId of INVALID_NETWORK_ID means we want to create a new network
if (netId == INVALID_NETWORK_ID) {
- Integer savedNetId = sNetworkIds.get(configKey(config));
+ Integer savedNetId = mNetworkIds.get(configKey(config));
if (savedNetId != null) {
netId = savedNetId;
} else {
newNetwork = true;
- netId = WifiNative.addNetworkCommand();
+ netId = mWifiNative.addNetwork();
if (netId < 0) {
loge("Failed to add a network!");
return new NetworkUpdateResult(INVALID_NETWORK_ID);
@@ -938,7 +1001,7 @@ class WifiConfigStore {
setVariables: {
if (config.SSID != null &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.ssidVarName,
config.SSID)) {
@@ -947,7 +1010,7 @@ class WifiConfigStore {
}
if (config.BSSID != null &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.bssidVarName,
config.BSSID)) {
@@ -958,7 +1021,7 @@ class WifiConfigStore {
String allowedKeyManagementString =
makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
if (config.allowedKeyManagement.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.KeyMgmt.varName,
allowedKeyManagementString)) {
@@ -970,7 +1033,7 @@ class WifiConfigStore {
String allowedProtocolsString =
makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
if (config.allowedProtocols.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.Protocol.varName,
allowedProtocolsString)) {
@@ -982,7 +1045,7 @@ class WifiConfigStore {
String allowedAuthAlgorithmsString =
makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
if (config.allowedAuthAlgorithms.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.AuthAlgorithm.varName,
allowedAuthAlgorithmsString)) {
@@ -995,7 +1058,7 @@ class WifiConfigStore {
makeString(config.allowedPairwiseCiphers,
WifiConfiguration.PairwiseCipher.strings);
if (config.allowedPairwiseCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.PairwiseCipher.varName,
allowedPairwiseCiphersString)) {
@@ -1007,7 +1070,7 @@ class WifiConfigStore {
String allowedGroupCiphersString =
makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
if (config.allowedGroupCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.GroupCipher.varName,
allowedGroupCiphersString)) {
@@ -1019,7 +1082,7 @@ class WifiConfigStore {
// Prevent client screw-up by passing in a WifiConfiguration we gave it
// by preventing "*" as a key.
if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
- !WifiNative.setNetworkVariableCommand(
+ !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.pskVarName,
config.preSharedKey)) {
@@ -1033,7 +1096,7 @@ class WifiConfigStore {
// Prevent client screw-up by passing in a WifiConfiguration we gave it
// by preventing "*" as a key.
if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!WifiNative.setNetworkVariableCommand(
+ if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.wepKeyVarNames[i],
config.wepKeys[i])) {
@@ -1046,7 +1109,7 @@ class WifiConfigStore {
}
if (hasSetKey) {
- if (!WifiNative.setNetworkVariableCommand(
+ if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.wepTxKeyIdxVarName,
Integer.toString(config.wepTxKeyIndex))) {
@@ -1055,7 +1118,7 @@ class WifiConfigStore {
}
}
- if (!WifiNative.setNetworkVariableCommand(
+ if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.priorityVarName,
Integer.toString(config.priority))) {
@@ -1064,7 +1127,7 @@ class WifiConfigStore {
break setVariables;
}
- if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+ if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.hiddenSSIDVarName,
Integer.toString(config.hiddenSSID ? 1 : 0))) {
@@ -1078,10 +1141,18 @@ class WifiConfigStore {
String varName = field.varName();
String value = field.value();
if (value != null) {
- if (field != config.eap) {
+ if (field == config.engine) {
+ /*
+ * If the field is declared as an integer, it must not
+ * be null
+ */
+ if (value.length() == 0) {
+ value = "0";
+ }
+ } else if (field != config.eap) {
value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
}
- if (!WifiNative.setNetworkVariableCommand(
+ if (!mWifiNative.setNetworkVariable(
netId,
varName,
value)) {
@@ -1096,43 +1167,38 @@ class WifiConfigStore {
if (updateFailed) {
if (newNetwork) {
- WifiNative.removeNetworkCommand(netId);
+ mWifiNative.removeNetwork(netId);
loge("Failed to set a network variable, removed network: " + netId);
}
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
/* An update of the network variables requires reading them
- * back from the supplicant to update sConfiguredNetworks.
+ * back from the supplicant to update mConfiguredNetworks.
* This is because some of the variables (SSID, wep keys &
* passphrases) reflect different values when read back than
* when written. For example, wep key is stored as * irrespective
* of the value sent to the supplicant
*/
- WifiConfiguration sConfig;
- synchronized (sConfiguredNetworks) {
- sConfig = sConfiguredNetworks.get(netId);
- }
- if (sConfig == null) {
- sConfig = new WifiConfiguration();
- sConfig.networkId = netId;
+ WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
+ if (currentConfig == null) {
+ currentConfig = new WifiConfiguration();
+ currentConfig.networkId = netId;
}
- readNetworkVariables(sConfig);
+ readNetworkVariables(currentConfig);
- synchronized (sConfiguredNetworks) {
- sConfiguredNetworks.put(netId, sConfig);
- sNetworkIds.put(configKey(sConfig), netId);
- }
+ mConfiguredNetworks.put(netId, currentConfig);
+ mNetworkIds.put(configKey(currentConfig), netId);
- NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(sConfig, config);
+ NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
result.setIsNewNetwork(newNetwork);
result.setNetworkId(netId);
return result;
}
/* Compare current and new configuration and write to file on change */
- private static NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
+ private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
WifiConfiguration currentConfig,
WifiConfiguration newConfig) {
boolean ipChanged = false;
@@ -1232,7 +1298,7 @@ class WifiConfigStore {
return new NetworkUpdateResult(ipChanged, proxyChanged);
}
- private static void addIpSettingsFromConfig(LinkProperties linkProperties,
+ private void addIpSettingsFromConfig(LinkProperties linkProperties,
WifiConfiguration config) {
for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
linkProperties.addLinkAddress(linkAddr);
@@ -1251,7 +1317,7 @@ class WifiConfigStore {
*
* @param config the {@link WifiConfiguration} object to be filled in.
*/
- private static void readNetworkVariables(WifiConfiguration config) {
+ private void readNetworkVariables(WifiConfiguration config) {
int netId = config.networkId;
if (netId < 0)
@@ -1264,21 +1330,21 @@ class WifiConfigStore {
*/
String value;
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
config.SSID = value;
} else {
config.SSID = null;
}
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
if (!TextUtils.isEmpty(value)) {
config.BSSID = value;
} else {
config.BSSID = null;
}
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
config.priority = -1;
if (!TextUtils.isEmpty(value)) {
try {
@@ -1287,7 +1353,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
config.hiddenSSID = false;
if (!TextUtils.isEmpty(value)) {
try {
@@ -1296,7 +1362,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
config.wepTxKeyIndex = -1;
if (!TextUtils.isEmpty(value)) {
try {
@@ -1306,7 +1372,7 @@ class WifiConfigStore {
}
for (int i = 0; i < 4; i++) {
- value = WifiNative.getNetworkVariableCommand(netId,
+ value = mWifiNative.getNetworkVariable(netId,
WifiConfiguration.wepKeyVarNames[i]);
if (!TextUtils.isEmpty(value)) {
config.wepKeys[i] = value;
@@ -1315,14 +1381,14 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+ value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
if (!TextUtils.isEmpty(value)) {
config.preSharedKey = value;
} else {
config.preSharedKey = null;
}
- value = WifiNative.getNetworkVariableCommand(config.networkId,
+ value = mWifiNative.getNetworkVariable(config.networkId,
WifiConfiguration.Protocol.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
@@ -1335,7 +1401,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(config.networkId,
+ value = mWifiNative.getNetworkVariable(config.networkId,
WifiConfiguration.KeyMgmt.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
@@ -1348,7 +1414,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(config.networkId,
+ value = mWifiNative.getNetworkVariable(config.networkId,
WifiConfiguration.AuthAlgorithm.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
@@ -1361,7 +1427,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(config.networkId,
+ value = mWifiNative.getNetworkVariable(config.networkId,
WifiConfiguration.PairwiseCipher.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
@@ -1374,7 +1440,7 @@ class WifiConfigStore {
}
}
- value = WifiNative.getNetworkVariableCommand(config.networkId,
+ value = mWifiNative.getNetworkVariable(config.networkId,
WifiConfiguration.GroupCipher.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
@@ -1389,25 +1455,83 @@ class WifiConfigStore {
for (WifiConfiguration.EnterpriseField field :
config.enterpriseFields) {
- value = WifiNative.getNetworkVariableCommand(netId,
+ value = mWifiNative.getNetworkVariable(netId,
field.varName());
if (!TextUtils.isEmpty(value)) {
- if (field != config.eap) value = removeDoubleQuotes(value);
+ if (field != config.eap && field != config.engine) {
+ value = removeDoubleQuotes(value);
+ }
field.setValue(value);
}
}
+
+ migrateOldEapTlsIfNecessary(config, netId);
+ }
+
+ /**
+ * Migration code for old EAP-TLS configurations. This should only be used
+ * when restoring an old wpa_supplicant.conf or upgrading from a previous
+ * platform version.
+ *
+ * @param config the configuration to be migrated
+ * @param netId the wpa_supplicant's net ID
+ * @param value the old private_key value
+ */
+ private void migrateOldEapTlsIfNecessary(WifiConfiguration config, int netId) {
+ String value = mWifiNative.getNetworkVariable(netId,
+ WifiConfiguration.OLD_PRIVATE_KEY_NAME);
+ /*
+ * If the old configuration value is not present, then there is nothing
+ * to do.
+ */
+ if (TextUtils.isEmpty(value)) {
+ return;
+ } else {
+ // Also ignore it if it's empty quotes.
+ value = removeDoubleQuotes(value);
+ if (TextUtils.isEmpty(value)) {
+ return;
+ }
+ }
+
+ config.engine.setValue(WifiConfiguration.ENGINE_ENABLE);
+ config.engine_id.setValue(convertToQuotedString(WifiConfiguration.KEYSTORE_ENGINE_ID));
+
+ /*
+ * The old key started with the keystore:// URI prefix, but we don't
+ * need that anymore. Trim it off if it exists.
+ */
+ final String keyName;
+ if (value.startsWith(WifiConfiguration.KEYSTORE_URI)) {
+ keyName = new String(value.substring(WifiConfiguration.KEYSTORE_URI.length()));
+ } else {
+ keyName = value;
+ }
+ config.key_id.setValue(convertToQuotedString(keyName));
+
+ // Now tell the wpa_supplicant the new configuration values.
+ final EnterpriseField needsUpdate[] = { config.engine, config.engine_id, config.key_id };
+ for (EnterpriseField field : needsUpdate) {
+ mWifiNative.setNetworkVariable(netId, field.varName(), field.value());
+ }
+
+ // Remove old private_key string so we don't run this again.
+ mWifiNative.setNetworkVariable(netId, WifiConfiguration.OLD_PRIVATE_KEY_NAME,
+ convertToQuotedString(""));
+
+ saveConfig();
}
- private static String removeDoubleQuotes(String string) {
+ private String removeDoubleQuotes(String string) {
if (string.length() <= 2) return "";
return string.substring(1, string.length() - 1);
}
- private static String convertToQuotedString(String string) {
+ private String convertToQuotedString(String string) {
return "\"" + string + "\"";
}
- private static String makeString(BitSet set, String[] strings) {
+ private String makeString(BitSet set, String[] strings) {
StringBuffer buf = new StringBuffer();
int nextSetBit = -1;
@@ -1427,7 +1551,7 @@ class WifiConfigStore {
return buf.toString();
}
- private static int lookupString(String string, String[] strings) {
+ private int lookupString(String string, String[] strings) {
int size = strings.length;
string = string.replace('-', '_');
@@ -1462,10 +1586,10 @@ class WifiConfigStore {
return key.hashCode();
}
- static String dump() {
+ String dump() {
StringBuffer sb = new StringBuffer();
String LS = System.getProperty("line.separator");
- sb.append("sLastPriority ").append(sLastPriority).append(LS);
+ sb.append("mLastPriority ").append(mLastPriority).append(LS);
sb.append("Configured networks ").append(LS);
for (WifiConfiguration conf : getConfiguredNetworks()) {
sb.append(conf).append(LS);
@@ -1473,15 +1597,15 @@ class WifiConfigStore {
return sb.toString();
}
- public static String getConfigFile() {
+ public String getConfigFile() {
return ipConfigFile;
}
- private static void loge(String s) {
+ private void loge(String s) {
Log.e(TAG, s);
}
- private static void log(String s) {
+ private void log(String s) {
Log.d(TAG, s);
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 85a6f27..dfc1b18 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,6 +29,33 @@ import java.util.BitSet;
*/
public class WifiConfiguration implements Parcelable {
+ /**
+ * In old configurations, the "private_key" field was used. However, newer
+ * configurations use the key_id field with the engine_id set to "keystore".
+ * If this field is found in the configuration, the migration code is
+ * triggered.
+ * @hide
+ */
+ public static final String OLD_PRIVATE_KEY_NAME = "private_key";
+
+ /**
+ * String representing the keystore OpenSSL ENGINE's ID.
+ * @hide
+ */
+ public static final String KEYSTORE_ENGINE_ID = "keystore";
+
+ /**
+ * String representing the keystore URI used for wpa_supplicant.
+ * @hide
+ */
+ public static final String KEYSTORE_URI = "keystore://";
+
+ /**
+ * String to set the engine value to when it should be enabled.
+ * @hide
+ */
+ public static final String ENGINE_ENABLE = "1";
+
/** {@hide} */
public static final String ssidVarName = "ssid";
/** {@hide} */
@@ -82,14 +109,18 @@ public class WifiConfiguration implements Parcelable {
/** {@hide} */
public EnterpriseField client_cert = new EnterpriseField("client_cert");
/** {@hide} */
- public EnterpriseField private_key = new EnterpriseField("private_key");
+ public EnterpriseField engine = new EnterpriseField("engine");
+ /** {@hide} */
+ public EnterpriseField engine_id = new EnterpriseField("engine_id");
+ /** {@hide} */
+ public EnterpriseField key_id = new EnterpriseField("key_id");
/** {@hide} */
public EnterpriseField ca_cert = new EnterpriseField("ca_cert");
/** {@hide} */
public EnterpriseField[] enterpriseFields = {
eap, phase2, identity, anonymous_identity, password, client_cert,
- private_key, ca_cert };
+ engine, engine_id, key_id, ca_cert };
/**
* Recognized key management schemes.
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index d5b404e..7bb927b 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -70,7 +70,6 @@ public class WifiInfo implements Parcelable {
private InetAddress mIpAddress;
private String mMacAddress;
- private boolean mExplicitConnect;
WifiInfo() {
mSSID = null;
@@ -80,7 +79,6 @@ public class WifiInfo implements Parcelable {
mRssi = -9999;
mLinkSpeed = -1;
mHiddenSSID = false;
- mExplicitConnect = false;
}
/**
@@ -98,7 +96,6 @@ public class WifiInfo implements Parcelable {
mLinkSpeed = source.mLinkSpeed;
mIpAddress = source.mIpAddress;
mMacAddress = source.mMacAddress;
- mExplicitConnect = source.mExplicitConnect;
}
}
@@ -175,22 +172,6 @@ public class WifiInfo implements Parcelable {
mNetworkId = id;
}
-
- /**
- * @hide
- */
- public boolean isExplicitConnect() {
- return mExplicitConnect;
- }
-
- /**
- * @hide
- */
- public void setExplicitConnect(boolean explicitConnect) {
- this.mExplicitConnect = explicitConnect;
- }
-
-
/**
* Each configured network has a unique small integer ID, used to identify
* the network when performing operations on the supplicant. This method
@@ -279,8 +260,7 @@ public class WifiInfo implements Parcelable {
append(mSupplicantState == null ? none : mSupplicantState).
append(", RSSI: ").append(mRssi).
append(", Link speed: ").append(mLinkSpeed).
- append(", Net ID: ").append(mNetworkId).
- append(", Explicit connect: ").append(mExplicitConnect);
+ append(", Net ID: ").append(mNetworkId);
return sb.toString();
}
@@ -304,7 +284,6 @@ public class WifiInfo implements Parcelable {
dest.writeString(getSSID());
dest.writeString(mBSSID);
dest.writeString(mMacAddress);
- dest.writeByte(mExplicitConnect ? (byte)1 : (byte)0);
mSupplicantState.writeToParcel(dest, flags);
}
@@ -324,7 +303,6 @@ public class WifiInfo implements Parcelable {
info.setSSID(in.readString());
info.mBSSID = in.readString();
info.mMacAddress = in.readString();
- info.mExplicitConnect = in.readByte() == 1 ? true : false;
info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
return info;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 0e19c4c..8aa613b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -23,11 +23,15 @@ import android.net.DhcpInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.WorkSource;
import android.os.Messenger;
+import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
import java.util.List;
@@ -289,24 +293,6 @@ public class WifiManager {
public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
/**
- * Broadcast intent action for reporting errors
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ERROR_ACTION = "android.net.wifi.ERROR";
- /**
- * The type of error being reported
- * @hide
- */
- public static final String EXTRA_ERROR_CODE = "errorCode";
-
- /**
- * Valid error codes
- * @hide
- */
- public static final int WPS_OVERLAP_ERROR = 1;
-
- /**
* Broadcast intent action indicating that the configured networks changed.
* This can be as a result of adding/updating/deleting a network. If
* {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration
@@ -454,6 +440,13 @@ public class WifiManager {
private static final int MAX_RSSI = -55;
/**
+ * Number of RSSI levels used in the framework to initiate
+ * {@link #RSSI_CHANGED_ACTION} broadcast
+ * @hide
+ */
+ public static final int RSSI_LEVELS = 5;
+
+ /**
* Auto settings in the driver. The driver could choose to operate on both
* 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band.
* @hide
@@ -500,9 +493,6 @@ public class WifiManager {
/* Number of currently active WifiLocks and MulticastLocks */
private int mActiveLockCount;
- /* For communication with WifiService */
- private AsyncChannel mAsyncChannel = new AsyncChannel();
-
/**
* Create a new WifiManager instance.
* Applications will almost always want to use
@@ -656,17 +646,6 @@ public class WifiManager {
}
/**
- * Disable a configured network asynchronously. This call is for abnormal network
- * events, and the user may be notified of network change, if they recently attempted
- * to connect to the specified network.
- * @param netId the ID of the network as returned by {@link #addNetwork}.
- * @hide
- */
- public void disableNetwork(int netId, int reason) {
- mAsyncChannel.sendMessage(CMD_DISABLE_NETWORK, netId, reason);
- }
-
- /**
* 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
@@ -1101,37 +1080,258 @@ public class WifiManager {
/* TODO: deprecate synchronous API and open up the following API */
+ private static final int BASE = Protocol.BASE_WIFI_MANAGER;
+
/* Commands to WifiService */
/** @hide */
- public static final int CMD_CONNECT_NETWORK = 1;
+ public static final int CONNECT_NETWORK = BASE + 1;
+ /** @hide */
+ public static final int CONNECT_NETWORK_FAILED = BASE + 2;
+ /** @hide */
+ public static final int CONNECT_NETWORK_SUCCEEDED = BASE + 3;
+
+ /** @hide */
+ public static final int FORGET_NETWORK = BASE + 4;
+ /** @hide */
+ public static final int FORGET_NETWORK_FAILED = BASE + 5;
+ /** @hide */
+ public static final int FORGET_NETWORK_SUCCEEDED = BASE + 6;
+
+ /** @hide */
+ public static final int SAVE_NETWORK = BASE + 7;
+ /** @hide */
+ public static final int SAVE_NETWORK_FAILED = BASE + 8;
+ /** @hide */
+ public static final int SAVE_NETWORK_SUCCEEDED = BASE + 9;
+
/** @hide */
- public static final int CMD_FORGET_NETWORK = 2;
+ public static final int START_WPS = BASE + 10;
/** @hide */
- public static final int CMD_SAVE_NETWORK = 3;
+ public static final int START_WPS_SUCCEEDED = BASE + 11;
/** @hide */
- public static final int CMD_START_WPS = 4;
+ public static final int WPS_FAILED = BASE + 12;
/** @hide */
- public static final int CMD_DISABLE_NETWORK = 5;
+ public static final int WPS_COMPLETED = BASE + 13;
- /* Events from WifiService */
/** @hide */
- public static final int CMD_WPS_COMPLETED = 11;
+ public static final int CANCEL_WPS = BASE + 14;
+ /** @hide */
+ public static final int CANCEL_WPS_FAILED = BASE + 15;
+ /** @hide */
+ public static final int CANCEL_WPS_SUCCEDED = BASE + 16;
+
+ /** @hide */
+ public static final int DISABLE_NETWORK = BASE + 17;
+ /** @hide */
+ public static final int DISABLE_NETWORK_FAILED = BASE + 18;
+ /** @hide */
+ public static final int DISABLE_NETWORK_SUCCEEDED = BASE + 19;
/* For system use only */
/** @hide */
- public static final int CMD_ENABLE_TRAFFIC_STATS_POLL = 21;
+ public static final int ENABLE_TRAFFIC_STATS_POLL = BASE + 21;
/** @hide */
- public static final int CMD_TRAFFIC_STATS_POLL = 22;
+ public static final int TRAFFIC_STATS_POLL = BASE + 22;
+
+
+ /**
+ * Passed with {@link ActionListener#onFailure}.
+ * Indicates that the operation failed due to an internal error.
+ * @hide
+ */
+ public static final int ERROR = 0;
+
+ /**
+ * Passed with {@link ActionListener#onFailure}.
+ * Indicates that the operation is already in progress
+ * @hide
+ */
+ public static final int IN_PROGRESS = 1;
+
+ /**
+ * Passed with {@link ActionListener#onFailure}.
+ * Indicates that the operation failed because the framework is busy and
+ * unable to service the request
+ * @hide
+ */
+ public static final int BUSY = 2;
+
+ /* WPS specific errors */
+ /** WPS overlap detected {@hide} */
+ public static final int WPS_OVERLAP_ERROR = 3;
+ /** WEP on WPS is prohibited {@hide} */
+ public static final int WPS_WEP_PROHIBITED = 4;
+ /** TKIP only prohibited {@hide} */
+ public static final int WPS_TKIP_ONLY_PROHIBITED = 5;
+ /** Authentication failure on WPS {@hide} */
+ public static final int WPS_AUTH_FAILURE = 6;
+ /** WPS timed out {@hide} */
+ public static final int WPS_TIMED_OUT = 7;
+
+ /** Interface for callback invocation when framework channel is lost {@hide} */
+ public interface ChannelListener {
+ /**
+ * The channel to the framework has been disconnected.
+ * Application could try re-initializing using {@link #initialize}
+ */
+ public void onChannelDisconnected();
+ }
+
+ /** Interface for callback invocation on an application action {@hide} */
+ public interface ActionListener {
+ /** The operation succeeded */
+ public void onSuccess();
+ /**
+ * The operation failed
+ * @param reason The reason for failure could be one of
+ * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
+ */
+ public void onFailure(int reason);
+ }
+
+ /** Interface for callback invocation on a start WPS action {@hide} */
+ public interface WpsListener {
+ /** WPS start succeeded */
+ public void onStartSuccess(String pin);
+
+ /** WPS operation completed succesfully */
+ public void onCompletion();
+
+ /**
+ * WPS operation failed
+ * @param reason The reason for failure could be one of
+ * {@link #IN_PROGRESS}, {@link #WPS_OVERLAP_ERROR},{@link #ERROR} or {@link #BUSY}
+ */
+ public void onFailure(int reason);
+ }
/**
- * Initiate an asynchronous channel connection setup
+ * A channel that connects the application to the Wifi framework.
+ * Most operations require a Channel as an argument. An instance of Channel is obtained
+ * by doing a call on {@link #initialize}
+ * @hide
+ */
+ public static class Channel {
+ Channel(Looper looper, ChannelListener l) {
+ mAsyncChannel = new AsyncChannel();
+ mHandler = new WifiHandler(looper);
+ mChannelListener = l;
+ }
+ private ChannelListener mChannelListener;
+ private SparseArray<Object> mListenerMap = new SparseArray<Object>();
+ private Object mListenerMapLock = new Object();
+ private int mListenerKey = 0;
+ private static final int INVALID_KEY = -1;
+
+ AsyncChannel mAsyncChannel;
+ WifiHandler mHandler;
+ class WifiHandler extends Handler {
+ WifiHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ Object listener = removeListener(message.arg2);
+ switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (mChannelListener != null) {
+ mChannelListener.onChannelDisconnected();
+ mChannelListener = null;
+ }
+ break;
+ /* ActionListeners grouped together */
+ case WifiManager.CONNECT_NETWORK_FAILED:
+ case WifiManager.FORGET_NETWORK_FAILED:
+ case WifiManager.SAVE_NETWORK_FAILED:
+ case WifiManager.CANCEL_WPS_FAILED:
+ case WifiManager.DISABLE_NETWORK_FAILED:
+ if (listener != null) {
+ ((ActionListener) listener).onFailure(message.arg1);
+ }
+ break;
+ /* ActionListeners grouped together */
+ case WifiManager.CONNECT_NETWORK_SUCCEEDED:
+ case WifiManager.FORGET_NETWORK_SUCCEEDED:
+ case WifiManager.SAVE_NETWORK_SUCCEEDED:
+ case WifiManager.CANCEL_WPS_SUCCEDED:
+ case WifiManager.DISABLE_NETWORK_SUCCEEDED:
+ if (listener != null) {
+ ((ActionListener) listener).onSuccess();
+ }
+ break;
+ case WifiManager.START_WPS_SUCCEEDED:
+ if (listener != null) {
+ WpsResult result = (WpsResult) message.obj;
+ ((WpsListener) listener).onStartSuccess(result.pin);
+ //Listener needs to stay until completion or failure
+ synchronized(mListenerMapLock) {
+ mListenerMap.put(message.arg2, listener);
+ }
+ }
+ break;
+ case WifiManager.WPS_COMPLETED:
+ if (listener != null) {
+ ((WpsListener) listener).onCompletion();
+ }
+ break;
+ case WifiManager.WPS_FAILED:
+ if (listener != null) {
+ ((WpsListener) listener).onFailure(message.arg1);
+ }
+ break;
+ default:
+ //ignore
+ break;
+ }
+ }
+ }
+
+ int putListener(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ int key;
+ synchronized (mListenerMapLock) {
+ do {
+ key = mListenerKey++;
+ } while (key == INVALID_KEY);
+ mListenerMap.put(key, listener);
+ }
+ return key;
+ }
+
+ Object removeListener(int key) {
+ if (key == INVALID_KEY) return null;
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
+ mListenerMap.remove(key);
+ return listener;
+ }
+ }
+ }
+
+ /**
+ * Registers the application with the Wi-Fi framework. This function
+ * must be the first to be called before any Wi-Fi operations are performed.
+ *
* @param srcContext is the context of the source
- * @param srcHandler is the handler on which the source receives messages
+ * @param srcLooper is the Looper on which the callbacks are receivied
+ * @param listener for callback at loss of framework communication. Can be null.
+ * @return Channel instance that is necessary for performing any further Wi-Fi operations.
+ * A null is returned upon failure to initialize.
* @hide
*/
- public void asyncConnect(Context srcContext, Handler srcHandler) {
- mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
- }
+ public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
+ Messenger messenger = getWifiServiceMessenger();
+ if (messenger == null) return null;
+
+ Channel c = new Channel(srcLooper, listener);
+ if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
+ == AsyncChannel.STATUS_SUCCESSFUL) {
+ return c;
+ } else {
+ return null;
+ }
+ }
/**
* Connect to a network with the given configuration. The network also
@@ -1141,15 +1341,20 @@ public class WifiManager {
* sequence of addNetwork(), enableNetwork(), saveConfiguration() and
* reconnect()
*
+ * @param c is the channel created at {@link #initialize}
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
+ * @param listener for callbacks on success or failure. Can be null.
* @hide
*/
- public void connectNetwork(WifiConfiguration config) {
- if (config == null) {
- return;
- }
- mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);
+ public void connect(Channel c, WifiConfiguration config, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (config == null) throw new IllegalArgumentException("config cannot be null");
+
+ // Use INVALID_NETWORK_ID for arg1 when passing a config object
+ // arg1 is used to pass network id when the network already exists
+ c.mAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+ c.putListener(listener), config);
}
/**
@@ -1158,15 +1363,17 @@ public class WifiManager {
* This function is used instead of a enableNetwork(), saveConfiguration() and
* reconnect()
*
+ * @param c is the channel created at {@link #initialize}
* @param networkId the network id identifiying the network in the
* supplicant configuration list
+ * @param listener for callbacks on success or failure. Can be null.
* @hide
*/
- public void connectNetwork(int networkId) {
- if (networkId < 0) {
- return;
- }
- mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, networkId);
+ public void connect(Channel c, int networkId, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
+
+ c.mAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, c.putListener(listener));
}
/**
@@ -1180,16 +1387,17 @@ public class WifiManager {
* For an existing network, it accomplishes the task of updateNetwork()
* and saveConfiguration()
*
+ * @param c is the channel created at {@link #initialize}
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
+ * @param listener for callbacks on success or failure. Can be null.
* @hide
*/
- public void saveNetwork(WifiConfiguration config) {
- if (config == null) {
- return;
- }
+ public void save(Channel c, WifiConfiguration config, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (config == null) throw new IllegalArgumentException("config cannot be null");
- mAsyncChannel.sendMessage(CMD_SAVE_NETWORK, config);
+ c.mAsyncChannel.sendMessage(SAVE_NETWORK, 0, c.putListener(listener), config);
}
/**
@@ -1198,47 +1406,94 @@ public class WifiManager {
* This function is used instead of a sequence of removeNetwork()
* and saveConfiguration().
*
+ * @param c is the channel created at {@link #initialize}
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
+ * @param listener for callbacks on success or failure. Can be null.
* @hide
*/
- public void forgetNetwork(int netId) {
- if (netId < 0) {
- return;
- }
+ public void forget(Channel c, int netId, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
- mAsyncChannel.sendMessage(CMD_FORGET_NETWORK, netId);
+ c.mAsyncChannel.sendMessage(FORGET_NETWORK, netId, c.putListener(listener));
+ }
+
+ /**
+ * Disable network
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param netId is the network Id
+ * @param listener for callbacks on success or failure. Can be null.
+ * @hide
+ */
+ public void disable(Channel c, int netId, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
+
+ c.mAsyncChannel.sendMessage(DISABLE_NETWORK, netId, c.putListener(listener));
}
/**
* Start Wi-fi Protected Setup
*
+ * @param c is the channel created at {@link #initialize}
* @param config WPS configuration
+ * @param listener for callbacks on success or failure. Can be null.
* @hide
*/
- public void startWps(WpsInfo config) {
- if (config == null) {
- return;
- }
+ public void startWps(Channel c, WpsInfo config, WpsListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (config == null) throw new IllegalArgumentException("config cannot be null");
- mAsyncChannel.sendMessage(CMD_START_WPS, config);
+ c.mAsyncChannel.sendMessage(START_WPS, 0, c.putListener(listener), config);
}
/**
+ * Cancel any ongoing Wi-fi Protected Setup
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on success or failure. Can be null.
+ * @hide
+ */
+ public void cancelWps(Channel c, ActionListener listener) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+
+ c.mAsyncChannel.sendMessage(CANCEL_WPS, 0, c.putListener(listener));
+ }
+
+
+
+ /**
* Get a reference to WifiService handler. This is used by a client to establish
* an AsyncChannel communication with WifiService
*
* @return Messenger pointing to the WifiService handler
* @hide
*/
- public Messenger getMessenger() {
+ public Messenger getWifiServiceMessenger() {
+ try {
+ return mService.getWifiServiceMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get a reference to WifiStateMachine handler.
+ * @return Messenger pointing to the WifiService handler
+ * @hide
+ */
+ public Messenger getWifiStateMachineMessenger() {
try {
- return mService.getMessenger();
+ return mService.getWifiStateMachineMessenger();
} catch (RemoteException e) {
return null;
}
}
+
+
/**
* Returns the file in which IP and proxy configuration data is stored
* @hide
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 2ccc8a2..a447c86 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -20,6 +20,8 @@ import android.net.NetworkInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pProvDiscEvent;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.net.wifi.StateChangeResult;
import android.os.Message;
import android.util.Log;
@@ -28,6 +30,7 @@ import android.util.Log;
import com.android.internal.util.Protocol;
import com.android.internal.util.StateMachine;
+import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@@ -63,7 +66,23 @@ public class WifiMonitor {
"pre-shared key may be incorrect";
/* WPS events */
+ private static final String WPS_SUCCESS_STR = "WPS-SUCCESS";
+
+ /* Format: WPS-FAIL msg=%d [config_error=%d] [reason=%d (%s)] */
+ private static final String WPS_FAIL_STR = "WPS-FAIL";
+ private static final String WPS_FAIL_PATTERN =
+ "WPS-FAIL msg=\\d+(?: config_error=(\\d+))?(?: reason=(\\d+))?";
+
+ /* config error code values for config_error=%d */
+ private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
+ private static final int CONFIG_AUTH_FAILURE = 18;
+
+ /* reason code values for reason=%d */
+ private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
+ private static final int REASON_WEP_PROHIBITED = 2;
+
private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
+ private static final String WPS_TIMEOUT_STR = "WPS-TIMEOUT";
/**
* Names of events from wpa_supplicant (minus the prefix). In the
@@ -149,6 +168,9 @@ public class WifiMonitor {
/* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
+ /* P2P-FIND-STOPPED */
+ private static final String P2P_FIND_STOPPED_STR = "P2P-FIND-STOPPED";
+
/* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
@@ -181,6 +203,10 @@ public class WifiMonitor {
pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
group_capab=0x0 */
private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
+
+ /* P2P-PROV-DISC-PBC-RESP 02:12:47:f2:5a:36 */
+ private static final String P2P_PROV_DISC_PBC_RSP_STR = "P2P-PROV-DISC-PBC-RESP";
+
/* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
group_capab=0x0 */
@@ -190,13 +216,60 @@ public class WifiMonitor {
group_capab=0x0 */
private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
+ /*
+ * Protocol format is as follows.<br>
+ * See the Table.62 in the WiFi Direct specification for the detail.
+ * ______________________________________________________________
+ * | Length(2byte) | Type(1byte) | TransId(1byte)}|
+ * ______________________________________________________________
+ * | status(1byte) | vendor specific(variable) |
+ *
+ * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300000101
+ * length=3, service type=0(ALL Service), transaction id=1,
+ * status=1(service protocol type not available)<br>
+ *
+ * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300020201
+ * length=3, service type=2(UPnP), transaction id=2,
+ * status=1(service protocol type not available)
+ *
+ * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 990002030010757569643a3131323
+ * 2646534652d383537342d353961622d393332322d3333333435363738393034343a3
+ * a75726e3a736368656d61732d75706e702d6f72673a736572766963653a436f6e746
+ * 56e744469726563746f72793a322c757569643a36383539646564652d383537342d3
+ * 53961622d393333322d3132333435363738393031323a3a75706e703a726f6f74646
+ * 576696365
+ * length=153,type=2(UPnP),transaction id=3,status=0
+ *
+ * UPnP Protocol format is as follows.
+ * ______________________________________________________
+ * | Version (1) | USN (Variable) |
+ *
+ * version=0x10(UPnP1.0) data=usn:uuid:1122de4e-8574-59ab-9322-33345678
+ * 9044::urn:schemas-upnp-org:service:ContentDirectory:2,usn:uuid:6859d
+ * ede-8574-59ab-9332-123456789012::upnp:rootdevice
+ *
+ * P2P-SERV-DISC-RESP 58:17:0c:bc:dd:ca 21 1900010200045f6970
+ * 70c00c000c01094d795072696e746572c027
+ * length=25, type=1(Bonjour),transaction id=2,status=0
+ *
+ * Bonjour Protocol format is as follows.
+ * __________________________________________________________
+ * |DNS Name(Variable)|DNS Type(1)|Version(1)|RDATA(Variable)|
+ *
+ * DNS Name=_ipp._tcp.local.,DNS type=12(PTR), Version=1,
+ * RDATA=MyPrinter._ipp._tcp.local.
+ *
+ */
+ private static final String P2P_SERV_DISC_RESP_STR = "P2P-SERV-DISC-RESP";
+
private static final String HOST_AP_EVENT_PREFIX_STR = "AP";
- /* AP-STA-CONNECTED 42:fc:89:a8:96:09 */
+ /* AP-STA-CONNECTED 42:fc:89:a8:96:09 dev_addr=02:90:4c:a0:92:54 */
private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
/* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
private final StateMachine mStateMachine;
+ private final WifiNative mWifiNative;
/* Supplicant events reported to a state machine */
private static final int BASE = Protocol.BASE_WIFI_MONITOR;
@@ -215,10 +288,16 @@ public class WifiMonitor {
public static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 6;
/* Password failure and EAP authentication failure */
public static final int AUTHENTICATION_FAILURE_EVENT = BASE + 7;
- /* WPS overlap detected */
- public static final int WPS_OVERLAP_EVENT = BASE + 8;
+ /* WPS success detected */
+ public static final int WPS_SUCCESS_EVENT = BASE + 8;
+ /* WPS failure detected */
+ public static final int WPS_FAIL_EVENT = BASE + 9;
+ /* WPS overlap detected */
+ public static final int WPS_OVERLAP_EVENT = BASE + 10;
+ /* WPS timeout detected */
+ public static final int WPS_TIMEOUT_EVENT = BASE + 11;
/* Driver was hung */
- public static final int DRIVER_HUNG_EVENT = BASE + 9;
+ public static final int DRIVER_HUNG_EVENT = BASE + 12;
/* P2P events */
public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21;
@@ -233,8 +312,11 @@ public class WifiMonitor {
public static final int P2P_INVITATION_RECEIVED_EVENT = BASE + 31;
public static final int P2P_INVITATION_RESULT_EVENT = BASE + 32;
public static final int P2P_PROV_DISC_PBC_REQ_EVENT = BASE + 33;
- public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 34;
- public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 35;
+ public static final int P2P_PROV_DISC_PBC_RSP_EVENT = BASE + 34;
+ public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 35;
+ public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 36;
+ public static final int P2P_FIND_STOPPED_EVENT = BASE + 37;
+ public static final int P2P_SERV_DISC_RESP_EVENT = BASE + 38;
/* hostap events */
public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41;
@@ -260,8 +342,9 @@ public class WifiMonitor {
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(StateMachine wifiStateMachine) {
+ public WifiMonitor(StateMachine wifiStateMachine, WifiNative wifiNative) {
mStateMachine = wifiStateMachine;
+ mWifiNative = wifiNative;
}
public void startMonitoring() {
@@ -286,7 +369,7 @@ public class WifiMonitor {
//noinspection InfiniteLoopStatement
for (;;) {
- String eventStr = WifiNative.waitForEvent();
+ String eventStr = mWifiNative.waitForEvent();
// Skip logging the common but mostly uninteresting scan-results event
if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
@@ -296,8 +379,14 @@ public class WifiMonitor {
if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+ } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
+ mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
+ } else if (eventStr.startsWith(WPS_FAIL_STR)) {
+ handleWpsFailEvent(eventStr);
} else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
+ } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
+ mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
} else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
handleP2pEvents(eventStr);
} else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
@@ -358,17 +447,6 @@ public class WifiMonitor {
handleDriverEvent(eventData);
} else if (event == TERMINATING) {
/**
- * If monitor socket is closed, we have already
- * stopped the supplicant, simply exit the monitor thread
- */
- if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
- if (false) {
- Log.d(TAG, "Monitor socket is closed, exiting thread");
- }
- break;
- }
-
- /**
* Close the supplicant connection if we see
* too many recv errors
*/
@@ -400,7 +478,7 @@ public class WifiMonitor {
int connectTries = 0;
while (true) {
- if (WifiNative.connectToSupplicant()) {
+ if (mWifiNative.connectToSupplicant()) {
return true;
}
if (connectTries++ < 5) {
@@ -446,6 +524,43 @@ public class WifiMonitor {
}
}
+ private void handleWpsFailEvent(String dataString) {
+ final Pattern p = Pattern.compile(WPS_FAIL_PATTERN);
+ Matcher match = p.matcher(dataString);
+ if (match.find()) {
+ String cfgErr = match.group(1);
+ String reason = match.group(2);
+
+ if (reason != null) {
+ switch(Integer.parseInt(reason)) {
+ case REASON_TKIP_ONLY_PROHIBITED:
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
+ WifiManager.WPS_TKIP_ONLY_PROHIBITED, 0));
+ return;
+ case REASON_WEP_PROHIBITED:
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
+ WifiManager.WPS_WEP_PROHIBITED, 0));
+ return;
+ }
+ }
+ if (cfgErr != null) {
+ switch(Integer.parseInt(cfgErr)) {
+ case CONFIG_AUTH_FAILURE:
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
+ WifiManager.WPS_AUTH_FAILURE, 0));
+ return;
+ case CONFIG_MULTIPLE_PBC_DETECTED:
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
+ WifiManager.WPS_OVERLAP_ERROR, 0));
+ return;
+ }
+ }
+ }
+ //For all other errors, return a generic internal error
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
+ WifiManager.ERROR, 0));
+ }
+
/**
* Handle p2p events
*/
@@ -454,6 +569,8 @@ public class WifiMonitor {
mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
} else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
+ } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
+ mStateMachine.sendMessage(P2P_FIND_STOPPED_EVENT);
} else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
new WifiP2pConfig(dataString));
@@ -480,10 +597,23 @@ public class WifiMonitor {
mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
} else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
- new WifiP2pDevice(dataString));
+ new WifiP2pProvDiscEvent(dataString));
+ } else if (dataString.startsWith(P2P_PROV_DISC_PBC_RSP_STR)) {
+ mStateMachine.sendMessage(P2P_PROV_DISC_PBC_RSP_EVENT,
+ new WifiP2pProvDiscEvent(dataString));
} else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT,
- new WifiP2pDevice(dataString));
+ new WifiP2pProvDiscEvent(dataString));
+ } else if (dataString.startsWith(P2P_PROV_DISC_SHOW_PIN_STR)) {
+ mStateMachine.sendMessage(P2P_PROV_DISC_SHOW_PIN_EVENT,
+ new WifiP2pProvDiscEvent(dataString));
+ } else if (dataString.startsWith(P2P_SERV_DISC_RESP_STR)) {
+ List<WifiP2pServiceResponse> list = WifiP2pServiceResponse.newInstance(dataString);
+ if (list != null) {
+ mStateMachine.sendMessage(P2P_SERV_DISC_RESP_EVENT, list);
+ } else {
+ Log.e(TAG, "Null service resp " + dataString);
+ }
}
}
@@ -492,10 +622,12 @@ public class WifiMonitor {
*/
private void handleHostApEvents(String dataString) {
String[] tokens = dataString.split(" ");
+ /* AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
- mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, tokens[1]);
+ mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, new WifiP2pDevice(dataString));
+ /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
} else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
- mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, tokens[1]);
+ mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, new WifiP2pDevice(dataString));
}
}
@@ -505,6 +637,9 @@ public class WifiMonitor {
* id=network-id state=new-state
*/
private void handleSupplicantStateChange(String dataString) {
+ String SSID = null;
+ int index = dataString.lastIndexOf("SSID=");
+ if (index != -1) SSID = dataString.substring(index + 5);
String[] dataTokens = dataString.split(" ");
String BSSID = null;
@@ -525,7 +660,6 @@ public class WifiMonitor {
try {
value = Integer.parseInt(nameValue[1]);
} catch (NumberFormatException e) {
- Log.w(TAG, "STATE-CHANGE non-integer parameter: " + token);
continue;
}
@@ -548,7 +682,7 @@ public class WifiMonitor {
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+ notifySupplicantStateChange(networkId, SSID, BSSID, newSupplicantState);
}
}
@@ -597,11 +731,13 @@ public class WifiMonitor {
* Send the state machine a notification that the state of the supplicant
* has changed.
* @param networkId the configured network on which the state change occurred
+ * @param SSID network name
+ * @param BSSID network address
* @param newState the new {@code SupplicantState}
*/
- void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ void notifySupplicantStateChange(int networkId, String SSID, String BSSID, SupplicantState newState) {
mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
+ new StateChangeResult(networkId, SSID, BSSID, newState)));
}
/**
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 6ff1bc2..4ec2e02 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -19,6 +19,9 @@ package android.net.wifi;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pDevice;
+import android.text.TextUtils;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
import android.util.Log;
import java.io.InputStream;
@@ -27,30 +30,25 @@ import java.util.ArrayList;
import java.util.List;
/**
- * 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.
- * Also, note that all WifiNative calls should happen in the
- * WifiStateTracker class except for waitForEvent() call which is
- * on a separate monitor channel for WifiMonitor
+ * Native calls for bring up/shut down of the supplicant daemon and for
+ * sending requests to the supplicant daemon
*
- * TODO: clean up the API and move the functionality from JNI to here. We should
- * be able to get everything done with doBooleanCommand, doIntCommand and
- * doStringCommand native commands
+ * waitForEvent() is called on the monitor thread for events. All other methods
+ * must be serialized from the framework.
*
* {@hide}
*/
public class WifiNative {
+ private static final boolean DBG = false;
+ private final String mTAG;
+ private static final int DEFAULT_GROUP_OWNER_INTENT = 7;
+
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);
+ String mInterface = "";
public native static boolean loadDriver();
@@ -58,96 +56,258 @@ public class WifiNative {
public native static boolean unloadDriver();
- public native static boolean startSupplicant();
-
- public native static boolean startP2pSupplicant();
-
- /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
- *
- * Note that underneath we use a harsh-sounding "terminate" supplicant command
- * for a graceful stop and a mild-sounding "stop" interface
- * to kill the process
- */
- public native static boolean stopSupplicant();
+ public native static boolean startSupplicant(boolean p2pSupported);
/* Sends a kill signal to supplicant. To be used when we have lost connection
or when the supplicant is hung */
public native static boolean killSupplicant();
- public native static boolean connectToSupplicant();
+ private native boolean connectToSupplicant(String iface);
+
+ private native void closeSupplicantConnection(String iface);
+
+ /**
+ * Wait for the supplicant to send an event, returning the event string.
+ * @return the event string sent by the supplicant.
+ */
+ private native String waitForEvent(String iface);
+
+ private native boolean doBooleanCommand(String iface, String command);
+
+ private native int doIntCommand(String iface, String command);
+
+ private native String doStringCommand(String iface, String command);
- public native static void closeSupplicantConnection();
+ public WifiNative(String iface) {
+ mInterface = iface;
+ mTAG = "WifiNative-" + iface;
+ }
- public native static boolean pingCommand();
+ public boolean connectToSupplicant() {
+ return connectToSupplicant(mInterface);
+ }
- public native static boolean scanCommand(boolean forceActive);
+ public void closeSupplicantConnection() {
+ closeSupplicantConnection(mInterface);
+ }
- public native static boolean setScanModeCommand(boolean setActive);
+ public String waitForEvent() {
+ return waitForEvent(mInterface);
+ }
- public native static String listNetworksCommand();
+ private boolean doBooleanCommand(String command) {
+ if (DBG) Log.d(mTAG, "doBoolean: " + command);
+ return doBooleanCommand(mInterface, command);
+ }
- public native static int addNetworkCommand();
+ private int doIntCommand(String command) {
+ if (DBG) Log.d(mTAG, "doInt: " + command);
+ return doIntCommand(mInterface, command);
+ }
- public native static boolean setNetworkVariableCommand(int netId, String name, String value);
+ private String doStringCommand(String command) {
+ if (DBG) Log.d(mTAG, "doString: " + command);
+ return doStringCommand(mInterface, command);
+ }
+
+ public boolean ping() {
+ String pong = doStringCommand("PING");
+ return (pong != null && pong.equals("PONG"));
+ }
+
+ public boolean scan() {
+ return doBooleanCommand("SCAN");
+ }
+
+ public boolean setScanMode(boolean setActive) {
+ if (setActive) {
+ return doBooleanCommand("DRIVER SCAN-ACTIVE");
+ } else {
+ return doBooleanCommand("DRIVER SCAN-PASSIVE");
+ }
+ }
+
+ /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
+ *
+ * Note that underneath we use a harsh-sounding "terminate" supplicant command
+ * for a graceful stop and a mild-sounding "stop" interface
+ * to kill the process
+ */
+ public boolean stopSupplicant() {
+ return doBooleanCommand("TERMINATE");
+ }
+
+ public String listNetworks() {
+ return doStringCommand("LIST_NETWORKS");
+ }
+
+ public int addNetwork() {
+ return doIntCommand("ADD_NETWORK");
+ }
+
+ public boolean setNetworkVariable(int netId, String name, String value) {
+ if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
+ return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
+ }
- public native static String getNetworkVariableCommand(int netId, String name);
+ public String getNetworkVariable(int netId, String name) {
+ if (TextUtils.isEmpty(name)) return null;
+ return doStringCommand("GET_NETWORK " + netId + " " + name);
+ }
- public native static boolean removeNetworkCommand(int netId);
+ public boolean removeNetwork(int netId) {
+ return doBooleanCommand("REMOVE_NETWORK " + netId);
+ }
- public native static boolean enableNetworkCommand(int netId, boolean disableOthers);
+ public boolean enableNetwork(int netId, boolean disableOthers) {
+ if (disableOthers) {
+ return doBooleanCommand("SELECT_NETWORK " + netId);
+ } else {
+ return doBooleanCommand("ENABLE_NETWORK " + netId);
+ }
+ }
- public native static boolean disableNetworkCommand(int netId);
+ public boolean disableNetwork(int netId) {
+ return doBooleanCommand("DISABLE_NETWORK " + netId);
+ }
- public native static boolean reconnectCommand();
+ public boolean reconnect() {
+ return doBooleanCommand("RECONNECT");
+ }
- public native static boolean reassociateCommand();
+ public boolean reassociate() {
+ return doBooleanCommand("REASSOCIATE");
+ }
- public native static boolean disconnectCommand();
+ public boolean disconnect() {
+ return doBooleanCommand("DISCONNECT");
+ }
- public native static String statusCommand();
+ public String status() {
+ return doStringCommand("STATUS");
+ }
- public native static String getMacAddressCommand();
+ public String getMacAddress() {
+ //Macaddr = XX.XX.XX.XX.XX.XX
+ String ret = doStringCommand("DRIVER MACADDR");
+ if (!TextUtils.isEmpty(ret)) {
+ String[] tokens = ret.split(" = ");
+ if (tokens.length == 2) return tokens[1];
+ }
+ return null;
+ }
- public native static String scanResultsCommand();
+ public String scanResults() {
+ return doStringCommand("SCAN_RESULTS");
+ }
- public native static boolean startDriverCommand();
+ public boolean startDriver() {
+ return doBooleanCommand("DRIVER START");
+ }
- public native static boolean stopDriverCommand();
+ public boolean stopDriver() {
+ return doBooleanCommand("DRIVER STOP");
+ }
/**
* Start filtering out Multicast V4 packets
* @return {@code true} if the operation succeeded, {@code false} otherwise
+ *
+ * Multicast filtering rules work as follows:
+ *
+ * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
+ * a power optimized mode (typically when screen goes off).
+ *
+ * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
+ * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
+ *
+ * DRIVER RXFILTER-ADD Num
+ * where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
+ *
+ * and DRIVER RXFILTER-START
+ * In order to stop the usage of these rules, we do
+ *
+ * DRIVER RXFILTER-STOP
+ * DRIVER RXFILTER-REMOVE Num
+ * where Num is as described for RXFILTER-ADD
+ *
+ * The SETSUSPENDOPT driver command overrides the filtering rules
*/
- public native static boolean startFilteringMulticastV4Packets();
+ public boolean startFilteringMulticastV4Packets() {
+ return doBooleanCommand("DRIVER RXFILTER-STOP")
+ && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
+ && doBooleanCommand("DRIVER RXFILTER-START");
+ }
/**
* Stop filtering out Multicast V4 packets.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
- public native static boolean stopFilteringMulticastV4Packets();
+ public boolean stopFilteringMulticastV4Packets() {
+ return doBooleanCommand("DRIVER RXFILTER-STOP")
+ && doBooleanCommand("DRIVER RXFILTER-ADD 2")
+ && doBooleanCommand("DRIVER RXFILTER-START");
+ }
/**
* Start filtering out Multicast V6 packets
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
- public native static boolean startFilteringMulticastV6Packets();
+ public boolean startFilteringMulticastV6Packets() {
+ return doBooleanCommand("DRIVER RXFILTER-STOP")
+ && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
+ && doBooleanCommand("DRIVER RXFILTER-START");
+ }
/**
* Stop filtering out Multicast V6 packets.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
- public native static boolean stopFilteringMulticastV6Packets();
+ public boolean stopFilteringMulticastV6Packets() {
+ return doBooleanCommand("DRIVER RXFILTER-STOP")
+ && doBooleanCommand("DRIVER RXFILTER-ADD 3")
+ && doBooleanCommand("DRIVER RXFILTER-START");
+ }
- public native static boolean setPowerModeCommand(int mode);
+ public int getPowerMode() {
+ String ret = doStringCommand("DRIVER GETPOWER");
+ if (!TextUtils.isEmpty(ret)) {
+ // reply comes back in the form "powermode = XX" where XX is the
+ // number we're interested in.
+ String[] tokens = ret.split(" = ");
+ try {
+ if (tokens.length == 2) return Integer.parseInt(tokens[1]);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ return -1;
+ }
- public native static int getBandCommand();
+ public boolean setPowerMode(int mode) {
+ return doBooleanCommand("DRIVER POWERMODE " + mode);
+ }
- public native static boolean setBandCommand(int band);
+ public int getBand() {
+ String ret = doStringCommand("DRIVER GETBAND");
+ if (!TextUtils.isEmpty(ret)) {
+ //reply is "BAND X" where X is the band
+ String[] tokens = ret.split(" ");
+ try {
+ if (tokens.length == 2) return Integer.parseInt(tokens[1]);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ return -1;
+ }
- public native static int getPowerModeCommand();
+ public boolean setBand(int band) {
+ return doBooleanCommand("DRIVER SETBAND " + band);
+ }
- /**
+ /**
* Sets the bluetooth coexistence mode.
*
* @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
@@ -155,7 +315,9 @@ public class WifiNative {
* {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
* @return Whether the mode was successfully set.
*/
- public native static boolean setBluetoothCoexistenceModeCommand(int mode);
+ public boolean setBluetoothCoexistenceMode(int mode) {
+ return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
+ }
/**
* Enable or disable Bluetooth coexistence scan mode. When this mode is on,
@@ -165,43 +327,57 @@ public class WifiNative {
* @param isSet whether to enable or disable this mode
* @return {@code true} if the command succeeded, {@code false} otherwise.
*/
- public native static boolean setBluetoothCoexistenceScanModeCommand(boolean setCoexScanMode);
-
- 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 startWpsPbcCommand(String bssid);
-
- public native static boolean startWpsWithPinFromAccessPointCommand(String bssid, String apPin);
-
- public native static String startWpsWithPinFromDeviceCommand(String bssid);
+ public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
+ if (setCoexScanMode) {
+ return doBooleanCommand("DRIVER BTCOEXSCAN-START");
+ } else {
+ return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
+ }
+ }
- public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+ public boolean saveConfig() {
+ // Make sure we never write out a value for AP_SCAN other than 1
+ return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG");
+ }
- public native static boolean setCountryCodeCommand(String countryCode);
+ public boolean setScanResultHandling(int mode) {
+ return doBooleanCommand("AP_SCAN " + mode);
+ }
- /**
- * 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();
+ public boolean addToBlacklist(String bssid) {
+ if (TextUtils.isEmpty(bssid)) return false;
+ return doBooleanCommand("BLACKLIST " + bssid);
+ }
- public native static void enableBackgroundScanCommand(boolean enable);
+ public boolean clearBlacklist() {
+ return doBooleanCommand("BLACKLIST clear");
+ }
- public native static void setScanIntervalCommand(int scanInterval);
+ public boolean setSuspendOptimizations(boolean enabled) {
+ if (enabled) {
+ return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
+ } else {
+ return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
+ }
+ }
- private native static boolean doBooleanCommand(String command);
+ public boolean setCountryCode(String countryCode) {
+ return doBooleanCommand("DRIVER COUNTRY " + countryCode);
+ }
- private native static int doIntCommand(String command);
+ public void enableBackgroundScan(boolean enable) {
+ //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
+ //and will need an update if the names are changed
+ if (enable) {
+ doBooleanCommand("DRIVER BGSCAN-START");
+ } else {
+ doBooleanCommand("DRIVER BGSCAN-STOP");
+ }
+ }
- private native static String doStringCommand(String command);
+ public void setScanInterval(int scanInterval) {
+ doBooleanCommand("SCAN_INTERVAL " + scanInterval);
+ }
/** Example output:
* RSSI=-65
@@ -209,60 +385,137 @@ public class WifiNative {
* NOISE=9999
* FREQUENCY=0
*/
- public static String signalPoll() {
+ public String signalPoll() {
return doStringCommand("SIGNAL_POLL");
}
- public static boolean wpsPbc() {
- return doBooleanCommand("WPS_PBC");
+ public boolean startWpsPbc(String bssid) {
+ if (TextUtils.isEmpty(bssid)) {
+ return doBooleanCommand("WPS_PBC");
+ } else {
+ return doBooleanCommand("WPS_PBC " + bssid);
+ }
+ }
+
+ public boolean startWpsPbc(String iface, String bssid) {
+ if (TextUtils.isEmpty(bssid)) {
+ return doBooleanCommand("WPS_PBC interface=" + iface);
+ } else {
+ return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid);
+ }
}
- public static boolean wpsPin(String pin) {
+ public boolean startWpsPinKeypad(String pin) {
+ if (TextUtils.isEmpty(pin)) return false;
return doBooleanCommand("WPS_PIN any " + pin);
}
- public static boolean setPersistentReconnect(boolean enabled) {
+ public boolean startWpsPinKeypad(String iface, String pin) {
+ if (TextUtils.isEmpty(pin)) return false;
+ return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin);
+ }
+
+
+ public String startWpsPinDisplay(String bssid) {
+ if (TextUtils.isEmpty(bssid)) {
+ return doStringCommand("WPS_PIN any");
+ } else {
+ return doStringCommand("WPS_PIN " + bssid);
+ }
+ }
+
+ public String startWpsPinDisplay(String iface, String bssid) {
+ if (TextUtils.isEmpty(bssid)) {
+ return doStringCommand("WPS_PIN interface=" + iface + " any");
+ } else {
+ return doStringCommand("WPS_PIN interface=" + iface + " " + bssid);
+ }
+ }
+
+ /* Configures an access point connection */
+ public boolean startWpsRegistrar(String bssid, String pin) {
+ if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
+ return doBooleanCommand("WPS_REG " + bssid + " " + pin);
+ }
+
+ public boolean cancelWps() {
+ return doBooleanCommand("WPS_CANCEL");
+ }
+
+ public boolean setPersistentReconnect(boolean enabled) {
int value = (enabled == true) ? 1 : 0;
- return WifiNative.doBooleanCommand("SET persistent_reconnect " + value);
+ return doBooleanCommand("SET persistent_reconnect " + value);
+ }
+
+ public boolean setDeviceName(String name) {
+ return doBooleanCommand("SET device_name " + name);
+ }
+
+ public boolean setDeviceType(String type) {
+ return doBooleanCommand("SET device_type " + type);
+ }
+
+ public boolean setConfigMethods(String cfg) {
+ return doBooleanCommand("SET config_methods " + cfg);
+ }
+
+ public boolean setP2pSsidPostfix(String postfix) {
+ return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
+ }
+
+ public boolean setP2pGroupIdle(String iface, int time) {
+ return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time);
}
- public static boolean setDeviceName(String name) {
- return WifiNative.doBooleanCommand("SET device_name " + name);
+ public boolean setP2pPowerSave(String iface, boolean enabled) {
+ if (enabled) {
+ return doBooleanCommand("P2P_SET interface=" + iface + " ps 1");
+ } else {
+ return doBooleanCommand("P2P_SET interface=" + iface + " ps 0");
+ }
}
- public static boolean setDeviceType(String type) {
- return WifiNative.doBooleanCommand("SET device_type " + type);
+ /**
+ * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
+ * P2P connection over STA
+ */
+ public boolean setConcurrencyPriority(String s) {
+ return doBooleanCommand("P2P_SET conc_priority " + s);
}
- public static boolean p2pFind() {
+ public boolean p2pFind() {
return doBooleanCommand("P2P_FIND");
}
- public static boolean p2pFind(int timeout) {
+ public boolean p2pFind(int timeout) {
if (timeout <= 0) {
return p2pFind();
}
return doBooleanCommand("P2P_FIND " + timeout);
}
- public static boolean p2pListen() {
+ public boolean p2pStopFind() {
+ return doBooleanCommand("P2P_STOP_FIND");
+ }
+
+ public boolean p2pListen() {
return doBooleanCommand("P2P_LISTEN");
}
- public static boolean p2pListen(int timeout) {
+ public boolean p2pListen(int timeout) {
if (timeout <= 0) {
return p2pListen();
}
return doBooleanCommand("P2P_LISTEN " + timeout);
}
- public static boolean p2pFlush() {
+ public boolean p2pFlush() {
return doBooleanCommand("P2P_FLUSH");
}
/* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
[persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
- public static String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
+ public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
if (config == null) return null;
List<String> args = new ArrayList<String>();
WpsInfo wps = config.wps;
@@ -273,8 +526,11 @@ public class WifiNative {
args.add("pbc");
break;
case WpsInfo.DISPLAY:
- //TODO: pass the pin back for display
- args.add("pin");
+ if (TextUtils.isEmpty(wps.pin)) {
+ args.add("pin");
+ } else {
+ args.add(wps.pin);
+ }
args.add("display");
break;
case WpsInfo.KEYPAD:
@@ -295,9 +551,11 @@ public class WifiNative {
if (joinExistingGroup) args.add("join");
+ //TODO: This can be adapted based on device plugged in state and
+ //device battery state
int groupOwnerIntent = config.groupOwnerIntent;
if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
- groupOwnerIntent = 3; //default value
+ groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
}
args.add("go_intent=" + groupOwnerIntent);
@@ -307,26 +565,44 @@ public class WifiNative {
return doStringCommand(command);
}
- public static boolean p2pCancelConnect() {
+ public boolean p2pCancelConnect() {
return doBooleanCommand("P2P_CANCEL");
}
- public static boolean p2pGroupAdd() {
+ public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
+ if (config == null) return false;
+
+ switch (config.wps.setup) {
+ case WpsInfo.PBC:
+ return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
+ case WpsInfo.DISPLAY:
+ //We are doing display, so provision discovery is keypad
+ return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
+ case WpsInfo.KEYPAD:
+ //We are doing keypad, so provision discovery is display
+ return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
+ default:
+ break;
+ }
+ return false;
+ }
+
+ public boolean p2pGroupAdd() {
return doBooleanCommand("P2P_GROUP_ADD");
}
- public static boolean p2pGroupRemove(String iface) {
- if (iface == null) return false;
+ public boolean p2pGroupRemove(String iface) {
+ if (TextUtils.isEmpty(iface)) return false;
return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
}
- public static boolean p2pReject(String deviceAddress) {
+ public boolean p2pReject(String deviceAddress) {
return doBooleanCommand("P2P_REJECT " + deviceAddress);
}
/* Invite a peer to a group */
- public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
- if (deviceAddress == null) return false;
+ public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
+ if (TextUtils.isEmpty(deviceAddress)) return false;
if (group == null) {
return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
@@ -337,35 +613,15 @@ public class WifiNative {
}
/* Reinvoke a persistent connection */
- public static boolean p2pReinvoke(int netId, String deviceAddress) {
- if (deviceAddress == null || netId < 0) return false;
+ public boolean p2pReinvoke(int netId, String deviceAddress) {
+ if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
}
- public static String p2pGetInterfaceAddress(String deviceAddress) {
- if (deviceAddress == null) return null;
-
- // "p2p_peer deviceAddress" returns a multi-line result containing
- // intended_addr=fa:7b:7a:42:82:13
- String peerInfo = p2pPeer(deviceAddress);
- if (peerInfo == null) return null;
- String[] tokens= peerInfo.split("\n");
-
- for (String token : tokens) {
- //TODO: update from interface_addr when wpa_supplicant implementation is fixed
- if (token.startsWith("intended_addr=")) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) break;
- return nameValue[1];
- }
- }
- return null;
- }
-
- public static String p2pGetDeviceAddress() {
- String status = statusCommand();
+ public String p2pGetDeviceAddress() {
+ String status = status();
if (status == null) return "";
String[] tokens = status.split("\n");
@@ -379,7 +635,88 @@ public class WifiNative {
return "";
}
- public static String p2pPeer(String deviceAddress) {
+ public boolean isGroupOwner(String deviceAddress) {
+ /* BSS returns details only for a GO */
+ String bssInfo = doStringCommand("BSS p2p_dev_addr=" + deviceAddress);
+ if (TextUtils.isEmpty(bssInfo)) return false;
+ return true;
+ }
+
+ public String p2pPeer(String deviceAddress) {
return doStringCommand("P2P_PEER " + deviceAddress);
}
+
+ public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
+ /*
+ * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
+ * P2P_SERVICE_ADD upnp <version hex> <service>
+ *
+ * e.g)
+ * [Bonjour]
+ * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+ * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
+ * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+ * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
+ * 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
+ *
+ * [UPnP]
+ * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
+ * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
+ * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
+ * -org:device:InternetGatewayDevice:1
+ * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
+ * -org:service:ContentDirectory:2
+ */
+ for (String s : servInfo.getSupplicantQueryList()) {
+ String command = "P2P_SERVICE_ADD";
+ command += (" " + s);
+ if (!doBooleanCommand(command)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
+ /*
+ * P2P_SERVICE_DEL bonjour <query hexdump>
+ * P2P_SERVICE_DEL upnp <version hex> <service>
+ */
+ for (String s : servInfo.getSupplicantQueryList()) {
+ String command = "P2P_SERVICE_DEL ";
+
+ String[] data = s.split(" ");
+ if (data.length < 2) {
+ return false;
+ }
+ if ("upnp".equals(data[0])) {
+ command += s;
+ } else if ("bonjour".equals(data[0])) {
+ command += data[0];
+ command += (" " + data[1]);
+ } else {
+ return false;
+ }
+ if (!doBooleanCommand(command)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean p2pServiceFlush() {
+ return doBooleanCommand("P2P_SERVICE_FLUSH");
+ }
+
+ public String p2pServDiscReq(String addr, String query) {
+ String command = "P2P_SERV_DISC_REQ";
+ command += (" " + addr);
+ command += (" " + query);
+
+ return doStringCommand(command);
+ }
+
+ public boolean p2pServDiscCancelReq(String id) {
+ return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index df622d6..2f14098 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -45,6 +45,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.DhcpInfoInternal;
@@ -113,15 +114,22 @@ public class WifiStateMachine extends StateMachine {
private static final String SOFTAP_IFACE = "wl0.1";
private WifiMonitor mWifiMonitor;
+ private WifiNative mWifiNative;
+ private WifiConfigStore mWifiConfigStore;
private INetworkManagementService mNwService;
private ConnectivityManager mCm;
+ private final boolean mP2pSupported;
+
/* Scan results handling */
private List<ScanResult> mScanResults;
private static final Pattern scanResultPattern = Pattern.compile("\t+");
private static final int SCAN_RESULT_CACHE_SIZE = 80;
private final LruCache<String, ScanResult> mScanResultCache;
+ /* Chipset supports background scan */
+ private final boolean mBackgroundScanSupported;
+
private String mInterfaceName;
/* Tethering interface could be seperate from wlan interface */
private String mTetherInterfaceName;
@@ -135,9 +143,17 @@ public class WifiStateMachine extends StateMachine {
private int mReconnectCount = 0;
private boolean mIsScanMode = false;
private boolean mScanResultIsPending = false;
+ /* Tracks if the current scan settings are active */
+ private boolean mSetScanActive = false;
+ /* High perf mode is true if an app has held a high perf Wifi Lock */
+ private boolean mHighPerfMode = false;
private boolean mBluetoothConnectionActive = false;
+ private BroadcastReceiver mScreenReceiver;
+ private IntentFilter mScreenFilter;
+ private PowerManager.WakeLock mSuspendWakeLock;
+
/**
* Interval in milliseconds between polling for RSSI
* and linkspeed information
@@ -177,7 +193,6 @@ public class WifiStateMachine extends StateMachine {
private WifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private SupplicantStateTracker mSupplicantStateTracker;
- private WpsStateMachine mWpsStateMachine;
private DhcpStateMachine mDhcpStateMachine;
private AlarmManager mAlarmManager;
@@ -268,14 +283,14 @@ public class WifiStateMachine extends StateMachine {
static final int CMD_ENABLE_NETWORK = BASE + 54;
/* Enable all networks */
static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
- /* Disable a network. The device does not attempt a connection to the given network. */
- static final int CMD_DISABLE_NETWORK = BASE + 56;
/* Blacklist network. De-prioritizes the given BSSID for connection. */
- static final int CMD_BLACKLIST_NETWORK = BASE + 57;
+ static final int CMD_BLACKLIST_NETWORK = BASE + 56;
/* Clear the blacklist network list */
- static final int CMD_CLEAR_BLACKLIST = BASE + 58;
+ static final int CMD_CLEAR_BLACKLIST = BASE + 57;
/* Save configuration */
- static final int CMD_SAVE_CONFIG = BASE + 59;
+ static final int CMD_SAVE_CONFIG = BASE + 58;
+ /* Get configured networks*/
+ static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
/* Supplicant commands after driver start*/
/* Initiate a scan */
@@ -314,33 +329,16 @@ public class WifiStateMachine extends StateMachine {
static final int CMD_START_PACKET_FILTERING = BASE + 84;
/* Clear packet filter */
static final int CMD_STOP_PACKET_FILTERING = BASE + 85;
+ /* Set suspend mode optimizations in the driver */
+ static final int CMD_SET_SUSPEND_OPTIMIZATIONS = BASE + 86;
+ /* Clear suspend mode optimizations in the driver */
+ static final int CMD_CLEAR_SUSPEND_OPTIMIZATIONS = BASE + 87;
/* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
static final int MULTICAST_V6 = 1;
static final int MULTICAST_V4 = 0;
- /* Connect to a specified network (network id
- * or WifiConfiguration) This involves increasing
- * the priority of the network, enabling the network
- * (while disabling others) and issuing a reconnect.
- * Note that CMD_RECONNECT just does a reconnect to
- * an existing network. All the networks get enabled
- * upon a successful connection or a failure.
- */
- static final int CMD_CONNECT_NETWORK = BASE + 86;
- /* Save the specified network. This involves adding
- * an enabled network (if new) and updating the
- * config and issuing a save on supplicant config.
- */
- static final int CMD_SAVE_NETWORK = BASE + 87;
- /* Delete the specified network. This involves
- * removing the network and issuing a save on
- * supplicant config.
- */
- static final int CMD_FORGET_NETWORK = BASE + 88;
- /* Start Wi-Fi protected setup */
- static final int CMD_START_WPS = BASE + 89;
- /* Set the frequency band */
+ /* Set the frequency band */
static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
/* Enable background scan for configured networks */
static final int CMD_ENABLE_BACKGROUND_SCAN = BASE + 91;
@@ -349,15 +347,9 @@ public class WifiStateMachine extends StateMachine {
/* Reset the supplicant state tracker */
static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
- /* Commands/events reported by WpsStateMachine */
- /* Indicates the completion of WPS activity */
- static final int WPS_COMPLETED_EVENT = BASE + 121;
- /* Reset the WPS state machine */
- static final int CMD_RESET_WPS_STATE = BASE + 122;
-
- /* Interaction with WifiP2pService */
- public static final int WIFI_ENABLE_PENDING = BASE + 131;
- public static final int P2P_ENABLE_PROCEED = BASE + 132;
+ /* P2p commands */
+ public static final int CMD_ENABLE_P2P = BASE + 131;
+ public static final int CMD_DISABLE_P2P = BASE + 132;
private static final int CONNECT_MODE = 1;
private static final int SCAN_ONLY_MODE = 2;
@@ -454,8 +446,12 @@ public class WifiStateMachine extends StateMachine {
private State mScanModeState = new ScanModeState();
/* Connecting to an access point */
private State mConnectModeState = new ConnectModeState();
- /* Fetching IP after network connection (assoc+auth complete) */
- private State mConnectingState = new ConnectingState();
+ /* Connected at 802.11 (L2) level */
+ private State mL2ConnectedState = new L2ConnectedState();
+ /* fetching IP after connection to access point (assoc+auth complete) */
+ private State mObtainingIpState = new ObtainingIpState();
+ /* Waiting for link quality verification to be complete */
+ private State mVerifyingLinkState = new VerifyingLinkState();
/* Connected with IP addr */
private State mConnectedState = new ConnectedState();
/* disconnect issued, waiting for network disconnect confirmation */
@@ -463,7 +459,7 @@ public class WifiStateMachine extends StateMachine {
/* Network is not connected, supplicant assoc+auth is not complete */
private State mDisconnectedState = new DisconnectedState();
/* Waiting for WPS to be completed*/
- private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
+ private State mWpsRunningState = new WpsRunningState();
/* Soft ap is starting up */
private State mSoftApStartingState = new SoftApStartingState();
@@ -476,9 +472,6 @@ public class WifiStateMachine extends StateMachine {
/* Waiting for untether confirmation to stop soft Ap */
private State mSoftApStoppingState = new SoftApStoppingState();
- /* Wait till p2p is disabled */
- private State mWaitForP2pDisableState = new WaitForP2pDisableState();
-
private class TetherStateChange {
ArrayList<String> available;
ArrayList<String> active;
@@ -537,11 +530,6 @@ public class WifiStateMachine extends StateMachine {
private final WorkSource mLastRunningWifiUids = new WorkSource();
private final IBatteryStats mBatteryStats;
- private boolean mNextWifiActionExplicit = false;
- private int mLastExplicitNetworkId;
- private long mLastNetworkChoiceTime;
- private static final long EXPLICIT_CONNECT_ALLOWED_DELAY_MS = 2 * 60 * 1000;
-
public WifiStateMachine(Context context, String wlanInterface) {
super(TAG);
@@ -555,11 +543,16 @@ public class WifiStateMachine extends StateMachine {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
- mWifiMonitor = new WifiMonitor(this);
+ mP2pSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT);
+
+ mWifiNative = new WifiNative(mInterfaceName);
+ mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
+ mWifiMonitor = new WifiMonitor(this, mWifiNative);
mDhcpInfoInternal = new DhcpInfoInternal();
mWifiInfo = new WifiInfo();
- mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
- mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
+ mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
+ getHandler());
mLinkProperties = new LinkProperties();
WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
@@ -586,6 +579,9 @@ public class WifiStateMachine extends StateMachine {
mDriverStopDelayMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_wifi_driver_stop_delay);
+ mBackgroundScanSupported = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_background_scan_support);
+
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -607,11 +603,41 @@ public class WifiStateMachine extends StateMachine {
},
new IntentFilter(ACTION_START_SCAN));
+ mScreenFilter = new IntentFilter();
+ mScreenFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mScreenFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ mScreenReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ enableRssiPolling(true);
+ if (mBackgroundScanSupported) {
+ enableBackgroundScanCommand(false);
+ }
+ enableAllNetworks();
+ sendMessage(CMD_CLEAR_SUSPEND_OPTIMIZATIONS);
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ enableRssiPolling(false);
+ if (mBackgroundScanSupported) {
+ enableBackgroundScanCommand(true);
+ }
+ //Allow 2s for suspend optimizations to be set
+ mSuspendWakeLock.acquire(2000);
+ sendMessage(CMD_SET_SUSPEND_OPTIMIZATIONS);
+ }
+ }
+ };
+
mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
+ mSuspendWakeLock.setReferenceCounted(false);
+
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mDriverUnloadingState, mDefaultState);
@@ -625,11 +651,13 @@ public class WifiStateMachine extends StateMachine {
addState(mDriverStartedState, mSupplicantStartedState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
- addState(mConnectingState, mConnectModeState);
- addState(mConnectedState, mConnectModeState);
+ addState(mL2ConnectedState, mConnectModeState);
+ addState(mObtainingIpState, mL2ConnectedState);
+ addState(mVerifyingLinkState, mL2ConnectedState);
+ addState(mConnectedState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
- addState(mWaitForWpsCompletionState, mConnectModeState);
+ addState(mWpsRunningState, mConnectModeState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
@@ -638,10 +666,10 @@ public class WifiStateMachine extends StateMachine {
addState(mTetheringState, mSoftApStartedState);
addState(mTetheredState, mSoftApStartedState);
addState(mSoftApStoppingState, mDefaultState);
- addState(mWaitForP2pDisableState, mDefaultState);
setInitialState(mInitialState);
+ setProcessedMessagesSize(100);
if (DBG) setDbg(true);
//start the state machine
@@ -652,6 +680,9 @@ public class WifiStateMachine extends StateMachine {
* Methods exposed for public use
********************************************************/
+ public Messenger getMessenger() {
+ return new Messenger(getHandler());
+ }
/**
* TODO: doc
*/
@@ -855,8 +886,11 @@ public class WifiStateMachine extends StateMachine {
return result;
}
- public List<WifiConfiguration> syncGetConfiguredNetworks() {
- return WifiConfigStore.getConfiguredNetworks();
+ public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
+ List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
+ resultMsg.recycle();
+ return result;
}
/**
@@ -893,9 +927,8 @@ public class WifiStateMachine extends StateMachine {
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
- Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId,
- WifiConfiguration.DISABLED_UNKNOWN_REASON);
- boolean result = (resultMsg.arg1 != FAILURE);
+ Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
+ boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
resultMsg.recycle();
return result;
}
@@ -918,39 +951,6 @@ public class WifiStateMachine extends StateMachine {
sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
}
- public void connectNetwork(int netId) {
- sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
- }
-
- public void connectNetwork(WifiConfiguration wifiConfig) {
- /* arg1 is used to indicate netId, force a netId value of
- * WifiConfiguration.INVALID_NETWORK_ID when we are passing
- * a configuration since the default value of 0 is a valid netId
- */
- sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
- 0, wifiConfig));
- }
-
- public void saveNetwork(WifiConfiguration wifiConfig) {
- sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
- }
-
- public void forgetNetwork(int netId) {
- sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
- }
-
- public void disableNetwork(Messenger replyTo, int netId, int reason) {
- Message message = obtainMessage(CMD_DISABLE_NETWORK, netId, reason);
- message.replyTo = replyTo;
- sendMessage(message);
- }
-
- public void startWps(Messenger replyTo, WpsInfo config) {
- Message msg = obtainMessage(CMD_START_WPS, config);
- msg.replyTo = replyTo;
- sendMessage(msg);
- }
-
public void enableRssiPolling(boolean enabled) {
sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
}
@@ -1042,7 +1042,7 @@ public class WifiStateMachine extends StateMachine {
* Returns the wifi configuration file
*/
public String getConfigFile() {
- return WifiConfigStore.getConfigFile();
+ return mWifiConfigStore.getConfigFile();
}
/**
@@ -1116,12 +1116,43 @@ public class WifiStateMachine extends StateMachine {
sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
sb.append("Supplicant status").append(LS)
- .append(WifiNative.statusCommand()).append(LS).append(LS);
+ .append(mWifiNative.status()).append(LS).append(LS);
- sb.append(WifiConfigStore.dump());
+ sb.append(mWifiConfigStore.dump());
return sb.toString();
}
+ @Override
+ protected boolean recordProcessedMessage(Message msg) {
+ //Ignore screen on/off & common messages when driver has started
+ if (getCurrentState() == mConnectedState || getCurrentState() == mDisconnectedState) {
+ switch (msg.what) {
+ case CMD_LOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_START_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_HIGH_PERF_MODE:
+ case CMD_SET_SUSPEND_OPTIMIZATIONS:
+ case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
+ case CMD_ENABLE_BACKGROUND_SCAN:
+ case CMD_ENABLE_ALL_NETWORKS:
+ return false;
+ }
+ }
+
+ switch (msg.what) {
+ case CMD_START_SCAN:
+ case CMD_ENABLE_RSSI_POLL:
+ case CMD_RSSI_POLL:
+ case CMD_DELAYED_STOP_DRIVER:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiWatchdogStateMachine.RSSI_FETCH:
+ return false;
+ default:
+ return true;
+ }
+ }
+
/*********************************************************
* Internal private functions
********************************************************/
@@ -1149,9 +1180,9 @@ public class WifiStateMachine extends StateMachine {
ifcg = mNwService.getInterfaceConfig(intf);
if (ifcg != null) {
/* IP/netmask: 192.168.43.1/255.255.255.0 */
- ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
- "192.168.43.1"), 24);
- ifcg.interfaceFlags = "[up]";
+ ifcg.setLinkAddress(new LinkAddress(
+ NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
+ ifcg.setInterfaceUp();
mNwService.setInterfaceConfig(intf, ifcg);
}
@@ -1183,8 +1214,8 @@ public class WifiStateMachine extends StateMachine {
try {
ifcg = mNwService.getInterfaceConfig(mInterfaceName);
if (ifcg != null) {
- ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
- "0.0.0.0"), 0);
+ ifcg.setLinkAddress(
+ new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
}
} catch (Exception e) {
@@ -1410,23 +1441,6 @@ public class WifiStateMachine extends StateMachine {
mScanResults = scanList;
}
- private String fetchSSID() {
- String status = WifiNative.statusCommand();
- if (status == null) {
- return null;
- }
- // extract ssid from a series of "name=value"
- String[] lines = status.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("ssid")) return value;
- }
- return null;
- }
-
/*
* Fetch RSSI and linkspeed on current connection
*/
@@ -1434,7 +1448,7 @@ public class WifiStateMachine extends StateMachine {
int newRssi = -1;
int newLinkSpeed = -1;
- String signalPoll = WifiNative.signalPoll();
+ String signalPoll = mWifiNative.signalPoll();
if (signalPoll != null) {
String[] lines = signalPoll.split("\n");
@@ -1465,14 +1479,11 @@ public class WifiStateMachine extends StateMachine {
* 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 mWifiInforming others
+ * broadcasts, at the cost of not informing others
* interested in RSSI of all the changes in signal
* level.
*/
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
if (newSignalLevel != mLastSignalLevel) {
sendRssiChangeBroadcast(newRssi);
}
@@ -1486,29 +1497,14 @@ public class WifiStateMachine extends StateMachine {
}
}
- private void setHighPerfModeEnabledNative(boolean enable) {
- if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
- loge("set suspend optimizations failed!");
- }
- if (enable) {
- if (!WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE)) {
- loge("set power mode active failed!");
- }
- } else {
- if (!WifiNative.setPowerModeCommand(POWER_MODE_AUTO)) {
- loge("set power mode auto failed!");
- }
- }
- }
-
private void configureLinkProperties() {
- if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
+ if (mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+ mLinkProperties = mWifiConfigStore.getLinkProperties(mLastNetworkId);
} else {
synchronized (mDhcpInfoInternal) {
mLinkProperties = mDhcpInfoInternal.makeLinkProperties();
}
- mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+ mLinkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
}
mLinkProperties.setInterfaceName(mInterfaceName);
if (DBG) {
@@ -1540,22 +1536,17 @@ public class WifiStateMachine extends StateMachine {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
if (bssid != null)
intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED)
+ if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
+ mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
+ }
mContext.sendStickyBroadcast(intent);
}
- private void sendErrorBroadcast(int errorCode) {
- Intent intent = new Intent(WifiManager.ERROR_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_ERROR_CODE, errorCode);
- mContext.sendBroadcast(intent);
- }
-
private void sendLinkConfigurationChangedBroadcast() {
Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1610,9 +1601,9 @@ public class WifiStateMachine extends StateMachine {
/* BSSID is valid only in ASSOCIATING state */
mWifiInfo.setBSSID(stateChangeResult.BSSID);
}
+ mWifiInfo.setSSID(stateChangeResult.SSID);
mSupplicantStateTracker.sendMessage(Message.obtain(message));
- mWpsStateMachine.sendMessage(Message.obtain(message));
return state;
}
@@ -1628,6 +1619,10 @@ public class WifiStateMachine extends StateMachine {
* stop DHCP
*/
if (mDhcpStateMachine != null) {
+ /* In case we were in middle of DHCP operation
+ restore back powermode */
+ handlePostDhcpSetup();
+
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
mDhcpStateMachine.quit();
mDhcpStateMachine = null;
@@ -1647,17 +1642,18 @@ public class WifiStateMachine extends StateMachine {
mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
mWifiInfo.setRssi(MIN_RSSI);
mWifiInfo.setLinkSpeed(-1);
- mWifiInfo.setExplicitConnect(false);
- /* send event to CM & network change broadcast */
setNetworkDetailedState(DetailedState.DISCONNECTED);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
+
+ /* send event to CM & network change broadcast */
sendNetworkStateChangeBroadcast(mLastBssid);
/* Clear network properties */
mLinkProperties.clear();
/* Clear IP settings if the network used DHCP */
- if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- WifiConfigStore.clearIpConfiguration(mLastNetworkId);
+ if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+ mWifiConfigStore.clearIpConfiguration(mLastNetworkId);
}
mLastBssid= null;
@@ -1683,29 +1679,29 @@ public class WifiStateMachine extends StateMachine {
* coexistence would interrupt that connection.
*/
// Disable the coexistence mode
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ mWifiNative.setBluetoothCoexistenceMode(
+ mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
- mPowerMode = WifiNative.getPowerModeCommand();
+ mPowerMode = mWifiNative.getPowerMode();
if (mPowerMode < 0) {
// Handle the case where supplicant driver does not support
// getPowerModeCommand.
mPowerMode = WifiStateMachine.POWER_MODE_AUTO;
}
if (mPowerMode != WifiStateMachine.POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(WifiStateMachine.POWER_MODE_ACTIVE);
+ mWifiNative.setPowerMode(WifiStateMachine.POWER_MODE_ACTIVE);
}
}
void handlePostDhcpSetup() {
/* restore power mode */
- WifiNative.setPowerModeCommand(mPowerMode);
+ mWifiNative.setPowerMode(mPowerMode);
// Set the coexistence mode back to its default value
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ mWifiNative.setBluetoothCoexistenceMode(
+ mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
}
private void handleSuccessfulIpConfiguration(DhcpInfoInternal dhcpInfoInternal) {
@@ -1714,13 +1710,13 @@ public class WifiStateMachine extends StateMachine {
}
mLastSignalLevel = -1; // force update of signal strength
mReconnectCount = 0; //Reset IP failure tracking
- WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
+ mWifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
mWifiInfo.setInetAddress(addr);
if (getNetworkDetailedState() == DetailedState.CONNECTED) {
//DHCP renewal in connected state
LinkProperties linkProperties = dhcpInfoInternal.makeLinkProperties();
- linkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+ linkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
linkProperties.setInterfaceName(mInterfaceName);
if (!linkProperties.equals(mLinkProperties)) {
if (DBG) {
@@ -1732,8 +1728,6 @@ public class WifiStateMachine extends StateMachine {
}
} else {
configureLinkProperties();
- setNetworkDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
}
}
@@ -1750,7 +1744,7 @@ public class WifiStateMachine extends StateMachine {
if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
loge("Failed " +
mReconnectCount + " times, Disabling " + mLastNetworkId);
- WifiConfigStore.disableNetwork(mLastNetworkId,
+ mWifiConfigStore.disableNetwork(mLastNetworkId,
WifiConfiguration.DISABLED_DHCP_FAILURE);
mReconnectCount = 0;
}
@@ -1758,8 +1752,8 @@ public class WifiStateMachine extends StateMachine {
/* DHCP times out after about 30 seconds, we do a
* disconnect and an immediate reconnect to try again
*/
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
+ mWifiNative.disconnect();
+ mWifiNative.reconnect();
}
/* Current design is to not set the config on a running hostapd but instead
@@ -1819,11 +1813,14 @@ public class WifiStateMachine extends StateMachine {
/* Synchronous call returns */
case CMD_PING_SUPPLICANT:
case CMD_ENABLE_NETWORK:
- case CMD_DISABLE_NETWORK:
case CMD_ADD_OR_UPDATE_NETWORK:
case CMD_REMOVE_NETWORK:
case CMD_SAVE_CONFIG:
- mReplyChannel.replyToMessage(message, message.what, FAILURE);
+ replyToMessage(message, message.what, FAILURE);
+ break;
+ case CMD_GET_CONFIGURED_NETWORKS:
+ replyToMessage(message, message.what,
+ mWifiConfigStore.getConfiguredNetworks());
break;
case CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
@@ -1831,6 +1828,9 @@ public class WifiStateMachine extends StateMachine {
case CMD_ENABLE_BACKGROUND_SCAN:
mEnableBackgroundScan = (message.arg1 == 1);
break;
+ case CMD_SET_HIGH_PERF_MODE:
+ mHighPerfMode = (message.arg1 == 1);
+ break;
/* Discard */
case CMD_LOAD_DRIVER:
case CMD_UNLOAD_DRIVER:
@@ -1862,12 +1862,8 @@ public class WifiStateMachine extends StateMachine {
case CMD_CLEAR_BLACKLIST:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
- case CMD_CONNECT_NETWORK:
- case CMD_SAVE_NETWORK:
- case CMD_FORGET_NETWORK:
case CMD_RSSI_POLL:
case CMD_ENABLE_ALL_NETWORKS:
case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
@@ -1877,20 +1873,43 @@ public class WifiStateMachine extends StateMachine {
case CMD_SET_AP_CONFIG_COMPLETED:
case CMD_REQUEST_AP_CONFIG:
case CMD_RESPONSE_AP_CONFIG:
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
+ case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
+ break;
+ case CMD_SET_SUSPEND_OPTIMIZATIONS:
+ mSuspendWakeLock.release();
break;
case WifiMonitor.DRIVER_HUNG_EVENT:
setWifiEnabled(false);
setWifiEnabled(true);
break;
- case CMD_START_WPS:
- /* Return failure when the state machine cannot handle WPS initiation*/
- mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
- new WpsResult(Status.FAILURE));
+ case WifiManager.CONNECT_NETWORK:
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.BUSY);
break;
- case WifiP2pService.P2P_ENABLE_PENDING:
- // turn off wifi and defer to be handled in DriverUnloadedState
- setWifiEnabled(false);
- deferMessage(message);
+ case WifiManager.FORGET_NETWORK:
+ replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+ WifiManager.BUSY);
+ break;
+ case WifiManager.SAVE_NETWORK:
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.BUSY);
+ break;
+ case WifiManager.START_WPS:
+ replyToMessage(message, WifiManager.WPS_FAILED,
+ WifiManager.BUSY);
+ break;
+ case WifiManager.CANCEL_WPS:
+ replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
+ WifiManager.BUSY);
+ break;
+ case WifiManager.DISABLE_NETWORK:
+ replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
+ WifiManager.BUSY);
+ break;
+ case WifiWatchdogStateMachine.RSSI_FETCH:
+ replyToMessage(message, WifiWatchdogStateMachine.RSSI_FETCH_FAILED);
break;
default:
loge("Error! unhandled message" + message);
@@ -1910,7 +1929,7 @@ public class WifiStateMachine extends StateMachine {
// 50021 wifi_state_changed (custom|1|5)
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- if (WifiNative.isDriverLoaded()) {
+ if (mWifiNative.isDriverLoaded()) {
transitionTo(mDriverLoadedState);
}
else {
@@ -1963,7 +1982,7 @@ public class WifiStateMachine extends StateMachine {
break;
}
- if(WifiNative.loadDriver()) {
+ if(mWifiNative.loadDriver()) {
if (DBG) log("Driver load successful");
sendMessage(CMD_LOAD_DRIVER_SUCCESS);
} else {
@@ -2003,7 +2022,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2013,7 +2031,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2051,7 +2068,7 @@ public class WifiStateMachine extends StateMachine {
loge("Unable to change interface settings: " + ie);
}
- if(WifiNative.startSupplicant()) {
+ if(mWifiNative.startSupplicant(mP2pSupported)) {
if (DBG) log("Supplicant start successful");
mWifiMonitor.startMonitoring();
transitionTo(mSupplicantStartingState);
@@ -2066,7 +2083,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2083,7 +2099,7 @@ public class WifiStateMachine extends StateMachine {
public void run() {
if (DBG) log(getName() + message.toString() + "\n");
mWakeLock.acquire();
- if(WifiNative.unloadDriver()) {
+ if(mWifiNative.unloadDriver()) {
if (DBG) log("Driver unload successful");
sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
@@ -2137,7 +2153,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2147,7 +2162,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2163,16 +2177,11 @@ public class WifiStateMachine extends StateMachine {
if (DBG) log(getName() + message.toString() + "\n");
switch (message.what) {
case CMD_LOAD_DRIVER:
- mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
- transitionTo(mWaitForP2pDisableState);
- break;
- case WifiP2pService.P2P_ENABLE_PENDING:
- mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
+ transitionTo(mDriverLoadingState);
break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2208,15 +2217,14 @@ public class WifiStateMachine extends StateMachine {
/* Reset the supplicant state to indicate the supplicant
* state is not known at this time */
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
/* Initialize data structures */
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
- mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+ mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
- WifiConfigStore.initialize(mContext);
+ mWifiConfigStore.initialize();
sendSupplicantConnectionChangedBroadcast(true);
transitionTo(mDriverStartedState);
@@ -2224,7 +2232,7 @@ public class WifiStateMachine extends StateMachine {
case WifiMonitor.SUP_DISCONNECTION_EVENT:
if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
loge("Failed to setup control channel, restart supplicant");
- WifiNative.killSupplicant();
+ mWifiNative.killSupplicant();
transitionTo(mDriverLoadedState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
} else {
@@ -2245,7 +2253,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2255,7 +2262,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2273,72 +2279,74 @@ public class WifiStateMachine extends StateMachine {
long supplicantScanIntervalMs = Settings.Secure.getLong(mContext.getContentResolver(),
Settings.Secure.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
mDefaultSupplicantScanIntervalMs);
- WifiNative.setScanIntervalCommand((int)supplicantScanIntervalMs / 1000);
+ mWifiNative.setScanInterval((int)supplicantScanIntervalMs / 1000);
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
WifiConfiguration config;
- boolean eventLoggingEnabled = true;
switch(message.what) {
case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
transitionTo(mSupplicantStoppingState);
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
loge("Connection lost, restart supplicant");
- WifiNative.killSupplicant();
- WifiNative.closeSupplicantConnection();
+ mWifiNative.killSupplicant();
+ mWifiNative.closeSupplicantConnection();
mNetworkInfo.setIsAvailable(false);
handleNetworkDisconnect();
sendSupplicantConnectionChangedBroadcast(false);
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
transitionTo(mDriverLoadedState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
- eventLoggingEnabled = false;
- setScanResults(WifiNative.scanResultsCommand());
+ setScanResults(mWifiNative.scanResults());
sendScanResultsAvailableBroadcast();
mScanResultIsPending = false;
break;
case CMD_PING_SUPPLICANT:
- boolean ok = WifiNative.pingCommand();
- mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ boolean ok = mWifiNative.ping();
+ replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_ADD_OR_UPDATE_NETWORK:
config = (WifiConfiguration) message.obj;
- mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
- WifiConfigStore.addOrUpdateNetwork(config));
+ replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+ mWifiConfigStore.addOrUpdateNetwork(config));
break;
case CMD_REMOVE_NETWORK:
- ok = WifiConfigStore.removeNetwork(message.arg1);
- mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ ok = mWifiConfigStore.removeNetwork(message.arg1);
+ replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_ENABLE_NETWORK:
- ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
- mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+ replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_ENABLE_ALL_NETWORKS:
long time = android.os.SystemClock.elapsedRealtime();
if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
- WifiConfigStore.enableAllNetworks();
+ mWifiConfigStore.enableAllNetworks();
mLastEnableAllNetworksTime = time;
}
break;
- case CMD_DISABLE_NETWORK:
- ok = WifiConfigStore.disableNetwork(message.arg1, message.arg2);
- mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ case WifiManager.DISABLE_NETWORK:
+ if (mWifiConfigStore.disableNetwork(message.arg1,
+ WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
+ replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
+ WifiManager.ERROR);
+ }
break;
case CMD_BLACKLIST_NETWORK:
- WifiNative.addToBlacklistCommand((String)message.obj);
+ mWifiNative.addToBlacklist((String)message.obj);
break;
case CMD_CLEAR_BLACKLIST:
- WifiNative.clearBlacklistCommand();
+ mWifiNative.clearBlacklist();
break;
case CMD_SAVE_CONFIG:
- ok = WifiConfigStore.saveConfig();
- mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+ ok = mWifiConfigStore.saveConfig();
+ replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
// Inform the backup manager about a data change
IBackupManager ibm = IBackupManager.Stub.asInterface(
@@ -2359,19 +2367,29 @@ public class WifiStateMachine extends StateMachine {
case CMD_SET_SCAN_MODE:
mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
break;
- case CMD_SAVE_NETWORK:
+ case WifiManager.SAVE_NETWORK:
config = (WifiConfiguration) message.obj;
- WifiConfigStore.saveNetwork(config);
+ NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
+ if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+ replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+ } else {
+ loge("Failed to save network");
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.ERROR);
+ }
break;
- case CMD_FORGET_NETWORK:
- WifiConfigStore.forgetNetwork(message.arg1);
+ case WifiManager.FORGET_NETWORK:
+ if (mWifiConfigStore.forgetNetwork(message.arg1)) {
+ replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
+ } else {
+ loge("Failed to forget network");
+ replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+ WifiManager.ERROR);
+ }
break;
default:
return NOT_HANDLED;
}
- if (eventLoggingEnabled) {
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- }
return HANDLED;
}
@@ -2386,8 +2404,12 @@ public class WifiStateMachine extends StateMachine {
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ /* Send any reset commands to supplicant before shutting it down */
+ handleNetworkDisconnect();
+
if (DBG) log("stopping supplicant");
- if (!WifiNative.stopSupplicant()) {
+ if (!mWifiNative.stopSupplicant()) {
loge("Failed to stop supplicant");
}
@@ -2396,11 +2418,9 @@ public class WifiStateMachine extends StateMachine {
++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
mNetworkInfo.setIsAvailable(false);
- handleNetworkDisconnect();
setWifiState(WIFI_STATE_DISABLING);
sendSupplicantConnectionChangedBroadcast(false);
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
}
@Override
public boolean processMessage(Message message) {
@@ -2414,15 +2434,15 @@ public class WifiStateMachine extends StateMachine {
/* Socket connection can be lost when we do a graceful shutdown
* or when the driver is hung. Ensure supplicant is stopped here.
*/
- WifiNative.killSupplicant();
- WifiNative.closeSupplicantConnection();
+ mWifiNative.killSupplicant();
+ mWifiNative.closeSupplicantConnection();
transitionTo(mDriverLoadedState);
break;
case CMD_STOP_SUPPLICANT_FAILED:
if (message.arg1 == mSupplicantStopFailureToken) {
loge("Timed out on a supplicant stop, kill and proceed");
- WifiNative.killSupplicant();
- WifiNative.closeSupplicantConnection();
+ mWifiNative.killSupplicant();
+ mWifiNative.closeSupplicantConnection();
transitionTo(mDriverLoadedState);
}
break;
@@ -2436,7 +2456,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2446,7 +2465,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2479,7 +2497,6 @@ public class WifiStateMachine extends StateMachine {
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
case WifiMonitor.WPS_OVERLAP_EVENT:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2493,7 +2510,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2513,7 +2529,7 @@ public class WifiStateMachine extends StateMachine {
* When this mode is on, some of the low-level scan parameters used by the
* driver are changed to reduce interference with bluetooth
*/
- WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
+ mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
/* set country code */
setCountryCode();
/* set frequency band of operation */
@@ -2522,56 +2538,63 @@ public class WifiStateMachine extends StateMachine {
setNetworkDetailedState(DetailedState.DISCONNECTED);
/* Remove any filtering on Multicast v6 at start */
- WifiNative.stopFilteringMulticastV6Packets();
+ mWifiNative.stopFilteringMulticastV6Packets();
/* Reset Multicast v4 filtering state */
if (mFilteringMulticastV4Packets.get()) {
- WifiNative.startFilteringMulticastV4Packets();
+ mWifiNative.startFilteringMulticastV4Packets();
} else {
- WifiNative.stopFilteringMulticastV4Packets();
+ mWifiNative.stopFilteringMulticastV4Packets();
}
if (mIsScanMode) {
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.disconnectCommand();
+ mWifiNative.setScanResultHandling(SCAN_ONLY_MODE);
+ mWifiNative.disconnect();
transitionTo(mScanModeState);
} else {
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- WifiNative.reconnectCommand();
+ mWifiNative.setScanResultHandling(CONNECT_MODE);
+ mWifiNative.reconnect();
+ // Status pulls in the current supplicant state and network connection state
+ // events over the monitor connection. This helps framework sync up with
+ // current supplicant state
+ mWifiNative.status();
transitionTo(mDisconnectedState);
}
+
+ if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
+
+ mContext.registerReceiver(mScreenReceiver, mScreenFilter);
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
- boolean eventLoggingEnabled = true;
switch(message.what) {
- case CMD_SET_SCAN_TYPE:
- if (message.arg1 == SCAN_ACTIVE) {
- WifiNative.setScanModeCommand(true);
- } else {
- WifiNative.setScanModeCommand(false);
- }
+ case CMD_SET_SCAN_TYPE:
+ mSetScanActive = (message.arg1 == SCAN_ACTIVE);
+ mWifiNative.setScanMode(mSetScanActive);
break;
case CMD_START_SCAN:
- eventLoggingEnabled = false;
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ boolean forceActive = (message.arg1 == SCAN_ACTIVE);
+ if (forceActive && !mSetScanActive) {
+ mWifiNative.setScanMode(forceActive);
+ }
+ mWifiNative.scan();
+ if (forceActive && !mSetScanActive) {
+ mWifiNative.setScanMode(mSetScanActive);
+ }
mScanResultIsPending = true;
break;
- case CMD_SET_HIGH_PERF_MODE:
- setHighPerfModeEnabledNative(message.arg1 == 1);
- break;
case CMD_SET_COUNTRY_CODE:
String country = (String) message.obj;
if (DBG) log("set country code " + country);
- if (!WifiNative.setCountryCodeCommand(country.toUpperCase())) {
+ if (!mWifiNative.setCountryCode(country.toUpperCase())) {
loge("Failed to set country code " + country);
}
break;
case CMD_SET_FREQUENCY_BAND:
int band = message.arg1;
if (DBG) log("set frequency band " + band);
- if (WifiNative.setBandCommand(band)) {
+ if (mWifiNative.setBand(band)) {
mFrequencyBand.set(band);
//Fetch the latest scan results when frequency band is set
startScan(true);
@@ -2582,7 +2605,7 @@ public class WifiStateMachine extends StateMachine {
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive = (message.arg1 !=
BluetoothAdapter.STATE_DISCONNECTED);
- WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
+ mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
break;
case CMD_STOP_DRIVER:
int mode = message.arg1;
@@ -2615,38 +2638,51 @@ public class WifiStateMachine extends StateMachine {
case CMD_DELAYED_STOP_DRIVER:
if (message.arg1 != mDelayedStopCounter) break;
if (getCurrentState() != mDisconnectedState) {
- WifiNative.disconnectCommand();
+ mWifiNative.disconnect();
handleNetworkDisconnect();
}
mWakeLock.acquire();
- WifiNative.stopDriverCommand();
+ mWifiNative.stopDriver();
transitionTo(mDriverStoppingState);
mWakeLock.release();
break;
case CMD_START_PACKET_FILTERING:
if (message.arg1 == MULTICAST_V6) {
- WifiNative.startFilteringMulticastV6Packets();
+ mWifiNative.startFilteringMulticastV6Packets();
} else if (message.arg1 == MULTICAST_V4) {
- WifiNative.startFilteringMulticastV4Packets();
+ mWifiNative.startFilteringMulticastV4Packets();
} else {
loge("Illegal arugments to CMD_START_PACKET_FILTERING");
}
break;
case CMD_STOP_PACKET_FILTERING:
if (message.arg1 == MULTICAST_V6) {
- WifiNative.stopFilteringMulticastV6Packets();
+ mWifiNative.stopFilteringMulticastV6Packets();
} else if (message.arg1 == MULTICAST_V4) {
- WifiNative.stopFilteringMulticastV4Packets();
+ mWifiNative.stopFilteringMulticastV4Packets();
} else {
loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
}
break;
+ case CMD_SET_SUSPEND_OPTIMIZATIONS:
+ if (!mHighPerfMode) {
+ mWifiNative.setSuspendOptimizations(true);
+ }
+ mSuspendWakeLock.release();
+ break;
+ case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
+ mWifiNative.setSuspendOptimizations(false);
+ break;
+ case CMD_SET_HIGH_PERF_MODE:
+ mHighPerfMode = (message.arg1 == 1);
+ if (mHighPerfMode) {
+ //Disable any suspend optimizations
+ mWifiNative.setSuspendOptimizations(false);
+ }
+ break;
default:
return NOT_HANDLED;
}
- if (eventLoggingEnabled) {
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- }
return HANDLED;
}
@Override
@@ -2655,6 +2691,9 @@ public class WifiStateMachine extends StateMachine {
mIsRunning = false;
updateBatteryWorkSource(null);
mScanResults = null;
+
+ if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
+ mContext.unregisterReceiver(mScreenReceiver);
}
}
@@ -2678,7 +2717,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
@@ -2692,7 +2730,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2718,14 +2755,13 @@ public class WifiStateMachine extends StateMachine {
break;
case CMD_START_DRIVER:
mWakeLock.acquire();
- WifiNative.startDriverCommand();
+ mWifiNative.startDriver();
mWakeLock.release();
transitionTo(mDriverStartingState);
break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2745,8 +2781,8 @@ public class WifiStateMachine extends StateMachine {
/* Ignore */
return HANDLED;
} else {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- WifiNative.reconnectCommand();
+ mWifiNative.setScanResultHandling(message.arg1);
+ mWifiNative.reconnect();
mIsScanMode = false;
transitionTo(mDisconnectedState);
}
@@ -2762,7 +2798,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -2781,10 +2816,6 @@ public class WifiStateMachine extends StateMachine {
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
break;
- case WifiMonitor.WPS_OVERLAP_EVENT:
- /* We just need to broadcast the error */
- sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
- break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
// Due to a WEXT bug, during the time of driver start/stop
@@ -2813,15 +2844,15 @@ public class WifiStateMachine extends StateMachine {
break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
- WifiNative.disconnectCommand();
+ mWifiNative.disconnect();
break;
case CMD_RECONNECT:
- WifiNative.reconnectCommand();
+ mWifiNative.reconnect();
break;
case CMD_REASSOCIATE:
- WifiNative.reassociateCommand();
+ mWifiNative.reassociate();
break;
- case CMD_CONNECT_NETWORK:
+ case WifiManager.CONNECT_NETWORK:
int netId = message.arg1;
WifiConfiguration config = (WifiConfiguration) message.obj;
@@ -2833,29 +2864,54 @@ public class WifiStateMachine extends StateMachine {
* a connection to the enabled network.
*/
if (config != null) {
- netId = WifiConfigStore.selectNetwork(config);
+ netId = mWifiConfigStore.selectNetwork(config);
} else {
- WifiConfigStore.selectNetwork(netId);
+ mWifiConfigStore.selectNetwork(netId);
}
/* The state tracker handles enabling networks upon completion/failure */
- mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);
+ mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
+
+ if (mWifiNative.reconnect()) {
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
+ } else {
+ loge("Failed to initiate connection");
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.ERROR);
+ }
- WifiNative.reconnectCommand();
- mLastExplicitNetworkId = netId;
- mLastNetworkChoiceTime = SystemClock.elapsedRealtime();
- mNextWifiActionExplicit = true;
- if (DBG) log("Setting wifi connect explicit for netid " + netId);
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
break;
- case CMD_START_WPS:
- mWpsStateMachine.sendMessage(Message.obtain(message));
- transitionTo(mWaitForWpsCompletionState);
+ case WifiManager.START_WPS:
+ WpsInfo wpsInfo = (WpsInfo) message.obj;
+ WpsResult result;
+ switch (wpsInfo.setup) {
+ case WpsInfo.PBC:
+ result = mWifiConfigStore.startWpsPbc(wpsInfo);
+ break;
+ case WpsInfo.KEYPAD:
+ result = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
+ break;
+ case WpsInfo.DISPLAY:
+ result = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
+ break;
+ default:
+ result = new WpsResult(Status.FAILURE);
+ Log.e(TAG, "Invalid setup for WPS");
+ break;
+ }
+ if (result.status == Status.SUCCESS) {
+ replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, result);
+ transitionTo(mWpsRunningState);
+ } else {
+ Log.e(TAG, "Failed to start WPS with config " + wpsInfo.toString());
+ replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
+ }
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
/* Set the scan setting back to "connect" mode */
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ mWifiNative.setScanResultHandling(CONNECT_MODE);
/* Handle scan results */
return NOT_HANDLED;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
@@ -2863,21 +2919,12 @@ public class WifiStateMachine extends StateMachine {
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
- //TODO: make supplicant modification to push this in events
- mWifiInfo.setSSID(fetchSSID());
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
- if (mNextWifiActionExplicit &&
- mWifiInfo.getNetworkId() == mLastExplicitNetworkId &&
- SystemClock.elapsedRealtime() < mLastNetworkChoiceTime +
- EXPLICIT_CONNECT_ALLOWED_DELAY_MS) {
- mWifiInfo.setExplicitConnect(true);
- }
- mNextWifiActionExplicit = false;
/* send event to CM & network change broadcast */
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectingState);
+ transitionTo(mObtainingIpState);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (DBG) log("Network connection lost");
@@ -2887,131 +2934,24 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
- class ConnectingState extends State {
-
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- try {
- mNwService.enableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Failed to enable IPv6: " + re);
- } catch (IllegalStateException e) {
- loge("Failed to enable IPv6: " + e);
- }
-
- if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- //start DHCP
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
- mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
- } else {
- DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration(
- mLastNetworkId);
- InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.addr = dhcpInfoInternal.makeLinkAddress();
- ifcg.interfaceFlags = "[up]";
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) log("Static IP configuration succeeded");
- sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
- } catch (RemoteException re) {
- loge("Static IP configuration failed: " + re);
- sendMessage(CMD_STATIC_IP_FAILURE);
- } catch (IllegalStateException e) {
- loge("Static IP configuration failed: " + e);
- sendMessage(CMD_STATIC_IP_FAILURE);
- }
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
-
- switch(message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
- handlePreDhcpSetup();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
- break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
- handlePostDhcpSetup();
- if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
- handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
- transitionTo(mConnectedState);
- } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
- handleFailedIpConfiguration();
- transitionTo(mDisconnectingState);
- }
- break;
- case CMD_STATIC_IP_SUCCESS:
- handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
- transitionTo(mConnectedState);
- break;
- case CMD_STATIC_IP_FAILURE:
- handleFailedIpConfiguration();
- transitionTo(mDisconnectingState);
- break;
- case CMD_DISCONNECT:
- WifiNative.disconnectCommand();
- transitionTo(mDisconnectingState);
- break;
- /* Ignore connection to same network */
- case CMD_CONNECT_NETWORK:
- int netId = message.arg1;
- if (mWifiInfo.getNetworkId() == netId) {
- break;
- }
- return NOT_HANDLED;
- case CMD_SAVE_NETWORK:
- deferMessage(message);
- break;
- /* Ignore */
- case WifiMonitor.NETWORK_CONNECTION_EVENT:
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- /* Defer scan when IP is being fetched */
- case CMD_START_SCAN:
- deferMessage(message);
- break;
- /* Defer any power mode changes since we must keep active power mode at DHCP */
- case CMD_SET_HIGH_PERF_MODE:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectedState extends State {
+ class L2ConnectedState extends State {
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
mRssiPollToken++;
if (mEnableRssiPolling) {
- sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
+ sendMessage(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0));
}
}
+
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
- boolean eventLoggingEnabled = true;
switch (message.what) {
case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
handlePreDhcpSetup();
@@ -3020,14 +2960,17 @@ public class WifiStateMachine extends StateMachine {
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+ if (DBG) log("DHCP successful");
handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+ transitionTo(mVerifyingLinkState);
} else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+ if (DBG) log("DHCP failed");
handleFailedIpConfiguration();
transitionTo(mDisconnectingState);
}
break;
case CMD_DISCONNECT:
- WifiNative.disconnectCommand();
+ mWifiNative.disconnect();
transitionTo(mDisconnectingState);
break;
case CMD_SET_SCAN_MODE:
@@ -3037,29 +2980,28 @@ public class WifiStateMachine extends StateMachine {
}
break;
case CMD_START_SCAN:
- eventLoggingEnabled = false;
/* When the network is connected, re-scanning can trigger
* a reconnection. Put it in scan-only mode during scan.
* When scan results are received, the mode is switched
* back to CONNECT_MODE.
*/
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ mWifiNative.setScanResultHandling(SCAN_ONLY_MODE);
/* Have the parent state handle the rest */
return NOT_HANDLED;
/* Ignore connection to same network */
- case CMD_CONNECT_NETWORK:
+ case WifiManager.CONNECT_NETWORK:
int netId = message.arg1;
if (mWifiInfo.getNetworkId() == netId) {
break;
}
return NOT_HANDLED;
- case CMD_SAVE_NETWORK:
+ case WifiManager.SAVE_NETWORK:
WifiConfiguration config = (WifiConfiguration) message.obj;
- NetworkUpdateResult result = WifiConfigStore.saveNetwork(config);
+ NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
if (result.hasIpChanged()) {
log("Reconfiguring IP on connection");
- transitionTo(mConnectingState);
+ transitionTo(mObtainingIpState);
}
if (result.hasProxyChanged()) {
log("Reconfiguring proxy on connection");
@@ -3067,16 +3009,23 @@ public class WifiStateMachine extends StateMachine {
sendLinkConfigurationChangedBroadcast();
}
}
+
+ if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+ replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+ } else {
+ loge("Failed to save network");
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.ERROR);
+ }
break;
/* Ignore */
case WifiMonitor.NETWORK_CONNECTION_EVENT:
break;
case CMD_RSSI_POLL:
- eventLoggingEnabled = false;
if (message.arg1 == mRssiPollToken) {
// Get Info and continue polling
fetchRssiAndLinkSpeedNative();
- sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
} else {
// Polling has completed
@@ -3088,34 +3037,165 @@ public class WifiStateMachine extends StateMachine {
if (mEnableRssiPolling) {
// first poll
fetchRssiAndLinkSpeedNative();
- sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
}
break;
+ case WifiWatchdogStateMachine.RSSI_FETCH:
+ fetchRssiAndLinkSpeedNative();
+ replyToMessage(message, WifiWatchdogStateMachine.RSSI_FETCH_SUCCEEDED,
+ mWifiInfo.getRssi());
+ break;
default:
return NOT_HANDLED;
}
- if (eventLoggingEnabled) {
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- }
+
return HANDLED;
}
+
@Override
public void exit() {
-
- /* Request a CS wakelock during transition to mobile */
- checkAndSetConnectivityInstance();
- mCm.requestNetworkTransitionWakelock(TAG);
-
/* If a scan result is pending in connected state, the supplicant
* is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
*/
if (mScanResultIsPending) {
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ mWifiNative.setScanResultHandling(CONNECT_MODE);
}
}
}
+ class ObtainingIpState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+ //start DHCP
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+ mDhcpStateMachine.registerForPreDhcpNotification();
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ } else {
+ DhcpInfoInternal dhcpInfoInternal = mWifiConfigStore.getIpConfiguration(
+ mLastNetworkId);
+ InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(dhcpInfoInternal.makeLinkAddress());
+ ifcg.setInterfaceUp();
+ try {
+ mNwService.setInterfaceConfig(mInterfaceName, ifcg);
+ if (DBG) log("Static IP configuration succeeded");
+ sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
+ } catch (RemoteException re) {
+ loge("Static IP configuration failed: " + re);
+ sendMessage(CMD_STATIC_IP_FAILURE);
+ } catch (IllegalStateException e) {
+ loge("Static IP configuration failed: " + e);
+ sendMessage(CMD_STATIC_IP_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STATIC_IP_SUCCESS:
+ handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+ transitionTo(mVerifyingLinkState);
+ break;
+ case CMD_STATIC_IP_FAILURE:
+ handleFailedIpConfiguration();
+ transitionTo(mDisconnectingState);
+ break;
+ case WifiManager.SAVE_NETWORK:
+ deferMessage(message);
+ break;
+ /* Defer any power mode changes since we must keep active power mode at DHCP */
+ case CMD_SET_HIGH_PERF_MODE:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class VerifyingLinkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ //stay here
+ break;
+ case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
+ try {
+ mNwService.enableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Failed to enable IPv6: " + re);
+ } catch (IllegalStateException e) {
+ loge("Failed to enable IPv6: " + e);
+ }
+
+ setNetworkDetailedState(DetailedState.CONNECTED);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mConnectedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class ConnectedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ if (DBG) log("Watchdog reports poor link");
+ try {
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Failed to disable IPv6: " + re);
+ } catch (IllegalStateException e) {
+ loge("Failed to disable IPv6: " + e);
+ }
+ /* Report a disconnect */
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ transitionTo(mVerifyingLinkState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ /* Request a CS wakelock during transition to mobile */
+ checkAndSetConnectivityInstance();
+ mCm.requestNetworkTransitionWakelock(TAG);
+ }
+ }
+
class DisconnectingState extends State {
@Override
public void enter() {
@@ -3143,7 +3223,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -3192,7 +3271,7 @@ public class WifiStateMachine extends StateMachine {
* cleared
*/
if (!mScanResultIsPending) {
- WifiNative.enableBackgroundScanCommand(true);
+ mWifiNative.enableBackgroundScan(true);
}
} else {
setScanAlarm(true);
@@ -3204,9 +3283,9 @@ public class WifiStateMachine extends StateMachine {
switch (message.what) {
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
- WifiNative.setScanResultHandlingCommand(message.arg1);
+ mWifiNative.setScanResultHandling(message.arg1);
//Supplicant disconnect to prevent further connects
- WifiNative.disconnectCommand();
+ mWifiNative.disconnect();
mIsScanMode = true;
transitionTo(mScanModeState);
}
@@ -3214,10 +3293,10 @@ public class WifiStateMachine extends StateMachine {
case CMD_ENABLE_BACKGROUND_SCAN:
mEnableBackgroundScan = (message.arg1 == 1);
if (mEnableBackgroundScan) {
- WifiNative.enableBackgroundScanCommand(true);
+ mWifiNative.enableBackgroundScan(true);
setScanAlarm(false);
} else {
- WifiNative.enableBackgroundScanCommand(false);
+ mWifiNative.enableBackgroundScan(false);
setScanAlarm(true);
}
break;
@@ -3232,21 +3311,20 @@ public class WifiStateMachine extends StateMachine {
case CMD_START_SCAN:
/* Disable background scan temporarily during a regular scan */
if (mEnableBackgroundScan) {
- WifiNative.enableBackgroundScanCommand(false);
+ mWifiNative.enableBackgroundScan(false);
}
/* Handled in parent state */
return NOT_HANDLED;
case WifiMonitor.SCAN_RESULTS_EVENT:
/* Re-enable background scan when a pending scan result is received */
if (mEnableBackgroundScan && mScanResultIsPending) {
- WifiNative.enableBackgroundScanCommand(true);
+ mWifiNative.enableBackgroundScan(true);
}
/* Handled in parent state */
return NOT_HANDLED;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
@@ -3254,49 +3332,95 @@ public class WifiStateMachine extends StateMachine {
public void exit() {
/* No need for a background scan upon exit from a disconnected state */
if (mEnableBackgroundScan) {
- WifiNative.enableBackgroundScanCommand(false);
+ mWifiNative.enableBackgroundScan(false);
}
setScanAlarm(false);
}
}
- class WaitForWpsCompletionState extends State {
+ class WpsRunningState extends State {
+ //Tracks the source to provide a reply
+ private Message mSourceMessage;
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ mSourceMessage = Message.obtain(getCurrentMessage());
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
switch (message.what) {
+ case WifiMonitor.WPS_SUCCESS_EVENT:
+ replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
+ mSourceMessage.recycle();
+ mSourceMessage = null;
+ transitionTo(mDisconnectedState);
+ break;
+ case WifiMonitor.WPS_OVERLAP_EVENT:
+ replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
+ WifiManager.WPS_OVERLAP_ERROR);
+ mSourceMessage.recycle();
+ mSourceMessage = null;
+ transitionTo(mDisconnectedState);
+ break;
+ case WifiMonitor.WPS_FAIL_EVENT:
+ //arg1 has the reason for the failure
+ replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
+ mSourceMessage.recycle();
+ mSourceMessage = null;
+ transitionTo(mDisconnectedState);
+ break;
+ case WifiMonitor.WPS_TIMEOUT_EVENT:
+ replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
+ WifiManager.WPS_TIMED_OUT);
+ mSourceMessage.recycle();
+ mSourceMessage = null;
+ transitionTo(mDisconnectedState);
+ break;
+ case WifiManager.START_WPS:
+ replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
+ break;
+ case WifiManager.CANCEL_WPS:
+ if (mWifiNative.cancelWps()) {
+ replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
+ } else {
+ replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
+ }
+ transitionTo(mDisconnectedState);
+ break;
/* Defer all commands that can cause connections to a different network
* or put the state machine out of connect mode
*/
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
- case CMD_CONNECT_NETWORK:
+ case WifiManager.CONNECT_NETWORK:
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
+ case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after exiting WPS state */
deferMessage(message);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (DBG) log("Network connection lost");
handleNetworkDisconnect();
break;
- case WPS_COMPLETED_EVENT:
- /* we are still disconnected until we see a network connection
- * notification */
- transitionTo(mDisconnectedState);
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ //Throw away supplicant state changes when WPS is running.
+ //We will start getting supplicant state changes once we get
+ //a WPS success or failure
break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
+
+ @Override
+ public void exit() {
+ mWifiConfigStore.enableAllNetworks();
+ mWifiConfigStore.loadConfiguredNetworks();
+ }
}
class SoftApStartingState extends State {
@@ -3333,13 +3457,11 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
case CMD_TETHER_STATE_CHANGE:
- case WifiP2pService.P2P_ENABLE_PENDING:
deferMessage(message);
break;
case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
@@ -3362,7 +3484,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -3403,59 +3524,9 @@ public class WifiStateMachine extends StateMachine {
transitionTo(mTetheringState);
}
break;
- case WifiP2pService.P2P_ENABLE_PENDING:
- // turn of soft Ap and defer to be handled in DriverUnloadedState
- setWifiApEnabled(null, false);
- deferMessage(message);
- break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class WaitForP2pDisableState extends State {
- private int mSavedArg;
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- //Preserve the argument arg1 that has information used in DriverLoadingState
- mSavedArg = getCurrentMessage().arg1;
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch(message.what) {
- case WifiP2pService.WIFI_ENABLE_PROCEED:
- //restore argument from original message (CMD_LOAD_DRIVER)
- message.arg1 = mSavedArg;
- transitionTo(mDriverLoadingState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
- case CMD_SET_COUNTRY_CODE:
- case CMD_SET_FREQUENCY_BAND:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -3496,18 +3567,15 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
- case WifiP2pService.P2P_ENABLE_PENDING:
deferMessage(message);
break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -3538,7 +3606,6 @@ public class WifiStateMachine extends StateMachine {
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
@@ -3592,22 +3659,56 @@ public class WifiStateMachine extends StateMachine {
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
- case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
case CMD_SET_FREQUENCY_BAND:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
- case WifiP2pService.P2P_ENABLE_PENDING:
deferMessage(message);
break;
default:
return NOT_HANDLED;
}
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
+ //State machine initiated requests can have replyTo set to null indicating
+ //there are no recepients, we ignore those reply actions
+ private void replyToMessage(Message msg, int what) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessageWithArg2(msg);
+ dstMsg.what = what;
+ mReplyChannel.replyToMessage(msg, dstMsg);
+ }
+
+ private void replyToMessage(Message msg, int what, int arg1) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessageWithArg2(msg);
+ dstMsg.what = what;
+ dstMsg.arg1 = arg1;
+ mReplyChannel.replyToMessage(msg, dstMsg);
+ }
+
+ private void replyToMessage(Message msg, int what, Object obj) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessageWithArg2(msg);
+ dstMsg.what = what;
+ dstMsg.obj = obj;
+ mReplyChannel.replyToMessage(msg, dstMsg);
+ }
+
+ /**
+ * arg2 on the source message has a unique id that needs to be retained in replies
+ * to match the request
+ *
+ * see WifiManager for details
+ */
+ private Message obtainMessageWithArg2(Message srcMsg) {
+ Message msg = Message.obtain();
+ msg.arg2 = srcMsg.arg2;
+ return msg;
+ }
+
private void log(String s) {
Log.d(TAG, s);
}
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index b27c60f..f31ee68 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -26,9 +26,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.arp.ArpPeer;
import android.net.ConnectivityManager;
-import android.net.DnsPinger;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.RouteInfo;
import android.net.Uri;
import android.os.Message;
import android.os.SystemClock;
@@ -38,6 +41,7 @@ import android.provider.Settings.Secure;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -46,49 +50,88 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
+import java.net.SocketException;
import java.net.URL;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
/**
- * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
- * network with multiple access points. After the framework successfully
- * connects to an access point, the watchdog verifies connectivity by 'pinging'
- * the configured DNS server using {@link DnsPinger}.
- * <p>
- * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
- * that another AP might have internet access; otherwise the SSID is disabled.
- * <p>
- * On DNS success, the WatchdogService initiates a walled garden check via an
- * http get. A browser window is activated if a walled garden is detected.
+ * WifiWatchdogStateMachine monitors the connection to a Wi-Fi
+ * network. After the framework notifies that it has connected to an
+ * acccess point and is waiting for link to be verified, the watchdog
+ * takes over and verifies if the link is good by doing ARP pings to
+ * the gateway using {@link ArpPeer}.
+ *
+ * Upon successful verification, the watchdog notifies and continues
+ * to monitor the link afterwards when the RSSI level falls below
+ * a certain threshold.
+
+ * When Wi-fi connects at L2 layer, the beacons from access point reach
+ * the device and it can maintain a connection, but the application
+ * connectivity can be flaky (due to bigger packet size exchange).
+ *
+ * We now monitor the quality of the last hop on
+ * Wi-Fi using signal strength and ARP connectivity as indicators
+ * to decide if the link is good enough to switch to Wi-Fi as the uplink.
+ *
+ * ARP pings are useful for link validation but can still get through
+ * when the application traffic fails to go through and are thus not
+ * the best indicator of real packet loss since they are tiny packets
+ * (28 bytes) and have a much low chance of packet corruption than the
+ * regular data packets.
+ *
+ * When signal strength and ARP are used together, it ends up working well in tests.
+ * The goal is to switch to Wi-Fi after validating ARP transfer
+ * and RSSI and then switching out of Wi-Fi when we hit a low
+ * signal strength threshold and then waiting until the signal strength
+ * improves and validating ARP transfer.
*
* @hide
*/
public class WifiWatchdogStateMachine extends StateMachine {
- private static final boolean DBG = false;
+ /* STOPSHIP: Keep this configurable for debugging until ship */
+ private static boolean DBG = false;
private static final String TAG = "WifiWatchdogStateMachine";
- private static final String DISABLED_NETWORK_NOTIFICATION_ID = "WifiWatchdog.networkdisabled";
private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden";
- private static final int WIFI_SIGNAL_LEVELS = 4;
- /**
- * Low signal is defined as less than or equal to cut off
- */
- private static final int LOW_SIGNAL_CUTOFF = 0;
-
- private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
- private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 60 * 60 * 1000;
+ /* RSSI Levels as used by notification icon
+ Level 4 -55 <= RSSI
+ Level 3 -66 <= RSSI < -55
+ Level 2 -77 <= RSSI < -67
+ Level 1 -88 <= RSSI < -78
+ Level 0 RSSI < -88 */
+
+ /* Wi-fi connection is considered poor below this
+ RSSI level threshold and the watchdog report it
+ to the WifiStateMachine */
+ private static final int RSSI_LEVEL_CUTOFF = 0;
+ /* Wi-fi connection is monitored actively below this
+ threshold */
+ private static final int RSSI_LEVEL_MONITOR = 1;
+ /* RSSI threshold during monitoring below which network is avoided */
+ private static final int RSSI_MONITOR_THRESHOLD = -84;
+ /* Number of times RSSI is measured to be low before being avoided */
+ private static final int RSSI_MONITOR_COUNT = 5;
+ private int mRssiMonitorCount = 0;
+
+ /* Avoid flapping. The interval is changed over time as long as we continue to avoid
+ * under the max interval after which we reset the interval again */
+ private static final int MIN_INTERVAL_AVOID_BSSID_MS[] = {0, 30 * 1000, 60 * 1000,
+ 5 * 60 * 1000, 30 * 60 * 1000};
+ /* Index into the interval array MIN_INTERVAL_AVOID_BSSID_MS */
+ private int mMinIntervalArrayIndex = 0;
+
+ private long mLastBssidAvoidedTime;
+
+ private int mCurrentSignalLevel;
+
+ private static final long DEFAULT_ARP_CHECK_INTERVAL_MS = 2 * 60 * 1000;
+ private static final long DEFAULT_RSSI_FETCH_INTERVAL_MS = 1000;
private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
- private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
- private static final int DEFAULT_NUM_DNS_PINGS = 5; // Multiple pings to detect setup issues
- private static final int DEFAULT_MIN_DNS_RESPONSES = 1;
-
- private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
+ private static final int DEFAULT_NUM_ARP_PINGS = 5;
+ private static final int DEFAULT_MIN_ARP_RESPONSES = 1;
- private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
+ private static final int DEFAULT_ARP_PING_TIMEOUT_MS = 100;
// See http://go/clientsdns for usage approval
private static final String DEFAULT_WALLED_GARDEN_URL =
@@ -102,10 +145,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
*/
private static final int WALLED_GARDEN_START_DELAY_MS = 3000;
- private static final int DNS_INTRATEST_PING_INTERVAL_MS = 200;
- /* With some router setups, it takes a few hunder milli-seconds before connection is active */
- private static final int DNS_START_DELAY_MS = 1000;
-
private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
/**
@@ -118,99 +157,84 @@ public class WifiWatchdogStateMachine extends StateMachine {
* which has a non-null networkInfo object
*/
private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
- /**
- * Indicates the signal has changed. Passed with arg1
- * {@link #mNetEventCounter} and arg2 [raw signal strength]
- */
+ /* Passed with RSSI information */
private static final int EVENT_RSSI_CHANGE = BASE + 3;
- private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4;
private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6;
- private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 100;
- private static final int MESSAGE_HANDLE_BAD_AP = BASE + 101;
- /**
- * arg1 == mOnlineWatchState.checkCount
- */
- private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 102;
- private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 103;
- private static final int MESSAGE_DELAYED_WALLED_GARDEN_CHECK = BASE + 104;
+ /* Internal messages */
+ private static final int CMD_ARP_CHECK = BASE + 11;
+ private static final int CMD_DELAYED_WALLED_GARDEN_CHECK = BASE + 12;
+ private static final int CMD_RSSI_FETCH = BASE + 13;
+
+ /* Notifications to WifiStateMachine */
+ static final int POOR_LINK_DETECTED = BASE + 21;
+ static final int GOOD_LINK_DETECTED = BASE + 22;
+ static final int RSSI_FETCH = BASE + 23;
+ static final int RSSI_FETCH_SUCCEEDED = BASE + 24;
+ static final int RSSI_FETCH_FAILED = BASE + 25;
+
+ private static final int SINGLE_ARP_CHECK = 0;
+ private static final int FULL_ARP_CHECK = 1;
private Context mContext;
private ContentResolver mContentResolver;
private WifiManager mWifiManager;
- private DnsPinger mDnsPinger;
private IntentFilter mIntentFilter;
private BroadcastReceiver mBroadcastReceiver;
+ private AsyncChannel mWsmChannel = new AsyncChannel();;
private DefaultState mDefaultState = new DefaultState();
private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
private NotConnectedState mNotConnectedState = new NotConnectedState();
+ private VerifyingLinkState mVerifyingLinkState = new VerifyingLinkState();
private ConnectedState mConnectedState = new ConnectedState();
- private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
+ private WalledGardenCheckState mWalledGardenCheckState = new WalledGardenCheckState();
+ /* Online and watching link connectivity */
private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
+ /* RSSI level is at RSSI_LEVEL_MONITOR and needs close monitoring */
+ private RssiMonitoringState mRssiMonitoringState = new RssiMonitoringState();
+ /* Online and doing nothing */
private OnlineState mOnlineState = new OnlineState();
- private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
- private DelayWalledGardenState mDelayWalledGardenState = new DelayWalledGardenState();
- private WalledGardenState mWalledGardenState = new WalledGardenState();
- private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
- private long mDnsCheckShortIntervalMs;
- private long mDnsCheckLongIntervalMs;
+ private int mArpToken = 0;
+ private long mArpCheckIntervalMs;
+ private int mRssiFetchToken = 0;
+ private long mRssiFetchIntervalMs;
private long mWalledGardenIntervalMs;
- private int mMaxSsidBlacklists;
- private int mNumDnsPings;
- private int mMinDnsResponses;
- private int mDnsPingTimeoutMs;
- private long mBlacklistFollowupIntervalMs;
+ private int mNumArpPings;
+ private int mMinArpResponses;
+ private int mArpPingTimeoutMs;
private boolean mPoorNetworkDetectionEnabled;
private boolean mWalledGardenTestEnabled;
private String mWalledGardenUrl;
- private boolean mShowDisabledNotification;
- /**
- * The {@link WifiInfo} object passed to WWSM on network broadcasts
- */
- private WifiInfo mConnectionInfo;
- private int mNetEventCounter = 0;
-
- /**
- * Currently maintained but not used, TODO
- */
- private HashSet<String> mBssids = new HashSet<String>();
- private int mNumCheckFailures = 0;
+ private WifiInfo mWifiInfo;
+ private LinkProperties mLinkProperties;
- private Long mLastWalledGardenCheckTime = null;
+ private long mLastWalledGardenCheckTime = 0;
- /**
- * This is set by the blacklisted state and reset when connected to a new AP.
- * It triggers a disableNetwork call if a DNS check fails.
- */
- public boolean mDisableAPNextFailure = false;
private static boolean sWifiOnly = false;
- private boolean mDisabledNotificationShown;
private boolean mWalledGardenNotificationShown;
- public boolean mHasConnectedWifiManager = false;
/**
* STATE MAP
* Default
* / \
- * Disabled Enabled
- * / \
- * NotConnected Connected
- * /---------\
- * (all other states)
+ * Disabled Enabled
+ * / \ \
+ * NotConnected Verifying Connected
+ * /---------\
+ * (all other states)
*/
private WifiWatchdogStateMachine(Context context) {
super(TAG);
mContext = context;
mContentResolver = context.getContentResolver();
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
- this.getHandler().getLooper(), this.getHandler(),
- ConnectivityManager.TYPE_WIFI);
+ mWsmChannel.connectSync(mContext, getHandler(),
+ mWifiManager.getWifiStateMachineMessenger());
setupNetworkReceiver();
@@ -221,16 +245,18 @@ public class WifiWatchdogStateMachine extends StateMachine {
addState(mWatchdogDisabledState, mDefaultState);
addState(mWatchdogEnabledState, mDefaultState);
addState(mNotConnectedState, mWatchdogEnabledState);
+ addState(mVerifyingLinkState, mWatchdogEnabledState);
addState(mConnectedState, mWatchdogEnabledState);
- addState(mDnsCheckingState, mConnectedState);
- addState(mDnsCheckFailureState, mConnectedState);
- addState(mDelayWalledGardenState, mConnectedState);
- addState(mWalledGardenState, mConnectedState);
- addState(mBlacklistedApState, mConnectedState);
+ addState(mWalledGardenCheckState, mConnectedState);
addState(mOnlineWatchState, mConnectedState);
+ addState(mRssiMonitoringState, mOnlineWatchState);
addState(mOnlineState, mConnectedState);
- setInitialState(mWatchdogDisabledState);
+ if (isWatchdogEnabled()) {
+ setInitialState(mNotConnectedState);
+ } else {
+ setInitialState(mWatchdogDisabledState);
+ }
updateSettings();
}
@@ -242,19 +268,16 @@ public class WifiWatchdogStateMachine extends StateMachine {
sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
// Disable for wifi only devices.
- if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
- sWifiOnly) {
+ if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null
+ && sWifiOnly) {
+ log("Disabling watchog for wi-fi only device");
putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
}
WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
wwsm.start();
- wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
return wwsm;
}
- /**
- *
- */
private void setupNetworkReceiver() {
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -263,10 +286,8 @@ public class WifiWatchdogStateMachine extends StateMachine {
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
- intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
- } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
+ obtainMessage(EVENT_RSSI_CHANGE,
+ intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
} else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
@@ -279,7 +300,7 @@ public class WifiWatchdogStateMachine extends StateMachine {
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
}
/**
@@ -311,39 +332,29 @@ public class WifiWatchdogStateMachine extends StateMachine {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
- Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS),
+ Settings.Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_ARP_PINGS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP)
- , false, contentObserver);
}
/**
@@ -375,49 +386,49 @@ public class WifiWatchdogStateMachine extends StateMachine {
}
}
- private boolean rssiStrengthAboveCutoff(int rssi) {
- return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
- }
-
public void dump(PrintWriter pw) {
pw.print("WatchdogStatus: ");
- pw.print("State " + getCurrentState());
- pw.println(", network [" + mConnectionInfo + "]");
- pw.print("checkFailures " + mNumCheckFailures);
- pw.println(", bssids: " + mBssids);
- pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
+ pw.print("State: " + getCurrentState());
+ pw.println("mWifiInfo: [" + mWifiInfo + "]");
+ pw.println("mLinkProperties: [" + mLinkProperties + "]");
+ pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
+ pw.println("mArpCheckIntervalMs: [" + mArpCheckIntervalMs+ "]");
+ pw.println("mRssiFetchIntervalMs: [" + mRssiFetchIntervalMs + "]");
+ pw.println("mWalledGardenIntervalMs: [" + mWalledGardenIntervalMs + "]");
+ pw.println("mNumArpPings: [" + mNumArpPings + "]");
+ pw.println("mMinArpResponses: [" + mMinArpResponses + "]");
+ pw.println("mArpPingTimeoutMs: [" + mArpPingTimeoutMs + "]");
+ pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
+ pw.println("mWalledGardenTestEnabled: [" + mWalledGardenTestEnabled + "]");
+ pw.println("mWalledGardenUrl: [" + mWalledGardenUrl + "]");
}
private boolean isWatchdogEnabled() {
- return getSettingsBoolean(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true);
+ boolean ret = getSettingsBoolean(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true);
+ if (DBG) log("watchdog enabled " + ret);
+ return ret;
}
private void updateSettings() {
- mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS,
- DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS);
- mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS,
- DEFAULT_DNS_CHECK_LONG_INTERVAL_MS);
- mMaxSsidBlacklists = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS,
- DEFAULT_MAX_SSID_BLACKLISTS);
- mNumDnsPings = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_NUM_DNS_PINGS,
- DEFAULT_NUM_DNS_PINGS);
- mMinDnsResponses = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES,
- DEFAULT_MIN_DNS_RESPONSES);
- mDnsPingTimeoutMs = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS,
- DEFAULT_DNS_PING_TIMEOUT_MS);
- mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS,
- DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS);
- //TODO: enable this by default after changing watchdog behavior
- //Also, update settings description
+ if (DBG) log("Updating secure settings");
+
+ mArpCheckIntervalMs = Secure.getLong(mContentResolver,
+ Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS,
+ DEFAULT_ARP_CHECK_INTERVAL_MS);
+ mRssiFetchIntervalMs = Secure.getLong(mContentResolver,
+ Secure.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS,
+ DEFAULT_RSSI_FETCH_INTERVAL_MS);
+ mNumArpPings = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_NUM_ARP_PINGS,
+ DEFAULT_NUM_ARP_PINGS);
+ mMinArpResponses = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES,
+ DEFAULT_MIN_ARP_RESPONSES);
+ mArpPingTimeoutMs = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS,
+ DEFAULT_ARP_PING_TIMEOUT_MS);
mPoorNetworkDetectionEnabled = getSettingsBoolean(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
+ Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
mWalledGardenUrl = getSettingsStr(mContentResolver,
@@ -426,69 +437,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
DEFAULT_WALLED_GARDEN_INTERVAL_MS);
- mShowDisabledNotification = getSettingsBoolean(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true);
- }
-
- /**
- * Helper to return wait time left given a min interval and last run
- *
- * @param interval minimum wait interval
- * @param lastTime last time action was performed in
- * SystemClock.elapsedRealtime(). Null if never.
- * @return non negative time to wait
- */
- private static long waitTime(long interval, Long lastTime) {
- if (lastTime == null)
- return 0;
- long wait = interval + lastTime - SystemClock.elapsedRealtime();
- return wait > 0 ? wait : 0;
- }
-
- private static String wifiInfoToStr(WifiInfo wifiInfo) {
- if (wifiInfo == null)
- return "null";
- return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
- }
-
- /**
- * Uses {@link #mConnectionInfo}.
- */
- private void updateBssids() {
- String curSsid = mConnectionInfo.getSSID();
- List<ScanResult> results = mWifiManager.getScanResults();
- int oldNumBssids = mBssids.size();
-
- if (results == null) {
- if (DBG) {
- log("updateBssids: Got null scan results!");
- }
- return;
- }
-
- for (ScanResult result : results) {
- if (result == null || result.SSID == null) {
- if (DBG) {
- log("Received invalid scan result: " + result);
- }
- continue;
- }
- if (curSsid.equals(result.SSID))
- mBssids.add(result.BSSID);
- }
- }
-
- private void resetWatchdogState() {
- if (DBG) {
- log("Resetting watchdog state...");
- }
- mConnectionInfo = null;
- mDisableAPNextFailure = false;
- mLastWalledGardenCheckTime = null;
- mNumCheckFailures = 0;
- mBssids.clear();
- setDisabledNetworkNotificationVisible(false);
- setWalledGardenNotificationVisible(false);
}
private void setWalledGardenNotificationVisible(boolean visible) {
@@ -507,7 +455,7 @@ public class WifiWatchdogStateMachine extends StateMachine {
CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
- mConnectionInfo.getSSID());
+ mWifiInfo.getSSID());
Notification notification = new Notification();
notification.when = 0;
@@ -524,42 +472,12 @@ public class WifiWatchdogStateMachine extends StateMachine {
mWalledGardenNotificationShown = visible;
}
- private void setDisabledNetworkNotificationVisible(boolean visible) {
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mDisabledNotificationShown) {
- return;
- }
-
- Resources r = Resources.getSystem();
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- CharSequence title = r.getText(R.string.wifi_watchdog_network_disabled);
- String msg = mConnectionInfo.getSSID() +
- r.getText(R.string.wifi_watchdog_network_disabled_detailed);
-
- Notification wifiDisabledWarning = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.stat_sys_warning)
- .setDefaults(Notification.DEFAULT_ALL)
- .setTicker(title)
- .setContentTitle(title)
- .setContentText(msg)
- .setContentIntent(PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true)
- .getNotification();
-
- notificationManager.notify(DISABLED_NETWORK_NOTIFICATION_ID, 1, wifiDisabledWarning);
- } else {
- notificationManager.cancel(DISABLED_NETWORK_NOTIFICATION_ID, 1);
+ class DefaultState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
}
- mDisabledNotificationShown = visible;
- }
- class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
@@ -568,11 +486,22 @@ public class WifiWatchdogStateMachine extends StateMachine {
if (DBG) {
log("Updating wifi-watchdog secure settings");
}
- return HANDLED;
- }
- if (DBG) {
- log("Caught message " + msg.what + " in state " +
- getCurrentState().getName());
+ break;
+ case EVENT_RSSI_CHANGE:
+ mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
+ break;
+ case EVENT_WIFI_RADIO_STATE_CHANGE:
+ case EVENT_NETWORK_STATE_CHANGE:
+ case CMD_ARP_CHECK:
+ case CMD_DELAYED_WALLED_GARDEN_CHECK:
+ case CMD_RSSI_FETCH:
+ case RSSI_FETCH_SUCCEEDED:
+ case RSSI_FETCH_FAILED:
+ //ignore
+ break;
+ default:
+ log("Unhandled message " + msg + " in state " + getCurrentState().getName());
+ break;
}
return HANDLED;
}
@@ -580,12 +509,31 @@ public class WifiWatchdogStateMachine extends StateMachine {
class WatchdogDisabledState extends State {
@Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ }
+
+ @Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case EVENT_WATCHDOG_TOGGLED:
if (isWatchdogEnabled())
transitionTo(mNotConnectedState);
return HANDLED;
+ case EVENT_NETWORK_STATE_CHANGE:
+ Intent intent = (Intent) msg.obj;
+ NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+ switch (networkInfo.getDetailedState()) {
+ case VERIFYING_POOR_LINK:
+ if (DBG) log("Watchdog disabled, verify link");
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ break;
+ default:
+ break;
+ }
+ break;
}
return NOT_HANDLED;
}
@@ -594,8 +542,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
class WatchdogEnabledState extends State {
@Override
public void enter() {
- resetWatchdogState();
- mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
if (DBG) log("WifiWatchdogService enabled");
}
@@ -605,95 +551,134 @@ public class WifiWatchdogStateMachine extends StateMachine {
case EVENT_WATCHDOG_TOGGLED:
if (!isWatchdogEnabled())
transitionTo(mWatchdogDisabledState);
- return HANDLED;
+ break;
case EVENT_NETWORK_STATE_CHANGE:
- Intent stateChangeIntent = (Intent) msg.obj;
+ Intent intent = (Intent) msg.obj;
NetworkInfo networkInfo = (NetworkInfo)
- stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- setDisabledNetworkNotificationVisible(false);
- setWalledGardenNotificationVisible(false);
- switch (networkInfo.getState()) {
- case CONNECTED:
- WifiInfo wifiInfo = (WifiInfo)
- stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
- if (wifiInfo == null) {
- loge("Connected --> WifiInfo object null!");
- return HANDLED;
- }
-
- if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
- loge("Received wifiInfo object with null elts: "
- + wifiInfoToStr(wifiInfo));
- return HANDLED;
- }
+ if (DBG) log("network state change " + networkInfo.getDetailedState());
- initConnection(wifiInfo);
- mConnectionInfo = wifiInfo;
- mNetEventCounter++;
+ switch (networkInfo.getDetailedState()) {
+ case VERIFYING_POOR_LINK:
+ mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ mWifiInfo = (WifiInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_WIFI_INFO);
if (mPoorNetworkDetectionEnabled) {
- updateBssids();
- transitionTo(mDnsCheckingState);
+ if (mWifiInfo == null) {
+ log("Ignoring link verification, mWifiInfo is NULL");
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ } else {
+ transitionTo(mVerifyingLinkState);
+ }
+ } else {
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ }
+ break;
+ case CONNECTED:
+ if (shouldCheckWalledGarden()) {
+ transitionTo(mWalledGardenCheckState);
} else {
- transitionTo(mDelayWalledGardenState);
+ transitionTo(mOnlineWatchState);
}
break;
default:
- mNetEventCounter++;
transitionTo(mNotConnectedState);
break;
}
- return HANDLED;
+ break;
case EVENT_WIFI_RADIO_STATE_CHANGE:
if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
if (DBG) log("WifiStateDisabling -- Resetting WatchdogState");
- resetWatchdogState();
- mNetEventCounter++;
transitionTo(mNotConnectedState);
}
- return HANDLED;
- }
-
- return NOT_HANDLED;
- }
-
- /**
- * @param wifiInfo Info object with non-null ssid and bssid
- */
- private void initConnection(WifiInfo wifiInfo) {
- if (DBG) {
- log("Connected:: old " + wifiInfoToStr(mConnectionInfo) +
- " ==> new " + wifiInfoToStr(wifiInfo));
+ break;
+ default:
+ return NOT_HANDLED;
}
- if (mConnectionInfo == null || !wifiInfo.getSSID().equals(mConnectionInfo.getSSID())) {
- resetWatchdogState();
- } else if (!wifiInfo.getBSSID().equals(mConnectionInfo.getBSSID())) {
- mDisableAPNextFailure = false;
- }
+ setWalledGardenNotificationVisible(false);
+ return HANDLED;
}
@Override
public void exit() {
- mContext.unregisterReceiver(mBroadcastReceiver);
if (DBG) log("WifiWatchdogService disabled");
}
}
class NotConnectedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ }
}
- class ConnectedState extends State {
+ class VerifyingLinkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ //Treat entry as an rssi change
+ handleRssiChange();
+ }
+
+ private void handleRssiChange() {
+ if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+ //stay here
+ if (DBG) log("enter VerifyingLinkState, stay level: " + mCurrentSignalLevel);
+ } else {
+ if (DBG) log("enter VerifyingLinkState, arp check level: " + mCurrentSignalLevel);
+ sendMessage(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0));
+ }
+ }
+
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case EVENT_SCAN_RESULTS_AVAILABLE:
- if (mPoorNetworkDetectionEnabled) {
- updateBssids();
+ case EVENT_WATCHDOG_SETTINGS_CHANGE:
+ updateSettings();
+ if (!mPoorNetworkDetectionEnabled) {
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
}
- return HANDLED;
+ break;
+ case EVENT_RSSI_CHANGE:
+ mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
+ handleRssiChange();
+ break;
+ case CMD_ARP_CHECK:
+ if (msg.arg1 == mArpToken) {
+ if (doArpTest(FULL_ARP_CHECK) == true) {
+ if (DBG) log("Notify link is good " + mCurrentSignalLevel);
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ } else {
+ if (DBG) log("Continue ARP check, rssi level: " + mCurrentSignalLevel);
+ sendMessageDelayed(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0),
+ mArpCheckIntervalMs);
+ }
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class ConnectedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
case EVENT_WATCHDOG_SETTINGS_CHANGE:
updateSettings();
+ //STOPSHIP: Remove this at ship
+ DBG = true;
+ if (DBG) log("Updated secure settings and turned debug on");
+
if (mPoorNetworkDetectionEnabled) {
transitionTo(mOnlineWatchState);
} else {
@@ -705,401 +690,229 @@ public class WifiWatchdogStateMachine extends StateMachine {
}
}
- class DnsCheckingState extends State {
- List<InetAddress> mDnsList;
- int[] dnsCheckSuccesses;
- String dnsCheckLogStr;
- String[] dnsResponseStrs;
- /** Keeps track of active dns pings. Map is from pingID to index in mDnsList */
- HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
-
+ class WalledGardenCheckState extends State {
+ private int mWalledGardenToken = 0;
@Override
public void enter() {
- mDnsList = mDnsPinger.getDnsList();
- int numDnses = mDnsList.size();
- dnsCheckSuccesses = new int[numDnses];
- dnsResponseStrs = new String[numDnses];
- for (int i = 0; i < numDnses; i++)
- dnsResponseStrs[i] = "";
-
- if (DBG) {
- dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
- mDnsList, mConnectionInfo.getSSID());
- log(dnsCheckLogStr);
- }
-
- idDnsMap.clear();
- for (int i=0; i < mNumDnsPings; i++) {
- for (int j = 0; j < numDnses; j++) {
- idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
- DNS_START_DELAY_MS + DNS_INTRATEST_PING_INTERVAL_MS * i), j);
- }
- }
+ if (DBG) log(getName() + "\n");
+ sendMessageDelayed(obtainMessage(CMD_DELAYED_WALLED_GARDEN_CHECK,
+ ++mWalledGardenToken, 0), WALLED_GARDEN_START_DELAY_MS);
}
@Override
public boolean processMessage(Message msg) {
- if (msg.what != DnsPinger.DNS_PING_RESULT) {
- return NOT_HANDLED;
- }
-
- int pingID = msg.arg1;
- int pingResponseTime = msg.arg2;
-
- Integer dnsServerId = idDnsMap.get(pingID);
- if (dnsServerId == null) {
- loge("Received a Dns response with unknown ID!");
- return HANDLED;
- }
-
- idDnsMap.remove(pingID);
- if (pingResponseTime >= 0)
- dnsCheckSuccesses[dnsServerId]++;
-
- if (DBG) {
- if (pingResponseTime >= 0) {
- dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
- } else {
- dnsResponseStrs[dnsServerId] += "|x";
- }
- }
-
- /**
- * After a full ping count, if we have more responses than this
- * cutoff, the outcome is success; else it is 'failure'.
- */
-
- /**
- * Our final success count will be at least this big, so we're
- * guaranteed to succeed.
- */
- if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
- // DNS CHECKS OK, NOW WALLED GARDEN
- if (DBG) {
- log(makeLogString() + " SUCCESS");
- }
-
- if (!shouldCheckWalledGarden()) {
- transitionTo(mOnlineWatchState);
- return HANDLED;
- }
-
- transitionTo(mDelayWalledGardenState);
- return HANDLED;
- }
-
- if (idDnsMap.isEmpty()) {
- if (DBG) {
- log(makeLogString() + " FAILURE");
- }
- transitionTo(mDnsCheckFailureState);
- return HANDLED;
+ switch (msg.what) {
+ case CMD_DELAYED_WALLED_GARDEN_CHECK:
+ if (msg.arg1 == mWalledGardenToken) {
+ mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
+ if (isWalledGardenConnection()) {
+ if (DBG) log("Walled garden detected");
+ setWalledGardenNotificationVisible(true);
+ }
+ transitionTo(mOnlineWatchState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
}
-
return HANDLED;
}
+ }
- private String makeLogString() {
- String logStr = dnsCheckLogStr;
- for (String respStr : dnsResponseStrs)
- logStr += " [" + respStr + "]";
- return logStr;
- }
-
- @Override
- public void exit() {
- mDnsPinger.cancelPings();
- }
-
- private boolean shouldCheckWalledGarden() {
- if (!mWalledGardenTestEnabled) {
- if (DBG)
- log("Skipping walled garden check - disabled");
- return false;
- }
- long waitTime = waitTime(mWalledGardenIntervalMs,
- mLastWalledGardenCheckTime);
- if (waitTime > 0) {
- if (DBG) {
- log("Skipping walled garden check - wait " +
- waitTime + " ms.");
- }
- return false;
+ class OnlineWatchState extends State {
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ if (mPoorNetworkDetectionEnabled) {
+ //Treat entry as an rssi change
+ handleRssiChange();
+ } else {
+ transitionTo(mOnlineState);
}
- return true;
}
- }
- class DelayWalledGardenState extends State {
- @Override
- public void enter() {
- sendMessageDelayed(MESSAGE_DELAYED_WALLED_GARDEN_CHECK, WALLED_GARDEN_START_DELAY_MS);
+ private void handleRssiChange() {
+ if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+ sendPoorLinkDetected();
+ } else if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+ transitionTo(mRssiMonitoringState);
+ } else {
+ //stay here
+ }
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_DELAYED_WALLED_GARDEN_CHECK:
- mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
- if (isWalledGardenConnection()) {
- if (DBG) log("Walled garden test complete - walled garden detected");
- transitionTo(mWalledGardenState);
+ case EVENT_RSSI_CHANGE:
+ mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
+ //Ready to avoid bssid again ?
+ long time = android.os.SystemClock.elapsedRealtime();
+ if (time - mLastBssidAvoidedTime > MIN_INTERVAL_AVOID_BSSID_MS[
+ mMinIntervalArrayIndex]) {
+ handleRssiChange();
} else {
- if (DBG) log("Walled garden test complete - online");
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
- } else {
- transitionTo(mOnlineState);
- }
+ if (DBG) log("Early to avoid " + mWifiInfo + " time: " + time +
+ " last avoided: " + mLastBssidAvoidedTime +
+ " mMinIntervalArrayIndex: " + mMinIntervalArrayIndex);
}
- return HANDLED;
+ break;
default:
return NOT_HANDLED;
}
+ return HANDLED;
}
}
- class OnlineWatchState extends State {
- /**
- * Signals a short-wait message is enqueued for the current 'guard' counter
- */
- boolean unstableSignalChecks = false;
-
- /**
- * The signal is unstable. We should enqueue a short-wait check, if one is enqueued
- * already
- */
- boolean signalUnstable = false;
-
- /**
- * A monotonic counter to ensure that at most one check message will be processed from any
- * set of check messages currently enqueued. Avoids duplicate checks when a low-signal
- * event is observed.
- */
- int checkGuard = 0;
- Long lastCheckTime = null;
-
- /** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */
- HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
-
- @Override
+ class RssiMonitoringState extends State {
public void enter() {
- lastCheckTime = SystemClock.elapsedRealtime();
- signalUnstable = false;
- checkGuard++;
- unstableSignalChecks = false;
- pingInfoMap.clear();
- triggerSingleDnsCheck();
+ if (DBG) log(getName() + "\n");
+ sendMessage(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0));
}
- @Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case EVENT_RSSI_CHANGE:
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("Rssi change message out of sync, ignoring");
- }
- return HANDLED;
- }
- int newRssi = msg.arg2;
- signalUnstable = !rssiStrengthAboveCutoff(newRssi);
- if (DBG) {
- log("OnlineWatchState:: new rssi " + newRssi + " --> level " +
- WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
- }
-
- if (signalUnstable && !unstableSignalChecks) {
- if (DBG) {
- log("Sending triggered check msg");
- }
- triggerSingleDnsCheck();
- }
- return HANDLED;
- case MESSAGE_SINGLE_DNS_CHECK:
- if (msg.arg1 != checkGuard) {
- if (DBG) {
- log("Single check msg out of sync, ignoring.");
- }
- return HANDLED;
- }
- lastCheckTime = SystemClock.elapsedRealtime();
- pingInfoMap.clear();
- for (InetAddress curDns: mDnsPinger.getDnsList()) {
- pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
- curDns);
+ mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
+ if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+ sendPoorLinkDetected();
+ } else if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+ //stay here;
+ } else {
+ //We dont need frequent RSSI monitoring any more
+ transitionTo(mOnlineWatchState);
}
- return HANDLED;
- case DnsPinger.DNS_PING_RESULT:
- InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
- if (curDnsServer == null) {
- return HANDLED;
+ break;
+ case CMD_RSSI_FETCH:
+ if (msg.arg1 == mRssiFetchToken) {
+ mWsmChannel.sendMessage(RSSI_FETCH);
+ sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
+ mRssiFetchIntervalMs);
}
- pingInfoMap.remove(msg.arg1);
- int responseTime = msg.arg2;
- if (responseTime >= 0) {
- if (DBG) {
- log("Single DNS ping OK. Response time: "
- + responseTime + " from DNS " + curDnsServer);
- }
- pingInfoMap.clear();
-
- checkGuard++;
- unstableSignalChecks = false;
- triggerSingleDnsCheck();
+ break;
+ case RSSI_FETCH_SUCCEEDED:
+ int rssi = msg.arg1;
+ if (DBG) log("RSSI_FETCH_SUCCEEDED: " + rssi);
+ if (msg.arg1 < RSSI_MONITOR_THRESHOLD) {
+ mRssiMonitorCount++;
} else {
- if (pingInfoMap.isEmpty()) {
- if (DBG) {
- log("Single dns ping failure. All dns servers failed, "
- + "starting full checks.");
- }
- transitionTo(mDnsCheckingState);
- }
+ mRssiMonitorCount = 0;
}
- return HANDLED;
- }
- return NOT_HANDLED;
- }
- @Override
- public void exit() {
- mDnsPinger.cancelPings();
- }
-
- /**
- * Times a dns check with an interval based on {@link #signalUnstable}
- */
- private void triggerSingleDnsCheck() {
- long waitInterval;
- if (signalUnstable) {
- waitInterval = mDnsCheckShortIntervalMs;
- unstableSignalChecks = true;
- } else {
- waitInterval = mDnsCheckLongIntervalMs;
+ if (mRssiMonitorCount > RSSI_MONITOR_COUNT) {
+ sendPoorLinkDetected();
+ ++mRssiFetchToken;
+ }
+ break;
+ case RSSI_FETCH_FAILED:
+ //can happen if we are waiting to get a disconnect notification
+ if (DBG) log("RSSI_FETCH_FAILED");
+ break;
+ default:
+ return NOT_HANDLED;
}
- sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
- waitTime(waitInterval, lastCheckTime));
+ return HANDLED;
}
- }
-
+ }
/* Child state of ConnectedState indicating that we are online
* and there is nothing to do
*/
class OnlineState extends State {
- }
-
- class DnsCheckFailureState extends State {
-
@Override
public void enter() {
- mNumCheckFailures++;
- obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
+ if (DBG) log(getName() + "\n");
}
+ }
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_HANDLE_BAD_AP) {
- return NOT_HANDLED;
- }
+ private boolean shouldCheckWalledGarden() {
+ if (!mWalledGardenTestEnabled) {
+ if (DBG) log("Skipping walled garden check - disabled");
+ return false;
+ }
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("Msg out of sync, ignoring...");
- }
- return HANDLED;
+ long waitTime = (mWalledGardenIntervalMs + mLastWalledGardenCheckTime)
+ - SystemClock.elapsedRealtime();
+
+ if (mLastWalledGardenCheckTime != 0 && waitTime > 0) {
+ if (DBG) {
+ log("Skipping walled garden check - wait " +
+ waitTime + " ms.");
}
+ return false;
+ }
+ return true;
+ }
- if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
- || mNumCheckFailures >= mMaxSsidBlacklists) {
- if (sWifiOnly) {
- log("Would disable bad network, but device has no mobile data!" +
- " Going idle...");
- // This state should be called idle -- will be changing flow.
- transitionTo(mNotConnectedState);
- return HANDLED;
- }
+ private boolean doArpTest(int type) {
+ boolean success;
- // TODO : Unban networks if they had low signal ?
- log("Disabling current SSID " + wifiInfoToStr(mConnectionInfo)
- + ". " + "numCheckFailures " + mNumCheckFailures
- + ", numAPs " + mBssids.size());
- int networkId = mConnectionInfo.getNetworkId();
- if (!mHasConnectedWifiManager) {
- mWifiManager.asyncConnect(mContext, getHandler());
- mHasConnectedWifiManager = true;
- }
- mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE);
- if (mShowDisabledNotification && mConnectionInfo.isExplicitConnect()) {
- setDisabledNetworkNotificationVisible(true);
- }
- transitionTo(mNotConnectedState);
- } else {
- log("Blacklisting current BSSID. " + wifiInfoToStr(mConnectionInfo)
- + "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size());
+ String iface = mLinkProperties.getInterfaceName();
+ String mac = mWifiInfo.getMacAddress();
+ InetAddress inetAddress = null;
+ InetAddress gateway = null;
- mWifiManager.addToBlacklist(mConnectionInfo.getBSSID());
- mWifiManager.reassociate();
- transitionTo(mBlacklistedApState);
- }
- return HANDLED;
+ for (LinkAddress la : mLinkProperties.getLinkAddresses()) {
+ inetAddress = la.getAddress();
+ break;
}
- }
- class WalledGardenState extends State {
- @Override
- public void enter() {
- obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
+ for (RouteInfo route : mLinkProperties.getRoutes()) {
+ gateway = route.getGateway();
+ break;
}
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
- return NOT_HANDLED;
- }
+ if (DBG) log("ARP " + iface + "addr: " + inetAddress + "mac: " + mac + "gw: " + gateway);
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("WalledGardenState::Msg out of sync, ignoring...");
- }
- return HANDLED;
- }
- setWalledGardenNotificationVisible(true);
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
+ try {
+ ArpPeer peer = new ArpPeer(iface, inetAddress, mac, gateway);
+ if (type == SINGLE_ARP_CHECK) {
+ success = (peer.doArp(mArpPingTimeoutMs) != null);
+ if (DBG) log("single ARP test result: " + success);
} else {
- transitionTo(mOnlineState);
+ int responses = 0;
+ for (int i=0; i < mNumArpPings; i++) {
+ if(peer.doArp(mArpPingTimeoutMs) != null) responses++;
+ }
+ if (DBG) log("full ARP test result: " + responses + "/" + mNumArpPings);
+ success = (responses >= mMinArpResponses);
}
- return HANDLED;
+ peer.close();
+ } catch (SocketException se) {
+ //Consider an Arp socket creation issue as a successful Arp
+ //test to avoid any wifi connectivity issues
+ loge("ARP test initiation failure: " + se);
+ success = true;
}
+
+ return success;
}
- class BlacklistedApState extends State {
- @Override
- public void enter() {
- mDisableAPNextFailure = true;
- sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
- mBlacklistFollowupIntervalMs);
- }
+ private int calculateSignalLevel(int rssi) {
+ int signalLevel = WifiManager.calculateSignalLevel(rssi,
+ WifiManager.RSSI_LEVELS);
+ if (DBG) log("RSSI current: " + mCurrentSignalLevel + "new: " + rssi + ", " + signalLevel);
+ return signalLevel;
+ }
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
- return NOT_HANDLED;
- }
+ private void sendPoorLinkDetected() {
+ if (DBG) log("send POOR_LINK_DETECTED " + mWifiInfo);
+ mWsmChannel.sendMessage(POOR_LINK_DETECTED);
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("BlacklistedApState::Msg out of sync, ignoring...");
- }
- return HANDLED;
- }
+ long time = android.os.SystemClock.elapsedRealtime();
+ if (time - mLastBssidAvoidedTime > MIN_INTERVAL_AVOID_BSSID_MS[
+ MIN_INTERVAL_AVOID_BSSID_MS.length - 1]) {
+ mMinIntervalArrayIndex = 1;
+ if (DBG) log("set mMinIntervalArrayIndex to 1");
+ } else {
- transitionTo(mDnsCheckingState);
- return HANDLED;
+ if (mMinIntervalArrayIndex < MIN_INTERVAL_AVOID_BSSID_MS.length - 1) {
+ mMinIntervalArrayIndex++;
+ }
+ if (DBG) log("mMinIntervalArrayIndex: " + mMinIntervalArrayIndex);
}
- }
+ mLastBssidAvoidedTime = android.os.SystemClock.elapsedRealtime();
+ }
/**
* Convenience function for retrieving a single secure settings value
@@ -1151,11 +964,11 @@ public class WifiWatchdogStateMachine extends StateMachine {
return Settings.Secure.putInt(cr, name, value ? 1 : 0);
}
- private void log(String s) {
+ private static void log(String s) {
Log.d(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Log.e(TAG, s);
}
}
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index f70e5af..b80df21 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -16,9 +16,6 @@
package android.net.wifi;
-import android.net.LinkProperties;
-import android.net.wifi.WifiConfiguration.IpAssignment;
-import android.net.wifi.WifiConfiguration.ProxySettings;
import android.os.Parcelable;
import android.os.Parcel;
@@ -51,22 +48,10 @@ public class WpsInfo implements Parcelable {
/** Passed with pin method configuration */
public String pin;
- /** @hide */
- public IpAssignment ipAssignment;
-
- /** @hide */
- public ProxySettings proxySettings;
-
- /** @hide */
- public LinkProperties linkProperties;
-
public WpsInfo() {
setup = INVALID;
BSSID = null;
pin = null;
- ipAssignment = IpAssignment.UNASSIGNED;
- proxySettings = ProxySettings.UNASSIGNED;
- linkProperties = new LinkProperties();
}
public String toString() {
@@ -77,12 +62,6 @@ public class WpsInfo implements Parcelable {
sbuf.append('\n');
sbuf.append(" pin: ").append(pin);
sbuf.append('\n');
- sbuf.append("IP assignment: " + ipAssignment.toString());
- sbuf.append("\n");
- sbuf.append("Proxy settings: " + proxySettings.toString());
- sbuf.append("\n");
- sbuf.append(linkProperties.toString());
- sbuf.append("\n");
return sbuf.toString();
}
@@ -97,9 +76,6 @@ public class WpsInfo implements Parcelable {
setup = source.setup;
BSSID = source.BSSID;
pin = source.pin;
- ipAssignment = source.ipAssignment;
- proxySettings = source.proxySettings;
- linkProperties = new LinkProperties(source.linkProperties);
}
}
@@ -108,9 +84,6 @@ public class WpsInfo implements Parcelable {
dest.writeInt(setup);
dest.writeString(BSSID);
dest.writeString(pin);
- dest.writeString(ipAssignment.name());
- dest.writeString(proxySettings.name());
- dest.writeParcelable(linkProperties, flags);
}
/** Implement the Parcelable interface */
@@ -121,9 +94,6 @@ public class WpsInfo implements Parcelable {
config.setup = in.readInt();
config.BSSID = in.readString();
config.pin = in.readString();
- config.ipAssignment = IpAssignment.valueOf(in.readString());
- config.proxySettings = ProxySettings.valueOf(in.readString());
- config.linkProperties = in.readParcelable(null);
return config;
}
diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java
deleted file mode 100644
index c14a8db..0000000
--- a/wifi/java/android/net/wifi/WpsStateMachine.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2010 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 com.android.internal.util.AsyncChannel;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.wifi.StateChangeResult;
-import android.net.wifi.WpsResult.Status;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Manages a WPS connection.
- *
- * WPS consists as a series of EAP exchange triggered
- * by a user action that leads to a successful connection
- * after automatic creation of configuration in the
- * supplicant. We currently support the following methods
- * of WPS setup
- * 1. Pin method: Pin can be either obtained from the device
- * or from the access point to connect to.
- * 2. Push button method: This involves pushing a button on
- * the access point and the device
- *
- * After a successful WPS setup, the state machine
- * reloads the configuration and updates the IP and proxy
- * settings, if any.
- */
-class WpsStateMachine extends StateMachine {
-
- private static final String TAG = "WpsStateMachine";
- private static final boolean DBG = false;
-
- private WifiStateMachine mWifiStateMachine;
-
- private WpsInfo mWpsInfo;
-
- private Context mContext;
- AsyncChannel mReplyChannel = new AsyncChannel();
-
- private State mDefaultState = new DefaultState();
- private State mInactiveState = new InactiveState();
- private State mActiveState = new ActiveState();
-
- public WpsStateMachine(Context context, WifiStateMachine wsm, Handler target) {
- super(TAG, target.getLooper());
-
- mContext = context;
- mWifiStateMachine = wsm;
- addState(mDefaultState);
- addState(mInactiveState, mDefaultState);
- addState(mActiveState, mDefaultState);
-
- setInitialState(mInactiveState);
-
- //start the state machine
- start();
- }
-
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class DefaultState extends State {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- WpsInfo wpsConfig;
- switch (message.what) {
- case WifiStateMachine.CMD_START_WPS:
- mWpsInfo = (WpsInfo) message.obj;
- WpsResult result;
- switch (mWpsInfo.setup) {
- case WpsInfo.PBC:
- result = WifiConfigStore.startWpsPbc(mWpsInfo);
- break;
- case WpsInfo.KEYPAD:
- result = WifiConfigStore.startWpsWithPinFromAccessPoint(mWpsInfo);
- break;
- case WpsInfo.DISPLAY:
- result = WifiConfigStore.startWpsWithPinFromDevice(mWpsInfo);
- break;
- default:
- result = new WpsResult(Status.FAILURE);
- Log.e(TAG, "Invalid setup for WPS");
- break;
- }
- mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED, result);
- if (result.status == Status.SUCCESS) {
- transitionTo(mActiveState);
- } else {
- Log.e(TAG, "Failed to start WPS with config " + mWpsInfo.toString());
- }
- break;
- case WifiStateMachine.CMD_RESET_WPS_STATE:
- transitionTo(mInactiveState);
- break;
- default:
- Log.e(TAG, "Failed to handle " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class ActiveState extends State {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
-
- @Override
- public boolean processMessage(Message message) {
- boolean retValue = HANDLED;
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState supState = (SupplicantState) stateChangeResult.state;
- switch (supState) {
- case COMPLETED:
- /* During WPS setup, all other networks are disabled. After
- * a successful connect a new config is created in the supplicant.
- *
- * We need to enable all networks after a successful connection
- * and the configuration list needs to be reloaded from the supplicant.
- */
- Log.d(TAG, "WPS set up successful");
- WifiConfigStore.enableAllNetworks();
- WifiConfigStore.loadConfiguredNetworks();
- WifiConfigStore.updateIpAndProxyFromWpsConfig(
- stateChangeResult.networkId, mWpsInfo);
- mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
- transitionTo(mInactiveState);
- break;
- case INACTIVE:
- /* A failed WPS connection */
- Log.d(TAG, "WPS set up failed, enabling other networks");
- WifiConfigStore.enableAllNetworks();
- mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
- transitionTo(mInactiveState);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring supplicant state " + supState.name());
- break;
- }
- break;
- case WifiStateMachine.CMD_START_WPS:
- /* Ignore request and send an in progress message */
- mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
- new WpsResult(Status.IN_PROGRESS));
- break;
- default:
- retValue = NOT_HANDLED;
- }
- return retValue;
- }
- }
-
- class InactiveState extends State {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
-
- @Override
- public boolean processMessage(Message message) {
- boolean retValue = HANDLED;
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- //Ignore supplicant state changes
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- break;
- default:
- retValue = NOT_HANDLED;
- }
- return retValue;
- }
- }
-
-}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index e0c1b13..6aea090 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -84,19 +84,19 @@ public class WifiP2pConfig implements Parcelable {
} catch (NumberFormatException e) {
devPasswdId = 0;
}
- //As defined in wps/wps_defs.h
+ //Based on definitions in wps/wps_defs.h
switch (devPasswdId) {
- case 0x00:
- wps.setup = WpsInfo.LABEL;
- break;
+ //DEV_PW_USER_SPECIFIED = 0x0001,
case 0x01:
- wps.setup = WpsInfo.KEYPAD;
+ wps.setup = WpsInfo.DISPLAY;
break;
+ //DEV_PW_PUSHBUTTON = 0x0004,
case 0x04:
wps.setup = WpsInfo.PBC;
break;
+ //DEV_PW_REGISTRAR_SPECIFIED = 0x0005
case 0x05:
- wps.setup = WpsInfo.DISPLAY;
+ wps.setup = WpsInfo.KEYPAD;
break;
default:
wps.setup = WpsInfo.PBC;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 1b0c301..afdc9be 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.util.Log;
import java.util.regex.Pattern;
+import java.util.regex.Matcher;
/**
* A class representing a Wi-Fi p2p device
@@ -34,23 +35,12 @@ public class WifiP2pDevice implements Parcelable {
/**
* The device name is a user friendly string to identify a Wi-Fi p2p device
*/
- public String deviceName;
+ public String deviceName = "";
/**
* The device MAC address uniquely identifies a Wi-Fi p2p device
*/
- public String deviceAddress;
-
- /**
- * interfaceAddress
- *
- * This address is used during group owner negotiation as the Intended
- * P2P Interface Address and the group interface will be created with
- * address as the local address in case of successfully completed
- * negotiation.
- * @hide
- */
- public String interfaceAddress;
+ public String deviceAddress = "";
/**
* Primary device type identifies the type of device. For example, an application
@@ -117,6 +107,43 @@ public class WifiP2pDevice implements Parcelable {
/** Device connection status */
public int status = UNAVAILABLE;
+ /** Detailed device string pattern
+ * Example:
+ * P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ */
+ private static final Pattern detailedDevicePattern = Pattern.compile(
+ "((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) " +
+ "(\\d+ )?" +
+ "p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) " +
+ "pri_dev_type=(\\d+-[0-9a-fA-F]+-\\d+) " +
+ "name='(.*)' " +
+ "config_methods=(0x[0-9a-fA-F]+) " +
+ "dev_capab=(0x[0-9a-fA-F]+) " +
+ "group_capab=(0x[0-9a-fA-F]+)"
+ );
+
+ /** 2 token device address pattern
+ * Example:
+ * P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
+ * AP-STA-DISCONNECTED 42:fc:89:a8:96:09
+ */
+ private static final Pattern twoTokenPattern = Pattern.compile(
+ "(p2p_dev_addr=)?((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+ );
+
+ /** 3 token device address pattern
+ * Example:
+ * AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
+ * AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
+ */
+ private static final Pattern threeTokenPattern = Pattern.compile(
+ "(?:[0-9a-f]{2}:){5}[0-9a-f]{2} p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+ );
+
+
public WifiP2pDevice() {
}
@@ -128,69 +155,55 @@ public class WifiP2pDevice implements Parcelable {
*
* P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
*
- * fa:7b:7a:42:02:13
- *
- * P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
- * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- * group_capab=0x0
+ * AP-STA-CONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
*
- * P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
- * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- * group_capab=0x0
+ * AP-STA-DISCONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
*
- * P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
- * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- * group_capab=0x0
+ * fa:7b:7a:42:02:13
*
* Note: The events formats can be looked up in the wpa_supplicant code
* @hide
*/
public WifiP2pDevice(String string) throws IllegalArgumentException {
- String[] tokens = string.split(" ");
+ String[] tokens = string.split("[ \n]");
+ Matcher match;
if (tokens.length < 1) {
throw new IllegalArgumentException("Malformed supplicant event");
}
- /* Just a device address */
- if (tokens.length == 1) {
- deviceAddress = string;
- return;
- }
-
- for (String token : tokens) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) continue;
-
- if (nameValue[0].equals("p2p_dev_addr")) {
- deviceAddress = nameValue[1];
- continue;
- }
-
- if (nameValue[0].equals("pri_dev_type")) {
- primaryDeviceType = nameValue[1];
- continue;
- }
-
- if (nameValue[0].equals("name")) {
- deviceName = trimQuotes(nameValue[1]);
- continue;
- }
-
- if (nameValue[0].equals("config_methods")) {
- wpsConfigMethodsSupported = parseHex(nameValue[1]);
- continue;
- }
-
- if (nameValue[0].equals("dev_capab")) {
- deviceCapability = parseHex(nameValue[1]);
- continue;
- }
-
- if (nameValue[0].equals("group_capab")) {
- groupCapability = parseHex(nameValue[1]);
- continue;
- }
+ switch (tokens.length) {
+ case 1:
+ /* Just a device address */
+ deviceAddress = string;
+ return;
+ case 2:
+ match = twoTokenPattern.matcher(string);
+ if (!match.find()) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+ deviceAddress = match.group(2);
+ return;
+ case 3:
+ match = threeTokenPattern.matcher(string);
+ if (!match.find()) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+ deviceAddress = match.group(1);
+ return;
+ default:
+ match = detailedDevicePattern.matcher(string);
+ if (!match.find()) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+
+ deviceAddress = match.group(3);
+ primaryDeviceType = match.group(4);
+ deviceName = match.group(5);
+ wpsConfigMethodsSupported = parseHex(match.group(6));
+ deviceCapability = parseHex(match.group(7));
+ groupCapability = parseHex(match.group(8));
+ break;
}
if (tokens[0].startsWith("P2P-DEVICE-FOUND")) {
@@ -239,7 +252,6 @@ public class WifiP2pDevice implements Parcelable {
StringBuffer sbuf = new StringBuffer();
sbuf.append("Device: ").append(deviceName);
sbuf.append("\n deviceAddress: ").append(deviceAddress);
- sbuf.append("\n interfaceAddress: ").append(interfaceAddress);
sbuf.append("\n primary type: ").append(primaryDeviceType);
sbuf.append("\n secondary type: ").append(secondaryDeviceType);
sbuf.append("\n wps: ").append(wpsConfigMethodsSupported);
@@ -259,7 +271,6 @@ public class WifiP2pDevice implements Parcelable {
if (source != null) {
deviceName = source.deviceName;
deviceAddress = source.deviceAddress;
- interfaceAddress = source.interfaceAddress;
primaryDeviceType = source.primaryDeviceType;
secondaryDeviceType = source.secondaryDeviceType;
wpsConfigMethodsSupported = source.wpsConfigMethodsSupported;
@@ -273,7 +284,6 @@ public class WifiP2pDevice implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(deviceName);
dest.writeString(deviceAddress);
- dest.writeString(interfaceAddress);
dest.writeString(primaryDeviceType);
dest.writeString(secondaryDeviceType);
dest.writeInt(wpsConfigMethodsSupported);
@@ -289,7 +299,6 @@ public class WifiP2pDevice implements Parcelable {
WifiP2pDevice device = new WifiP2pDevice();
device.deviceName = in.readString();
device.deviceAddress = in.readString();
- device.interfaceAddress = in.readString();
device.primaryDeviceType = in.readString();
device.secondaryDeviceType = in.readString();
device.wpsConfigMethodsSupported = in.readInt();
@@ -304,14 +313,6 @@ public class WifiP2pDevice implements Parcelable {
}
};
- private String trimQuotes(String str) {
- str = str.trim();
- if (str.startsWith("'") && str.endsWith("'")) {
- return str.substring(1, str.length()-1);
- }
- return str;
- }
-
//supported formats: 0x1abc, 0X1abc, 1abc
private int parseHex(String hexString) {
int num = 0;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index 9ce2545..3751727 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -24,6 +24,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
/**
* A class representing a Wi-Fi P2p device list
@@ -32,24 +33,28 @@ import java.util.Collections;
*/
public class WifiP2pDeviceList implements Parcelable {
- private Collection<WifiP2pDevice> mDevices;
+ private HashMap<String, WifiP2pDevice> mDevices;
public WifiP2pDeviceList() {
- mDevices = new ArrayList<WifiP2pDevice>();
+ mDevices = new HashMap<String, WifiP2pDevice>();
}
/** copy constructor */
public WifiP2pDeviceList(WifiP2pDeviceList source) {
if (source != null) {
- mDevices = source.getDeviceList();
+ for (WifiP2pDevice d : source.getDeviceList()) {
+ mDevices.put(d.deviceAddress, d);
+ }
}
}
/** @hide */
public WifiP2pDeviceList(ArrayList<WifiP2pDevice> devices) {
- mDevices = new ArrayList<WifiP2pDevice>();
+ mDevices = new HashMap<String, WifiP2pDevice>();
for (WifiP2pDevice device : devices) {
- mDevices.add(device);
+ if (device.deviceAddress != null) {
+ mDevices.put(device.deviceAddress, device);
+ }
}
}
@@ -62,37 +67,42 @@ public class WifiP2pDeviceList implements Parcelable {
/** @hide */
public void update(WifiP2pDevice device) {
- if (device == null) return;
- for (WifiP2pDevice d : mDevices) {
- //Found, update fields that can change
- if (d.equals(device)) {
- d.deviceName = device.deviceName;
- d.primaryDeviceType = device.primaryDeviceType;
- d.secondaryDeviceType = device.secondaryDeviceType;
- d.wpsConfigMethodsSupported = device.wpsConfigMethodsSupported;
- d.deviceCapability = device.deviceCapability;
- d.groupCapability = device.groupCapability;
- return;
- }
+ if (device == null || device.deviceAddress == null) return;
+ WifiP2pDevice d = mDevices.get(device.deviceAddress);
+ if (d != null) {
+ d.deviceName = device.deviceName;
+ d.primaryDeviceType = device.primaryDeviceType;
+ d.secondaryDeviceType = device.secondaryDeviceType;
+ d.wpsConfigMethodsSupported = device.wpsConfigMethodsSupported;
+ d.deviceCapability = device.deviceCapability;
+ d.groupCapability = device.groupCapability;
+ return;
}
//Not found, add a new one
- mDevices.add(device);
+ mDevices.put(device.deviceAddress, device);
+ }
+
+ /** @hide */
+ public WifiP2pDevice get(String deviceAddress) {
+ if (deviceAddress == null) return null;
+
+ return mDevices.get(deviceAddress);
}
/** @hide */
public boolean remove(WifiP2pDevice device) {
- if (device == null) return false;
- return mDevices.remove(device);
+ if (device == null || device.deviceAddress == null) return false;
+ return mDevices.remove(device.deviceAddress) != null;
}
/** Get the list of devices */
public Collection<WifiP2pDevice> getDeviceList() {
- return Collections.unmodifiableCollection(mDevices);
+ return Collections.unmodifiableCollection(mDevices.values());
}
public String toString() {
StringBuffer sbuf = new StringBuffer();
- for (WifiP2pDevice device : mDevices) {
+ for (WifiP2pDevice device : mDevices.values()) {
sbuf.append("\n").append(device);
}
return sbuf.toString();
@@ -106,7 +116,7 @@ public class WifiP2pDeviceList implements Parcelable {
/** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mDevices.size());
- for(WifiP2pDevice device : mDevices) {
+ for(WifiP2pDevice device : mDevices.values()) {
dest.writeParcelable(device, flags);
}
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 9473993..c30cc73 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
/**
* A class representing a Wi-Fi P2p group
@@ -48,6 +50,15 @@ public class WifiP2pGroup implements Parcelable {
private String mInterface;
+ /** P2P group started string pattern */
+ private static final Pattern groupStartedPattern = Pattern.compile(
+ "ssid=\"(.+)\" " +
+ "freq=(\\d+) " +
+ "(?:psk=)?([0-9a-fA-F]{64})?" +
+ "(?:passphrase=)?(?:\"(.{8,63})\")? " +
+ "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+ );
+
public WifiP2pGroup() {
}
@@ -78,24 +89,18 @@ public class WifiP2pGroup implements Parcelable {
mInterface = tokens[1];
mIsGroupOwner = tokens[2].equals("GO");
- for (String token : tokens) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) continue;
-
- if (nameValue[0].equals("ssid")) {
- mNetworkName = nameValue[1];
- continue;
- }
+ Matcher match = groupStartedPattern.matcher(supplicantEvent);
+ if (!match.find()) {
+ return;
+ }
- if (nameValue[0].equals("passphrase")) {
- mPassphrase = nameValue[1];
- continue;
- }
+ mNetworkName = match.group(1);
+ //freq and psk are unused right now
+ //int freq = Integer.parseInt(match.group(2));
+ //String psk = match.group(3);
+ mPassphrase = match.group(4);
+ mOwner = new WifiP2pDevice(match.group(5));
- if (nameValue[0].equals("go_dev_addr")) {
- mOwner = new WifiP2pDevice(nameValue[1]);
- }
- }
} else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
for (String token : tokens) {
String[] nameValue = token.split("=");
@@ -172,6 +177,12 @@ public class WifiP2pGroup implements Parcelable {
return mClients.size() == 0;
}
+ /** @hide Returns {@code true} if the device is part of the group */
+ public boolean contains(WifiP2pDevice device) {
+ if (mOwner.equals(device) || mClients.contains(device)) return true;
+ return false;
+ }
+
/** Get the list of clients currently part of the p2p group */
public Collection<WifiP2pDevice> getClientList() {
return Collections.unmodifiableCollection(mClients);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 9205300..df14bb9 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -21,6 +21,14 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
+import android.net.nsd.DnsSdTxtRecord;
+import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
+import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse;
import android.os.Binder;
import android.os.IBinder;
import android.os.Handler;
@@ -36,6 +44,7 @@ import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import java.util.HashMap;
+import java.util.List;
/**
* This class provides the API for managing Wi-Fi peer-to-peer connectivity. This lets an
@@ -47,21 +56,20 @@ import java.util.HashMap;
* callbacks provided by the application. The application needs to do an initialization with
* {@link #initialize} before doing any p2p operation.
*
- * <p> Application actions {@link #discoverPeers}, {@link #connect}, {@link #cancelConnect},
- * {@link #createGroup} and {@link #removeGroup} need a {@link ActionListener} instance for
- * receiving callbacks {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}.
- * Action callbacks indicate whether the initiation of the action was a success or a failure.
+ * <p> Most application calls need a {@link ActionListener} instance for receiving callbacks
+ * {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}. Action callbacks
+ * indicate whether the initiation of the action was a success or a failure.
* Upon failure, the reason of failure can be one of {@link #ERROR}, {@link #P2P_UNSUPPORTED}
* or {@link #BUSY}.
*
* <p> An application can initiate discovery of peers with {@link #discoverPeers}. An initiated
* discovery request from an application stays active until the device starts connecting to a peer
- * or forms a p2p group. The {@link ActionListener} callbacks provide feedback on whether the
- * discovery initiation was successful or failure. Additionally, applications can listen
- * to {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent action to know when the peer list changes.
+ * ,forms a p2p group or there is an explicit {@link #stopPeerDiscovery}.
+ * Applications can listen to {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION} to know if a peer-to-peer
+ * discovery is running or stopped. Additionally, {@link #WIFI_P2P_PEERS_CHANGED_ACTION} indicates
+ * if the peer list has changed.
*
- * <p> When the peer list change intent {@link #WIFI_P2P_PEERS_CHANGED_ACTION} is received
- * or when an application needs to fetch the current list of peers, it can request the list
+ * <p> When an application needs to fetch the current list of peers, it can request the list
* of peers with {@link #requestPeers}. When the peer list is available
* {@link PeerListListener#onPeersAvailable} is called with the device list.
*
@@ -69,7 +77,7 @@ import java.util.HashMap;
* {@link WifiP2pConfig} for details on setting up the configuration. For communication with legacy
* Wi-Fi devices that do not support p2p, an app can create a group using {@link #createGroup}
* which creates an access point whose details can be fetched with {@link #requestGroupInfo}.
-*
+ *
* <p> After a successful group formation through {@link #createGroup} or through {@link #connect},
* use {@link #requestConnectionInfo} to fetch the connection details. The connection info
* {@link WifiP2pInfo} contains the address of the group owner
@@ -77,8 +85,36 @@ import java.util.HashMap;
* if the current device is a p2p group owner. A p2p client can thus communicate with
* the p2p group owner through a socket connection.
*
- * <p> Android has no platform support for service discovery yet, so applications could
- * run a service discovery protocol to discover services on the peer-to-peer netework.
+ * <p> With peer discovery using {@link #discoverPeers}, an application discovers the neighboring
+ * peers, but has no good way to figure out which peer to establish a connection with. For example,
+ * if a game application is interested in finding all the neighboring peers that are also running
+ * the same game, it has no way to find out until after the connection is setup. Pre-association
+ * service discovery is meant to address this issue of filtering the peers based on the running
+ * services.
+ *
+ * <p>With pre-association service discovery, an application can advertise a service for a
+ * application on a peer device prior to a connection setup between the devices.
+ * Currently, DNS based service discovery (Bonjour) and Upnp are the higher layer protocols
+ * supported. Get Bonjour resources at dns-sd.org and Upnp resources at upnp.org
+ * As an example, a video application can discover a Upnp capable media renderer
+ * prior to setting up a Wi-fi p2p connection with the device.
+ *
+ * <p> An application can advertise a Upnp or a Bonjour service with a call to
+ * {@link #addLocalService}. After a local service is added,
+ * the framework automatically responds to a peer application discovering the service prior
+ * to establishing a p2p connection. A call to {@link #removeLocalService} removes a local
+ * service and {@link #clearLocalServices} can be used to clear all local services.
+ *
+ * <p> An application that is looking for peer devices that support certain services
+ * can do so with a call to {@link #discoverServices}. Prior to initiating the discovery,
+ * application can add service discovery request with a call to {@link #addServiceRequest},
+ * remove a service discovery request with a call to {@link #removeServiceRequest} or clear
+ * all requests with a call to {@link #clearServiceRequests}. When no service requests remain,
+ * a previously running service discovery will stop.
+ *
+ * The application is notified of a result of service discovery request through listener callbacks
+ * set through {@link #setDnsSdResponseListeners} for Bonjour or
+ * {@link #setUpnpServiceResponseListener} for Upnp.
*
* <p class="note"><strong>Note:</strong>
* Registering an application handler with {@link #initialize} requires the permissions
@@ -182,6 +218,41 @@ public class WifiP2pManager {
"android.net.wifi.p2p.PEERS_CHANGED";
/**
+ * Broadcast intent action indicating that peer discovery has either started or stopped.
+ * One extra {@link #EXTRA_DISCOVERY_STATE} indicates whether discovery has started
+ * or stopped.
+ *
+ * Note that discovery will be stopped during a connection setup. If the application tries
+ * to re-initiate discovery during this time, it can fail.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_P2P_DISCOVERY_CHANGED_ACTION =
+ "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE";
+
+ /**
+ * The lookup key for an int that indicates whether p2p discovery has started or stopped.
+ * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
+ *
+ * @see #WIFI_P2P_DISCOVERY_STARTED
+ * @see #WIFI_P2P_DISCOVERY_STOPPED
+ */
+ public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
+
+ /**
+ * p2p discovery has stopped
+ *
+ * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION
+ */
+ public static final int WIFI_P2P_DISCOVERY_STOPPED = 1;
+
+ /**
+ * p2p discovery has started
+ *
+ * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION
+ */
+ public static final int WIFI_P2P_DISCOVERY_STARTED = 2;
+
+ /**
* Broadcast intent action indicating that this device details have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -199,68 +270,116 @@ public class WifiP2pManager {
private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
/** @hide */
- public static final int ENABLE_P2P = BASE + 1;
+ public static final int DISCOVER_PEERS = BASE + 1;
/** @hide */
- public static final int ENABLE_P2P_FAILED = BASE + 2;
+ public static final int DISCOVER_PEERS_FAILED = BASE + 2;
/** @hide */
- public static final int ENABLE_P2P_SUCCEEDED = BASE + 3;
+ public static final int DISCOVER_PEERS_SUCCEEDED = BASE + 3;
/** @hide */
- public static final int DISABLE_P2P = BASE + 4;
+ public static final int STOP_DISCOVERY = BASE + 4;
/** @hide */
- public static final int DISABLE_P2P_FAILED = BASE + 5;
+ public static final int STOP_DISCOVERY_FAILED = BASE + 5;
/** @hide */
- public static final int DISABLE_P2P_SUCCEEDED = BASE + 6;
+ public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 6;
/** @hide */
- public static final int DISCOVER_PEERS = BASE + 7;
+ public static final int CONNECT = BASE + 7;
/** @hide */
- public static final int DISCOVER_PEERS_FAILED = BASE + 8;
+ public static final int CONNECT_FAILED = BASE + 8;
/** @hide */
- public static final int DISCOVER_PEERS_SUCCEEDED = BASE + 9;
+ public static final int CONNECT_SUCCEEDED = BASE + 9;
/** @hide */
- public static final int CONNECT = BASE + 10;
+ public static final int CANCEL_CONNECT = BASE + 10;
/** @hide */
- public static final int CONNECT_FAILED = BASE + 11;
+ public static final int CANCEL_CONNECT_FAILED = BASE + 11;
/** @hide */
- public static final int CONNECT_SUCCEEDED = BASE + 12;
+ public static final int CANCEL_CONNECT_SUCCEEDED = BASE + 12;
/** @hide */
- public static final int CANCEL_CONNECT = BASE + 13;
+ public static final int CREATE_GROUP = BASE + 13;
/** @hide */
- public static final int CANCEL_CONNECT_FAILED = BASE + 14;
+ public static final int CREATE_GROUP_FAILED = BASE + 14;
/** @hide */
- public static final int CANCEL_CONNECT_SUCCEEDED = BASE + 15;
+ public static final int CREATE_GROUP_SUCCEEDED = BASE + 15;
/** @hide */
- public static final int CREATE_GROUP = BASE + 16;
+ public static final int REMOVE_GROUP = BASE + 16;
/** @hide */
- public static final int CREATE_GROUP_FAILED = BASE + 17;
+ public static final int REMOVE_GROUP_FAILED = BASE + 17;
/** @hide */
- public static final int CREATE_GROUP_SUCCEEDED = BASE + 18;
+ public static final int REMOVE_GROUP_SUCCEEDED = BASE + 18;
/** @hide */
- public static final int REMOVE_GROUP = BASE + 19;
+ public static final int REQUEST_PEERS = BASE + 19;
+ /** @hide */
+ public static final int RESPONSE_PEERS = BASE + 20;
+
/** @hide */
- public static final int REMOVE_GROUP_FAILED = BASE + 20;
+ public static final int REQUEST_CONNECTION_INFO = BASE + 21;
/** @hide */
- public static final int REMOVE_GROUP_SUCCEEDED = BASE + 21;
+ public static final int RESPONSE_CONNECTION_INFO = BASE + 22;
/** @hide */
- public static final int REQUEST_PEERS = BASE + 22;
+ public static final int REQUEST_GROUP_INFO = BASE + 23;
/** @hide */
- public static final int RESPONSE_PEERS = BASE + 23;
+ public static final int RESPONSE_GROUP_INFO = BASE + 24;
/** @hide */
- public static final int REQUEST_CONNECTION_INFO = BASE + 24;
+ public static final int ADD_LOCAL_SERVICE = BASE + 28;
+ /** @hide */
+ public static final int ADD_LOCAL_SERVICE_FAILED = BASE + 29;
/** @hide */
- public static final int RESPONSE_CONNECTION_INFO = BASE + 25;
+ public static final int ADD_LOCAL_SERVICE_SUCCEEDED = BASE + 30;
/** @hide */
- public static final int REQUEST_GROUP_INFO = BASE + 26;
+ public static final int REMOVE_LOCAL_SERVICE = BASE + 31;
+ /** @hide */
+ public static final int REMOVE_LOCAL_SERVICE_FAILED = BASE + 32;
/** @hide */
- public static final int RESPONSE_GROUP_INFO = BASE + 27;
+ public static final int REMOVE_LOCAL_SERVICE_SUCCEEDED = BASE + 33;
+
+ /** @hide */
+ public static final int CLEAR_LOCAL_SERVICES = BASE + 34;
+ /** @hide */
+ public static final int CLEAR_LOCAL_SERVICES_FAILED = BASE + 35;
+ /** @hide */
+ public static final int CLEAR_LOCAL_SERVICES_SUCCEEDED = BASE + 36;
+
+ /** @hide */
+ public static final int ADD_SERVICE_REQUEST = BASE + 37;
+ /** @hide */
+ public static final int ADD_SERVICE_REQUEST_FAILED = BASE + 38;
+ /** @hide */
+ public static final int ADD_SERVICE_REQUEST_SUCCEEDED = BASE + 39;
+
+ /** @hide */
+ public static final int REMOVE_SERVICE_REQUEST = BASE + 40;
+ /** @hide */
+ public static final int REMOVE_SERVICE_REQUEST_FAILED = BASE + 41;
+ /** @hide */
+ public static final int REMOVE_SERVICE_REQUEST_SUCCEEDED = BASE + 42;
+
+ /** @hide */
+ public static final int CLEAR_SERVICE_REQUESTS = BASE + 43;
+ /** @hide */
+ public static final int CLEAR_SERVICE_REQUESTS_FAILED = BASE + 44;
+ /** @hide */
+ public static final int CLEAR_SERVICE_REQUESTS_SUCCEEDED = BASE + 45;
+
+ /** @hide */
+ public static final int DISCOVER_SERVICES = BASE + 46;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_FAILED = BASE + 47;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_SUCCEEDED = BASE + 48;
+
+ /** @hide */
+ public static final int PING = BASE + 49;
+
+ /** @hide */
+ public static final int RESPONSE_SERVICE = BASE + 50;
/**
* Create a new WifiP2pManager instance. Applications use
@@ -293,6 +412,14 @@ public class WifiP2pManager {
*/
public static final int BUSY = 2;
+ /**
+ * Passed with {@link ActionListener#onFailure}.
+ * Indicates that the {@link #discoverServices} failed because no service
+ * requests are set.
+ * @hide
+ */
+ public static final int NO_SERVICE_REQUESTS = 3;
+
/** Interface for callback invocation when framework channel is lost */
public interface ChannelListener {
/**
@@ -342,6 +469,89 @@ public class WifiP2pManager {
}
/**
+ * Interface for callback invocation when service discovery response other than
+ * Upnp or Bonjour is received
+ */
+ public interface ServiceResponseListener {
+
+ /**
+ * The requested service response is available.
+ *
+ * @param protocolType protocol type. currently only
+ * {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
+ * @param responseData service discovery response data based on the requested
+ * service protocol type. The format depends on the service type.
+ * @param srcDevice source device.
+ */
+ public void onServiceAvailable(int protocolType,
+ byte[] responseData, WifiP2pDevice srcDevice);
+ }
+
+ /**
+ * Interface for callback invocation when Bonjour service discovery response
+ * is received
+ */
+ public interface DnsSdServiceResponseListener {
+
+ /**
+ * The requested Bonjour service response is available.
+ *
+ * <p>This function is invoked when the device with the specified Bonjour
+ * registration type returned the instance name.
+ * @param instanceName instance name.<br>
+ * e.g) "MyPrinter".
+ * @param registrationType <br>
+ * e.g) "_ipp._tcp.local."
+ * @param srcDevice source device.
+ */
+ public void onDnsSdServiceAvailable(String instanceName,
+ String registrationType, WifiP2pDevice srcDevice);
+
+ }
+
+ /**
+ * Interface for callback invocation when Bonjour TXT record is available
+ * for a service
+ */
+ public interface DnsSdTxtRecordListener {
+ /**
+ * The requested Bonjour service response is available.
+ *
+ * <p>This function is invoked when the device with the specified full
+ * service domain service returned TXT record.
+ *
+ * @param fullDomainName full domain name. <br>
+ * e.g) "MyPrinter._ipp._tcp.local.".
+ * @param record txt record.
+ * @param srcDevice source device.
+ */
+ public void onDnsSdTxtRecordAvailable(String fullDomainName,
+ DnsSdTxtRecord record,
+ WifiP2pDevice srcDevice);
+ }
+
+ /**
+ * Interface for callback invocation when upnp service discovery response
+ * is received
+ * */
+ public interface UpnpServiceResponseListener {
+
+ /**
+ * The requested upnp service response is available.
+ *
+ * <p>This function is invoked when the specified device or service is found.
+ *
+ * @param uniqueServiceNames The list of unique service names.<br>
+ * e.g) uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:
+ * MediaServer:1
+ * @param srcDevice source device.
+ */
+ public void onUpnpServiceAvailable(List<String> uniqueServiceNames,
+ WifiP2pDevice srcDevice);
+ }
+
+
+ /**
* A channel that connects the application to the Wifi p2p framework.
* Most p2p operations require a Channel as an argument. An instance of Channel is obtained
* by doing a call on {@link #initialize}
@@ -352,7 +562,12 @@ public class WifiP2pManager {
mHandler = new P2pHandler(looper);
mChannelListener = l;
}
+ private final static int INVALID_LISTENER_KEY = 0;
private ChannelListener mChannelListener;
+ private ServiceResponseListener mServRspListener;
+ private DnsSdServiceResponseListener mDnsSdServRspListener;
+ private DnsSdTxtRecordListener mDnsSdTxtListener;
+ private UpnpServiceResponseListener mUpnpServRspListener;
private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
private Object mListenerMapLock = new Object();
private int mListenerKey = 0;
@@ -376,20 +591,36 @@ public class WifiP2pManager {
break;
/* ActionListeners grouped together */
case WifiP2pManager.DISCOVER_PEERS_FAILED:
+ case WifiP2pManager.STOP_DISCOVERY_FAILED:
+ case WifiP2pManager.DISCOVER_SERVICES_FAILED:
case WifiP2pManager.CONNECT_FAILED:
case WifiP2pManager.CANCEL_CONNECT_FAILED:
case WifiP2pManager.CREATE_GROUP_FAILED:
case WifiP2pManager.REMOVE_GROUP_FAILED:
+ case WifiP2pManager.ADD_LOCAL_SERVICE_FAILED:
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED:
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED:
+ case WifiP2pManager.ADD_SERVICE_REQUEST_FAILED:
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED:
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
break;
/* ActionListeners grouped together */
case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
+ case WifiP2pManager.STOP_DISCOVERY_SUCCEEDED:
+ case WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED:
case WifiP2pManager.CONNECT_SUCCEEDED:
case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
case WifiP2pManager.REMOVE_GROUP_SUCCEEDED:
+ case WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED:
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED:
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED:
+ case WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED:
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED:
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -412,6 +643,10 @@ public class WifiP2pManager {
((GroupInfoListener) listener).onGroupInfoAvailable(group);
}
break;
+ case WifiP2pManager.RESPONSE_SERVICE:
+ WifiP2pServiceResponse resp = (WifiP2pServiceResponse) message.obj;
+ handleServiceResponse(resp);
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -419,23 +654,78 @@ public class WifiP2pManager {
}
}
- int putListener(Object listener) {
- if (listener == null) return 0;
+ private void handleServiceResponse(WifiP2pServiceResponse resp) {
+ if (resp instanceof WifiP2pDnsSdServiceResponse) {
+ handleDnsSdServiceResponse((WifiP2pDnsSdServiceResponse)resp);
+ } else if (resp instanceof WifiP2pUpnpServiceResponse) {
+ if (mUpnpServRspListener != null) {
+ handleUpnpServiceResponse((WifiP2pUpnpServiceResponse)resp);
+ }
+ } else {
+ if (mServRspListener != null) {
+ mServRspListener.onServiceAvailable(resp.getServiceType(),
+ resp.getRawData(), resp.getSrcDevice());
+ }
+ }
+ }
+
+ private void handleUpnpServiceResponse(WifiP2pUpnpServiceResponse resp) {
+ mUpnpServRspListener.onUpnpServiceAvailable(resp.getUniqueServiceNames(),
+ resp.getSrcDevice());
+ }
+
+ private void handleDnsSdServiceResponse(WifiP2pDnsSdServiceResponse resp) {
+ if (resp.getDnsType() == WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR) {
+ if (mDnsSdServRspListener != null) {
+ mDnsSdServRspListener.onDnsSdServiceAvailable(
+ resp.getInstanceName(),
+ resp.getDnsQueryName(),
+ resp.getSrcDevice());
+ }
+ } else if (resp.getDnsType() == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
+ if (mDnsSdTxtListener != null) {
+ mDnsSdTxtListener.onDnsSdTxtRecordAvailable(
+ resp.getDnsQueryName(),
+ resp.getTxtRecord(),
+ resp.getSrcDevice());
+ }
+ } else {
+ Log.e(TAG, "Unhandled resp " + resp);
+ }
+ }
+
+ private int putListener(Object listener) {
+ if (listener == null) return INVALID_LISTENER_KEY;
int key;
synchronized (mListenerMapLock) {
- key = mListenerKey++;
+ do {
+ key = mListenerKey++;
+ } while (key == INVALID_LISTENER_KEY);
mListenerMap.put(key, listener);
}
return key;
}
- Object getListener(int key) {
+ private Object getListener(int key) {
+ if (key == INVALID_LISTENER_KEY) return null;
synchronized (mListenerMapLock) {
return mListenerMap.remove(key);
}
}
}
+ private static void checkChannel(Channel c) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ }
+
+ private static void checkServiceInfo(WifiP2pServiceInfo info) {
+ if (info == null) throw new IllegalArgumentException("service info is null");
+ }
+
+ private static void checkServiceRequest(WifiP2pServiceRequest req) {
+ if (req == null) throw new IllegalArgumentException("service request is null");
+ }
+
/**
* Registers the application with the Wi-Fi framework. This function
* must be the first to be called before any p2p operations are performed.
@@ -459,26 +749,6 @@ public class WifiP2pManager {
}
/**
- * Sends in a request to the system to enable p2p. This will pop up a dialog
- * to the user and upon authorization will enable p2p.
- * @hide
- */
- public void enableP2p(Channel c) {
- if (c == null) return;
- c.mAsyncChannel.sendMessage(ENABLE_P2P);
- }
-
- /**
- * Sends in a request to the system to disable p2p. This will pop up a dialog
- * to the user and upon authorization will enable p2p.
- * @hide
- */
- public void disableP2p(Channel c) {
- if (c == null) return;
- c.mAsyncChannel.sendMessage(DISABLE_P2P);
- }
-
- /**
* Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers
* for the purpose of establishing a connection.
*
@@ -498,10 +768,26 @@ public class WifiP2pManager {
* @param listener for callbacks on success or failure. Can be null.
*/
public void discoverPeers(Channel c, ActionListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
}
+ /**
+ * Stop an ongoing peer discovery
+ *
+ * <p> The function call immediately returns after sending a stop request
+ * to the framework. The application is notified of a success or failure to initiate
+ * stop through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void stopPeerDiscovery(Channel c, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, c.putListener(listener));
+ }
+
/**
* Start a p2p connection to a device with the specified configuration.
*
@@ -525,7 +811,7 @@ public class WifiP2pManager {
* @param listener for callbacks on success or failure. Can be null.
*/
public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config);
}
@@ -541,7 +827,7 @@ public class WifiP2pManager {
* @param listener for callbacks on success or failure. Can be null.
*/
public void cancelConnect(Channel c, ActionListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(CANCEL_CONNECT, 0, c.putListener(listener));
}
@@ -565,7 +851,7 @@ public class WifiP2pManager {
* @param listener for callbacks on success or failure. Can be null.
*/
public void createGroup(Channel c, ActionListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, c.putListener(listener));
}
@@ -581,18 +867,217 @@ public class WifiP2pManager {
* @param listener for callbacks on success or failure. Can be null.
*/
public void removeGroup(Channel c, ActionListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
}
/**
+ * Register a local service for service discovery. If a local service is registered,
+ * the framework automatically responds to a service discovery request from a peer.
+ *
+ * <p> The function call immediately returns after sending a request to add a local
+ * service to the framework. The application is notified of a success or failure to
+ * add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p>The service information is set through {@link WifiP2pServiceInfo}.<br>
+ * or its subclass calls {@link WifiP2pUpnpServiceInfo#newInstance} or
+ * {@link WifiP2pDnsSdServiceInfo#newInstance} for a Upnp or Bonjour service
+ * respectively
+ *
+ * <p>The service information can be cleared with calls to
+ * {@link #removeLocalService} or {@link #clearLocalServices}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param servInfo is a local service information.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void addLocalService(Channel c, WifiP2pServiceInfo servInfo, ActionListener listener) {
+ checkChannel(c);
+ checkServiceInfo(servInfo);
+ c.mAsyncChannel.sendMessage(ADD_LOCAL_SERVICE, 0, c.putListener(listener), servInfo);
+ }
+
+ /**
+ * Remove a registered local service added with {@link #addLocalService}
+ *
+ * <p> The function call immediately returns after sending a request to remove a
+ * local service to the framework. The application is notified of a success or failure to
+ * add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param servInfo is the local service information.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void removeLocalService(Channel c, WifiP2pServiceInfo servInfo,
+ ActionListener listener) {
+ checkChannel(c);
+ checkServiceInfo(servInfo);
+ c.mAsyncChannel.sendMessage(REMOVE_LOCAL_SERVICE, 0, c.putListener(listener), servInfo);
+ }
+
+ /**
+ * Clear all registered local services of service discovery.
+ *
+ * <p> The function call immediately returns after sending a request to clear all
+ * local services to the framework. The application is notified of a success or failure to
+ * add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void clearLocalServices(Channel c, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(CLEAR_LOCAL_SERVICES, 0, c.putListener(listener));
+ }
+
+ /**
+ * Register a callback to be invoked on receiving service discovery response.
+ * Used only for vendor specific protocol right now. For Bonjour or Upnp, use
+ * {@link #setDnsSdResponseListeners} or {@link #setUpnpServiceResponseListener}
+ * respectively.
+ *
+ * <p> see {@link #discoverServices} for the detail.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on receiving service discovery response.
+ */
+ public void setServiceResponseListener(Channel c,
+ ServiceResponseListener listener) {
+ checkChannel(c);
+ c.mServRspListener = listener;
+ }
+
+ /**
+ * Register a callback to be invoked on receiving Bonjour service discovery
+ * response.
+ *
+ * <p> see {@link #discoverServices} for the detail.
+ *
+ * @param c
+ * @param servListener is for listening to a Bonjour service response
+ * @param txtListener is for listening to a Bonjour TXT record response
+ */
+ public void setDnsSdResponseListeners(Channel c,
+ DnsSdServiceResponseListener servListener, DnsSdTxtRecordListener txtListener) {
+ checkChannel(c);
+ c.mDnsSdServRspListener = servListener;
+ c.mDnsSdTxtListener = txtListener;
+ }
+
+ /**
+ * Register a callback to be invoked on receiving upnp service discovery
+ * response.
+ *
+ * <p> see {@link #discoverServices} for the detail.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on receiving service discovery response.
+ */
+ public void setUpnpServiceResponseListener(Channel c,
+ UpnpServiceResponseListener listener) {
+ checkChannel(c);
+ c.mUpnpServRspListener = listener;
+ }
+
+ /**
+ * Initiate service discovery. A discovery process involves scanning for
+ * requested services for the purpose of establishing a connection to a peer
+ * that supports an available service.
+ *
+ * <p> The function call immediately returns after sending a request to start service
+ * discovery to the framework. The application is notified of a success or failure to initiate
+ * discovery through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p> The services to be discovered are specified with calls to {@link #addServiceRequest}.
+ *
+ * <p>The application is notified of the response against the service discovery request
+ * through listener callbacks registered by {@link #setServiceResponseListener} or
+ * {@link #setDnsSdResponseListeners}, or {@link #setUpnpServiceResponseListener}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void discoverServices(Channel c, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, c.putListener(listener));
+ }
+
+ /**
+ * Add a service discovery request.
+ *
+ * <p> The function call immediately returns after sending a request to add service
+ * discovery request to the framework. The application is notified of a success or failure to
+ * add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p>After service discovery request is added, you can initiate service discovery by
+ * {@link #discoverServices}.
+ *
+ * <p>The added service requests can be cleared with calls to
+ * {@link #removeServiceRequest(Channel, WifiP2pServiceRequest, ActionListener)} or
+ * {@link #clearServiceRequests(Channel, ActionListener)}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param req is the service discovery request.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void addServiceRequest(Channel c,
+ WifiP2pServiceRequest req, ActionListener listener) {
+ checkChannel(c);
+ checkServiceRequest(req);
+ c.mAsyncChannel.sendMessage(ADD_SERVICE_REQUEST, 0,
+ c.putListener(listener), req);
+ }
+
+ /**
+ * Remove a specified service discovery request added with {@link #addServiceRequest}
+ *
+ * <p> The function call immediately returns after sending a request to remove service
+ * discovery request to the framework. The application is notified of a success or failure to
+ * add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param req is the service discovery request.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void removeServiceRequest(Channel c, WifiP2pServiceRequest req,
+ ActionListener listener) {
+ checkChannel(c);
+ checkServiceRequest(req);
+ c.mAsyncChannel.sendMessage(REMOVE_SERVICE_REQUEST, 0,
+ c.putListener(listener), req);
+ }
+
+ /**
+ * Clear all registered service discovery requests.
+ *
+ * <p> The function call immediately returns after sending a request to clear all
+ * service discovery requests to the framework. The application is notified of a success
+ * or failure to add service through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void clearServiceRequests(Channel c, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(CLEAR_SERVICE_REQUESTS,
+ 0, c.putListener(listener));
+ }
+
+ /**
* Request the current list of peers.
*
* @param c is the channel created at {@link #initialize}
* @param listener for callback when peer list is available. Can be null.
*/
public void requestPeers(Channel c, PeerListListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
}
@@ -603,7 +1088,7 @@ public class WifiP2pManager {
* @param listener for callback when connection info is available. Can be null.
*/
public void requestConnectionInfo(Channel c, ConnectionInfoListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_CONNECTION_INFO, 0, c.putListener(listener));
}
@@ -614,7 +1099,7 @@ public class WifiP2pManager {
* @param listener for callback when group info is available. Can be null.
*/
public void requestGroupInfo(Channel c, GroupInfoListener listener) {
- if (c == null) return;
+ checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_GROUP_INFO, 0, c.putListener(listener));
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
new file mode 100644
index 0000000..f70abe8
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 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.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+/**
+ * A class representing a Wi-Fi p2p provisional discovery request/response
+ * See {@link #WifiP2pProvDiscEvent} for supported types
+ *
+ * @hide
+ */
+public class WifiP2pProvDiscEvent {
+
+ private static final String TAG = "WifiP2pProvDiscEvent";
+
+ public static final int PBC_REQ = 1;
+ public static final int PBC_RSP = 2;
+ public static final int ENTER_PIN = 3;
+ public static final int SHOW_PIN = 4;
+
+ /* One of PBC_REQ, PBC_RSP, ENTER_PIN or SHOW_PIN */
+ public int event;
+
+ public WifiP2pDevice device;
+
+ /* Valid when event = SHOW_PIN */
+ public String pin;
+
+ public WifiP2pProvDiscEvent() {
+ device = new WifiP2pDevice();
+ }
+
+ /**
+ * @param string formats supported include
+ *
+ * P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * P2P-PROV-DISC-PBC-RESP 02:12:47:f2:5a:36
+ *
+ * P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * Note: The events formats can be looked up in the wpa_supplicant code
+ * @hide
+ */
+ public WifiP2pProvDiscEvent(String string) throws IllegalArgumentException {
+ String[] tokens = string.split(" ");
+
+ if (tokens.length < 2) {
+ throw new IllegalArgumentException("Malformed event " + string);
+ }
+
+ if (tokens[0].endsWith("PBC-REQ")) event = PBC_REQ;
+ else if (tokens[0].endsWith("PBC-RESP")) event = PBC_RSP;
+ else if (tokens[0].endsWith("ENTER-PIN")) event = ENTER_PIN;
+ else if (tokens[0].endsWith("SHOW-PIN")) event = SHOW_PIN;
+ else throw new IllegalArgumentException("Malformed event " + string);
+
+ device = new WifiP2pDevice();
+
+ for (String token : tokens) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) {
+ //mac address without key is device address
+ if (token.matches("(([0-9a-f]{2}:){5}[0-9a-f]{2})")) {
+ device.deviceAddress = token;
+ } else if (token.matches("[0-9]+")) {
+ pin = token;
+ } else {
+ //ignore;
+ }
+ continue;
+ }
+
+ if (nameValue[0].equals("p2p_dev_addr")) {
+ device.deviceAddress = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("pri_dev_type")) {
+ device.primaryDeviceType = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("name")) {
+ device.deviceName = trimQuotes(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("config_methods")) {
+ device.wpsConfigMethodsSupported = parseHex(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("dev_capab")) {
+ device.deviceCapability = parseHex(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("group_capab")) {
+ device.groupCapability = parseHex(nameValue[1]);
+ continue;
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append(device);
+ sbuf.append("\n event: ").append(event);
+ sbuf.append("\n pin: ").append(pin);
+ return sbuf.toString();
+ }
+
+ private String trimQuotes(String str) {
+ str = str.trim();
+ if (str.startsWith("'") && str.endsWith("'")) {
+ return str.substring(1, str.length()-1);
+ }
+ return str;
+ }
+
+ //supported formats: 0x1abc, 0X1abc, 1abc
+ private int parseHex(String hexString) {
+ int num = 0;
+ if (hexString.startsWith("0x") || hexString.startsWith("0X")) {
+ hexString = hexString.substring(2);
+ }
+
+ try {
+ num = Integer.parseInt(hexString, 16);
+ } catch(NumberFormatException e) {
+ Log.e(TAG, "Failed to parse hex string " + hexString);
+ }
+ return num;
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 6bb22a4..6168f0e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -42,6 +42,9 @@ import android.net.wifi.WifiMonitor;
import android.net.wifi.WifiNative;
import android.net.wifi.WifiStateMachine;
import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.os.Binder;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -49,14 +52,19 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.telephony.TelephonyIntents;
@@ -67,7 +75,10 @@ import com.android.internal.util.StateMachine;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
/**
* WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
@@ -91,18 +102,22 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
INetworkManagementService mNwService;
private DhcpStateMachine mDhcpStateMachine;
- //Tracked to notify the user about wifi client/hotspot being shut down
- //during p2p bring up
- private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
- private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
-
private P2pStateMachine mP2pStateMachine;
private AsyncChannel mReplyChannel = new AsyncChannel();
private AsyncChannel mWifiChannel;
+ private static final Boolean JOIN_GROUP = true;
+ private static final Boolean FORM_GROUP = false;
+
/* Two minutes comes from the wpa_supplicant setting */
- private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 120 * 1000;
- private static int mGroupNegotiationTimeoutIndex = 0;
+ private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
+ private static int mGroupCreatingTimeoutIndex = 0;
+
+ /* Set a two minute discover timeout to avoid STA scans from being blocked */
+ private static final int DISCOVER_TIMEOUT_S = 120;
+
+ /* Idle time after a peer is gone when the group is torn down */
+ private static final int GROUP_IDLE_TIME_S = 2;
/**
* Delay between restarts upon failure to setup connection with supplicant
@@ -118,35 +133,13 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
- /* Message sent to WifiStateMachine to indicate p2p enable is pending */
- public static final int P2P_ENABLE_PENDING = BASE + 1;
- /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
- public static final int WIFI_ENABLE_PROCEED = BASE + 2;
-
- /* Delayed message to timeout of group negotiation */
- public static final int GROUP_NEGOTIATION_TIMED_OUT = BASE + 3;
-
- /* User accepted to disable Wi-Fi in order to enable p2p */
- private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 4;
- /* User rejected to disable Wi-Fi in order to enable p2p */
- private static final int WIFI_DISABLE_USER_REJECT = BASE + 5;
-
- /* User accepted a group negotiation request */
- private static final int GROUP_NEGOTIATION_USER_ACCEPT = BASE + 6;
- /* User rejected a group negotiation request */
- private static final int GROUP_NEGOTIATION_USER_REJECT = BASE + 7;
-
- /* User accepted a group invitation request */
- private static final int GROUP_INVITATION_USER_ACCEPT = BASE + 8;
- /* User rejected a group invitation request */
- private static final int GROUP_INVITATION_USER_REJECT = BASE + 9;
+ /* Delayed message to timeout group creation */
+ public static final int GROUP_CREATING_TIMED_OUT = BASE + 1;
- /* Airplane mode changed */
- private static final int AIRPLANE_MODE_CHANGED = BASE + 10;
- /* Emergency callback mode */
- private static final int EMERGENCY_CALLBACK_MODE = BASE + 11;
- private static final int WPS_PBC = BASE + 12;
- private static final int WPS_PIN = BASE + 13;
+ /* User accepted a peer request */
+ private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 2;
+ /* User rejected a peer request */
+ private static final int PEER_CONNECTION_USER_REJECT = BASE + 3;
private final boolean mP2pSupported;
@@ -157,8 +150,23 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
* is invoked */
private boolean mPersistGroup;
+ /* Track whether we are in p2p discovery. This is used to avoid sending duplicate
+ * broadcasts
+ */
+ private boolean mDiscoveryStarted;
+
private NetworkInfo mNetworkInfo;
+ /* The transaction Id of service discovery request */
+ private byte mServiceTransactionId = 0;
+
+ /* Service discovery request ID of wpa_supplicant.
+ * null means it's not set yet. */
+ private String mServiceDiscReqId;
+
+ /* clients(application) information list. */
+ private HashMap<Messenger, ClientInfo> mClientInfoList = new HashMap<Messenger, ClientInfo>();
+
/* Is chosen as a unique range to avoid conflict with
the range defined in Tethering.java */
private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
@@ -167,7 +175,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
public WifiP2pService(Context context) {
mContext = context;
- mInterface = SystemProperties.get("wifi.interface", "wlan0");
+ //STOPSHIP: get this from native side
+ mInterface = "p2p0";
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
@@ -179,15 +188,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
mP2pStateMachine.start();
-
- // broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- mContext.registerReceiver(new WifiStateReceiver(), filter);
-
}
public void connectivityServiceReady() {
@@ -195,26 +195,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
mNwService = INetworkManagementService.Stub.asInterface(b);
}
- private class WifiStateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_DISABLED);
- } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
- mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
- WifiManager.WIFI_AP_STATE_DISABLED);
- } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
- mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
- } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
- if (intent.getBooleanExtra("phoneinECMState", false) == true) {
- mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
- }
- }
- }
- }
-
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
"WifiP2pService");
@@ -264,30 +244,28 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
- private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
- private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
// Inactive is when p2p is enabled with no connectivity
private InactiveState mInactiveState = new InactiveState();
- private UserAuthorizingGroupNegotiationState mUserAuthorizingGroupNegotiationState
- = new UserAuthorizingGroupNegotiationState();
- private UserAuthorizingGroupInvitationState mUserAuthorizingGroupInvitationState
- = new UserAuthorizingGroupInvitationState();
+ private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
+ private UserAuthorizingInvitationState mUserAuthorizingInvitationState
+ = new UserAuthorizingInvitationState();
+ private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
+
private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
+ private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
- private WifiMonitor mWifiMonitor = new WifiMonitor(this);
+ private WifiNative mWifiNative = new WifiNative(mInterface);
+ private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
private WifiP2pGroup mGroup;
- // Saved WifiP2pConfig from GO negotiation request
- private WifiP2pConfig mSavedGoNegotiationConfig;
-
- // Saved WifiP2pConfig from connect request
- private WifiP2pConfig mSavedConnectConfig;
+ // Saved WifiP2pConfig for a peer connection
+ private WifiP2pConfig mSavedPeerConfig;
// Saved WifiP2pGroup from invitation request
private WifiP2pGroup mSavedP2pGroup;
@@ -299,15 +277,15 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
addState(mP2pNotSupportedState, mDefaultState);
addState(mP2pDisablingState, mDefaultState);
addState(mP2pDisabledState, mDefaultState);
- addState(mWaitForUserActionState, mP2pDisabledState);
- addState(mWaitForWifiDisableState, mP2pDisabledState);
addState(mP2pEnablingState, mDefaultState);
addState(mP2pEnabledState, mDefaultState);
addState(mInactiveState, mP2pEnabledState);
- addState(mUserAuthorizingGroupNegotiationState, mInactiveState);
- addState(mUserAuthorizingGroupInvitationState, mInactiveState);
- addState(mGroupNegotiationState, mP2pEnabledState);
+ addState(mGroupCreatingState, mP2pEnabledState);
+ addState(mUserAuthorizingInvitationState, mGroupCreatingState);
+ addState(mProvisionDiscoveryState, mGroupCreatingState);
+ addState(mGroupNegotiationState, mGroupCreatingState);
addState(mGroupCreatedState, mP2pEnabledState);
+ addState(mUserAuthorizingJoinState, mGroupCreatedState);
if (p2pSupported) {
setInitialState(mP2pDisabledState);
@@ -344,21 +322,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, getHandler(), message.replyTo);
break;
- case WifiStateMachine.WIFI_ENABLE_PENDING:
- // Disable p2p operation before we can respond
- sendMessage(WifiP2pManager.DISABLE_P2P);
- deferMessage(message);
- break;
- case WifiP2pManager.ENABLE_P2P:
- replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
+ case WifiP2pManager.DISCOVER_PEERS:
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.BUSY);
break;
- case WifiP2pManager.DISABLE_P2P:
- replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
+ case WifiP2pManager.STOP_DISCOVERY:
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
WifiP2pManager.BUSY);
break;
- case WifiP2pManager.DISCOVER_PEERS:
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ case WifiP2pManager.DISCOVER_SERVICES:
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
WifiP2pManager.BUSY);
break;
case WifiP2pManager.CONNECT:
@@ -377,6 +350,32 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
WifiP2pManager.BUSY);
break;
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ replyToMessage(message,
+ WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ replyToMessage(message,
+ WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
+ WifiP2pManager.BUSY);
+ break;
case WifiP2pManager.REQUEST_PEERS:
replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
break;
@@ -386,20 +385,28 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
case WifiP2pManager.REQUEST_GROUP_INFO:
replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
break;
- case AIRPLANE_MODE_CHANGED:
- if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
+ // Ignore
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SUP_CONNECTION_EVENT:
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
+ case PEER_CONNECTION_USER_ACCEPT:
+ case PEER_CONNECTION_USER_REJECT:
+ case GROUP_CREATING_TIMED_OUT:
break;
- case EMERGENCY_CALLBACK_MODE:
- sendMessage(WifiP2pManager.DISABLE_P2P);
+ /* unexpected group created, remove */
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ mGroup = (WifiP2pGroup) message.obj;
+ loge("Unexpected group creation, remove " + mGroup);
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
break;
- // Ignore
- case WIFI_DISABLE_USER_ACCEPT:
- case WIFI_DISABLE_USER_REJECT:
- case GROUP_NEGOTIATION_USER_ACCEPT:
- case GROUP_NEGOTIATION_USER_REJECT:
- case GROUP_INVITATION_USER_ACCEPT:
- case GROUP_INVITATION_USER_REJECT:
- case GROUP_NEGOTIATION_TIMED_OUT:
+ case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ loge("Unexpected group failure, flush peers");
+ mWifiNative.p2pFlush();
break;
default:
loge("Unhandled message " + message);
@@ -413,20 +420,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
- // Allow Wi-Fi to proceed
- case WifiStateMachine.WIFI_ENABLE_PENDING:
- replyToMessage(message, WIFI_ENABLE_PROCEED);
- break;
- case WifiP2pManager.ENABLE_P2P:
- replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
+ case WifiP2pManager.DISCOVER_PEERS:
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiP2pManager.DISABLE_P2P:
- replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
+ case WifiP2pManager.STOP_DISCOVERY:
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiP2pManager.DISCOVER_PEERS:
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ case WifiP2pManager.DISCOVER_SERVICES:
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
case WifiP2pManager.CONNECT:
@@ -437,7 +440,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiP2pManager.CREATE_GROUP:
+ case WifiP2pManager.CREATE_GROUP:
replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
@@ -445,120 +448,50 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class P2pDisablingState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- logd("stopping supplicant");
- if (!WifiNative.stopSupplicant()) {
- loge("Failed to stop supplicant, issue kill");
- WifiNative.killSupplicant();
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- logd("Supplicant connection lost");
- WifiNative.closeSupplicantConnection();
- transitionTo(mP2pDisabledState);
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiP2pManager.ENABLE_P2P:
- case WifiP2pManager.DISABLE_P2P:
- deferMessage(message);
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
-
- class P2pDisabledState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiP2pManager.ENABLE_P2P:
- OnClickListener listener = new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- sendMessage(WIFI_DISABLE_USER_ACCEPT);
- } else {
- sendMessage(WIFI_DISABLE_USER_REJECT);
- }
- }
- };
-
- // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
- if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
- mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
- Resources r = Resources.getSystem();
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
- .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
- .setPositiveButton(r.getString(R.string.ok), listener)
- .setNegativeButton(r.getString(R.string.cancel), listener)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- transitionTo(mWaitForUserActionState);
- } else {
- mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
- transitionTo(mWaitForWifiDisableState);
- }
- replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiP2pManager.DISABLE_P2P:
- replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
break;
- case WifiStateMachine.WIFI_ENABLE_PENDING:
- replyToMessage(message, WIFI_ENABLE_PROCEED);
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ replyToMessage(message,
+ WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
break;
- default:
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ replyToMessage(message,
+ WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ default:
return NOT_HANDLED;
}
return HANDLED;
}
}
- class WaitForUserActionState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
+ class P2pDisablingState extends State {
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
- case WIFI_DISABLE_USER_ACCEPT:
- mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
- transitionTo(mWaitForWifiDisableState);
- break;
- case WIFI_DISABLE_USER_REJECT:
- logd("User rejected enabling p2p");
- sendP2pStateChangedBroadcast(false);
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
+ if (DBG) logd("p2p socket connection lost");
transitionTo(mP2pDisabledState);
break;
- case WifiP2pManager.ENABLE_P2P:
- case WifiP2pManager.DISABLE_P2P:
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P:
deferMessage(message);
break;
default:
@@ -568,8 +501,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
}
- class WaitForWifiDisableState extends State {
- @Override
+ class P2pDisabledState extends State {
+ @Override
public void enter() {
if (DBG) logd(getName());
}
@@ -578,34 +511,19 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
- case WifiStateMachine.P2P_ENABLE_PROCEED:
- try {
- mNwService.wifiFirmwareReload(mInterface, "P2P");
- } catch (Exception e) {
- loge("Failed to reload p2p firmware " + e);
- // continue
- }
-
- //A runtime crash can leave the interface up and
- //this affects p2p when supplicant starts up.
- //Ensure interface is down before a supplicant start.
+ case WifiStateMachine.CMD_ENABLE_P2P:
try {
- mNwService.setInterfaceDown(mInterface);
- } catch (Exception e) {
- if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
- }
-
- if (WifiNative.startP2pSupplicant()) {
- mWifiMonitor.startMonitoring();
- transitionTo(mP2pEnablingState);
- } else {
- notifyP2pEnableFailure();
- transitionTo(mP2pDisabledState);
+ mNwService.setInterfaceUp(mInterface);
+ } catch (RemoteException re) {
+ loge("Unable to change interface settings: " + re);
+ } catch (IllegalStateException ie) {
+ loge("Unable to change interface settings: " + ie);
}
+ mWifiMonitor.startMonitoring();
+ transitionTo(mP2pEnablingState);
break;
- case WifiP2pManager.ENABLE_P2P:
- case WifiP2pManager.DISABLE_P2P:
- deferMessage(message);
+ case WifiStateMachine.CMD_DISABLE_P2P:
+ //Nothing to do
break;
default:
return NOT_HANDLED;
@@ -625,22 +543,15 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
case WifiMonitor.SUP_CONNECTION_EVENT:
- logd("P2p start successful");
+ if (DBG) logd("P2p socket connection successful");
transitionTo(mInactiveState);
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT:
- if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
- loge("Failed to start p2p, retry");
- WifiNative.killSupplicant();
- sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
- } else {
- loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
- mP2pRestartCount = 0;
- }
+ loge("P2p socket connection failed");
transitionTo(mP2pDisabledState);
break;
- case WifiP2pManager.ENABLE_P2P:
- case WifiP2pManager.DISABLE_P2P:
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P:
deferMessage(message);
break;
default:
@@ -656,31 +567,58 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
if (DBG) logd(getName());
sendP2pStateChangedBroadcast(true);
mNetworkInfo.setIsAvailable(true);
+ sendP2pConnectionChangedBroadcast();
initializeP2pSettings();
- showNotification();
}
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
- case WifiP2pManager.ENABLE_P2P:
- replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ //Nothing to do
break;
- case WifiP2pManager.DISABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P:
if (mPeers.clear()) sendP2pPeersChangedBroadcast();
- replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+ mWifiNative.closeSupplicantConnection();
transitionTo(mP2pDisablingState);
break;
case WifiP2pManager.DISCOVER_PEERS:
- int timeout = message.arg1;
- if (WifiNative.p2pFind(timeout)) {
+ // do not send service discovery request while normal find operation.
+ clearSupplicantServiceRequest();
+ if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
+ sendP2pDiscoveryChangedBroadcast(true);
} else {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.ERROR);
}
break;
+ case WifiMonitor.P2P_FIND_STOPPED_EVENT:
+ sendP2pDiscoveryChangedBroadcast(false);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ if (mWifiNative.p2pStopFind()) {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ case WifiP2pManager.DISCOVER_SERVICES:
+ if (DBG) logd(getName() + " discover services");
+ if (!updateSupplicantServiceRequest()) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.NO_SERVICE_REQUESTS);
+ break;
+ }
+ if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
@@ -691,52 +629,53 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
device = (WifiP2pDevice) message.obj;
if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
break;
- case WifiP2pManager.CONNECT:
- if (DBG) logd(getName() + " sending connect");
- mSavedConnectConfig = (WifiP2pConfig) message.obj;
- mPersistGroup = false;
- int netId = configuredNetworkId(mSavedConnectConfig.deviceAddress);
- if (netId >= 0) {
- //TODO: if failure, remove config and do a regular p2pConnect()
- WifiNative.p2pReinvoke(netId, mSavedConnectConfig.deviceAddress);
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ if (DBG) logd(getName() + " add service");
+ WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj;
+ if (addLocalService(message.replyTo, servInfo)) {
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
} else {
- boolean join = false;
- if (isGroupOwner(mSavedConnectConfig.deviceAddress)) join = true;
- String pin = WifiNative.p2pConnect(mSavedConnectConfig, join);
- try {
- Integer.parseInt(pin);
- notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
- } catch (NumberFormatException ignore) {
- // do nothing if p2pConnect did not return a pin
- }
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
}
- updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.INVITED);
- sendP2pPeersChangedBroadcast();
- replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
- transitionTo(mGroupNegotiationState);
break;
- case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant died */
- loge("Connection lost, restart p2p");
- WifiNative.killSupplicant();
- WifiNative.closeSupplicantConnection();
- if (mPeers.clear()) sendP2pPeersChangedBroadcast();
- transitionTo(mP2pDisabledState);
- sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
- break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- mGroup = (WifiP2pGroup) message.obj;
- if (DBG) logd(getName() + " group started");
- if (mGroup.isGroupOwner()) {
- startDhcpServer(mGroup.getInterface());
- } else {
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
- P2pStateMachine.this, mGroup.getInterface());
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
- WifiP2pDevice groupOwner = mGroup.getOwner();
- updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
- sendP2pPeersChangedBroadcast();
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ if (DBG) logd(getName() + " remove service");
+ servInfo = (WifiP2pServiceInfo)message.obj;
+ removeLocalService(message.replyTo, servInfo);
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
+ break;
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ if (DBG) logd(getName() + " clear service");
+ clearLocalServices(message.replyTo);
+ break;
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ if (DBG) logd(getName() + " add service request");
+ if (!addServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj)) {
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
+ break;
+ }
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
+ break;
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ if (DBG) logd(getName() + " remove service request");
+ removeServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj);
+ replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
+ break;
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ if (DBG) logd(getName() + " clear service request");
+ clearServiceRequests(message.replyTo);
+ replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
+ break;
+ case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
+ if (DBG) logd(getName() + " receive service response");
+ List<WifiP2pServiceResponse> sdRespList =
+ (List<WifiP2pServiceResponse>) message.obj;
+ for (WifiP2pServiceResponse resp : sdRespList) {
+ WifiP2pDevice dev =
+ mPeers.get(resp.getSrcDevice().deviceAddress);
+ resp.setSrcDevice(dev);
+ sendServiceResponse(resp);
}
- transitionTo(mGroupCreatedState);
break;
default:
return NOT_HANDLED;
@@ -748,7 +687,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
public void exit() {
sendP2pStateChangedBroadcast(false);
mNetworkInfo.setIsAvailable(false);
- clearNotification();
}
}
@@ -757,21 +695,91 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
public void enter() {
if (DBG) logd(getName());
//Start listening every time we get inactive
- WifiNative.p2pListen();
+ //TODO: Fix listen after driver behavior is fixed
+ //mWifiNative.p2pListen();
}
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
+ case WifiP2pManager.CONNECT:
+ if (DBG) logd(getName() + " sending connect");
+ mSavedPeerConfig = (WifiP2pConfig) message.obj;
+ mPersistGroup = false;
+ int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
+ if (netId >= 0) {
+ //TODO: if failure, remove config and do a regular p2pConnect()
+ mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
+ } else {
+ //Stop discovery before issuing connect
+ mWifiNative.p2pStopFind();
+ //If peer is a GO, we do not need to send provisional discovery,
+ //the supplicant takes care of it.
+ if (mWifiNative.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
+ if (DBG) logd("Sending join to GO");
+ p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ if (DBG) logd("Sending prov disc");
+ transitionTo(mProvisionDiscoveryState);
+ }
+ }
+ updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+ sendP2pPeersChangedBroadcast();
+ replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+ break;
case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
- mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
- notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
- transitionTo(mUserAuthorizingGroupNegotiationState);
+ mSavedPeerConfig = (WifiP2pConfig) message.obj;
+ transitionTo(mUserAuthorizingInvitationState);
+ break;
+ case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
+ WifiP2pGroup group = (WifiP2pGroup) message.obj;
+ WifiP2pDevice owner = group.getOwner();
+
+ if (owner == null) {
+ if (DBG) loge("Ignored invitation from null owner");
+ break;
+ }
+
+ mSavedPeerConfig = new WifiP2pConfig();
+ mSavedPeerConfig.deviceAddress = group.getOwner().deviceAddress;
+
+ //Check if we have the owner in peer list and use appropriate
+ //wps method. Default is to use PBC.
+ if ((owner = getDeviceFromPeerList(owner.deviceAddress)) != null) {
+ if (owner.wpsPbcSupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.PBC;
+ } else if (owner.wpsKeypadSupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
+ } else if (owner.wpsDisplaySupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
+ }
+ }
+ transitionTo(mUserAuthorizingInvitationState);
+ break;
+ case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
+ mSavedPeerConfig = new WifiP2pConfig();
+ mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
+ if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
+ if (DBG) logd("Keypad prov disc request");
+ } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
+ mSavedPeerConfig.wps.pin = provDisc.pin;
+ if (DBG) logd("Display prov disc request");
+ } else {
+ mSavedPeerConfig.wps.setup = WpsInfo.PBC;
+ if (DBG) logd("PBC prov disc request");
+ }
+ transitionTo(mUserAuthorizingInvitationState);
break;
case WifiP2pManager.CREATE_GROUP:
mPersistGroup = true;
- if (WifiNative.p2pGroupAdd()) {
+ if (mWifiNative.p2pGroupAdd()) {
replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
} else {
replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
@@ -779,40 +787,47 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
transitionTo(mGroupNegotiationState);
break;
- case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
- WifiP2pGroup group = (WifiP2pGroup) message.obj;
- notifyP2pInvitationReceived(group);
- transitionTo(mUserAuthorizingGroupInvitationState);
- break;
- default:
+ default:
return NOT_HANDLED;
}
return HANDLED;
}
}
- class UserAuthorizingGroupNegotiationState extends State {
+ class GroupCreatingState extends State {
@Override
public void enter() {
if (DBG) logd(getName());
+ sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
+ ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
}
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
- case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
- case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
- //Ignore additional connection requests
+ case GROUP_CREATING_TIMED_OUT:
+ if (mGroupCreatingTimeoutIndex == message.arg1) {
+ if (DBG) logd("Group negotiation timed out");
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ }
break;
- case GROUP_NEGOTIATION_USER_ACCEPT:
- sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
- mSavedGoNegotiationConfig = null;
+ case WifiP2pManager.DISCOVER_PEERS:
+ /* Discovery will break negotiation */
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ WifiP2pManager.BUSY);
break;
- case GROUP_NEGOTIATION_USER_REJECT:
- if (DBG) logd("User rejected incoming negotiation request");
- mSavedGoNegotiationConfig = null;
+ case WifiP2pManager.CANCEL_CONNECT:
+ //Do a supplicant p2p_cancel which only cancels an ongoing
+ //group negotiation. This will fail for a pending provision
+ //discovery or for a pending user action, but at the framework
+ //level, we always treat cancel as succeded and enter
+ //an inactive state
+ mWifiNative.p2pCancelConnect();
+ handleGroupCreationFailure();
transitionTo(mInactiveState);
+ replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
break;
default:
return NOT_HANDLED;
@@ -821,30 +836,31 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
}
- class UserAuthorizingGroupInvitationState extends State {
+ class UserAuthorizingInvitationState extends State {
@Override
public void enter() {
if (DBG) logd(getName());
+ notifyInvitationReceived();
}
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
- case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
- case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
- //Ignore additional connection requests
- break;
- case GROUP_INVITATION_USER_ACCEPT:
- if (DBG) logd(getName() + " connect to invited group");
- WifiP2pConfig config = new WifiP2pConfig();
- config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
- sendMessage(WifiP2pManager.CONNECT, config);
- mSavedP2pGroup = null;
- break;
- case GROUP_INVITATION_USER_REJECT:
- if (DBG) logd("User rejected incoming invitation request");
- mSavedP2pGroup = null;
+ case PEER_CONNECTION_USER_ACCEPT:
+ //TODO: handle persistence
+ if (mWifiNative.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
+ p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
+ } else {
+ p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
+ }
+ updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+ sendP2pPeersChangedBroadcast();
+ transitionTo(mGroupNegotiationState);
+ break;
+ case PEER_CONNECTION_USER_REJECT:
+ if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
+ mSavedPeerConfig = null;
transitionTo(mInactiveState);
break;
default:
@@ -852,15 +868,77 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
return HANDLED;
}
+
+ @Override
+ public void exit() {
+ //TODO: dismiss dialog if not already done
+ }
}
+ class ProvisionDiscoveryState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ WifiP2pProvDiscEvent provDisc;
+ WifiP2pDevice device;
+ switch (message.what) {
+ case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
+
+ if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ transitionTo(mGroupNegotiationState);
+ }
+ break;
+ case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
+
+ if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ /* we already have the pin */
+ if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
+ mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ transitionTo(mUserAuthorizingInvitationState);
+ }
+ }
+ break;
+ case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
+
+ if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ mSavedPeerConfig.wps.pin = provDisc.pin;
+ mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ notifyInvitationSent(provDisc.pin, device.deviceAddress);
+ transitionTo(mGroupNegotiationState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
class GroupNegotiationState extends State {
@Override
public void enter() {
if (DBG) logd(getName());
- sendMessageDelayed(obtainMessage(GROUP_NEGOTIATION_TIMED_OUT,
- ++mGroupNegotiationTimeoutIndex, 0), GROUP_NEGOTIATION_WAIT_TIME_MS);
}
@Override
@@ -873,36 +951,28 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
if (DBG) logd(getName() + " go success");
break;
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) logd(getName() + " group started");
+ if (mGroup.isGroupOwner()) {
+ startDhcpServer(mGroup.getInterface());
+ } else {
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
+ P2pStateMachine.this, mGroup.getInterface());
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ WifiP2pDevice groupOwner = mGroup.getOwner();
+ updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
+ sendP2pPeersChangedBroadcast();
+ }
+ mSavedPeerConfig = null;
+ transitionTo(mGroupCreatedState);
+ break;
case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
if (DBG) logd(getName() + " go failure");
- updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
- mSavedConnectConfig = null;
- sendP2pPeersChangedBroadcast();
+ handleGroupCreationFailure();
transitionTo(mInactiveState);
break;
- case GROUP_NEGOTIATION_TIMED_OUT:
- if (mGroupNegotiationTimeoutIndex == message.arg1) {
- if (DBG) logd("Group negotiation timed out");
- updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
- mSavedConnectConfig = null;
- sendP2pPeersChangedBroadcast();
- transitionTo(mInactiveState);
- }
- break;
- case WifiP2pManager.DISCOVER_PEERS:
- /* Discovery will break negotiation */
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CANCEL_CONNECT:
- if (WifiNative.p2pCancelConnect()) {
- replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
default:
return NOT_HANDLED;
}
@@ -910,6 +980,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
}
+
+
class GroupCreatedState extends State {
@Override
public void enter() {
@@ -923,6 +995,10 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS);
sendP2pConnectionChangedBroadcast();
}
+
+ if (!mPersistGroup) {
+ mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
+ }
}
@Override
@@ -930,28 +1006,27 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
case WifiMonitor.AP_STA_CONNECTED_EVENT:
- //After a GO setup, STA connected event comes with interface address
- String interfaceAddress = (String) message.obj;
- String deviceAddress = getDeviceAddress(interfaceAddress);
+ WifiP2pDevice device = (WifiP2pDevice) message.obj;
+ String deviceAddress = device.deviceAddress;
if (deviceAddress != null) {
mGroup.addClient(deviceAddress);
updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED);
if (DBG) logd(getName() + " ap sta connected");
sendP2pPeersChangedBroadcast();
} else {
- loge("Connect on unknown device address : " + interfaceAddress);
+ loge("Connect on null device address, ignore");
}
break;
case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
- interfaceAddress = (String) message.obj;
- deviceAddress = getDeviceAddress(interfaceAddress);
+ device = (WifiP2pDevice) message.obj;
+ deviceAddress = device.deviceAddress;
if (deviceAddress != null) {
updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
if (mGroup.removeClient(deviceAddress)) {
if (DBG) logd("Removed client " + deviceAddress);
if (!mPersistGroup && mGroup.isClientListEmpty()) {
Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
- WifiNative.p2pGroupRemove(mGroup.getInterface());
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
}
} else {
if (DBG) logd("Failed to remove client " + deviceAddress);
@@ -962,7 +1037,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
sendP2pPeersChangedBroadcast();
if (DBG) loge(getName() + " ap sta disconnected");
} else {
- loge("Disconnect on unknown device address : " + interfaceAddress);
+ loge("Disconnect on unknown device: " + device);
}
break;
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
@@ -972,13 +1047,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
if (DBG) logd("DhcpInfo: " + dhcpInfo);
setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
sendP2pConnectionChangedBroadcast();
+ //Turn on power save on client
+ mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
} else {
- WifiNative.p2pGroupRemove(mGroup.getInterface());
+ loge("DHCP failed");
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
}
break;
case WifiP2pManager.REMOVE_GROUP:
if (DBG) loge(getName() + " remove group");
- if (WifiNative.p2pGroupRemove(mGroup.getInterface())) {
+ if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
} else {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
@@ -1006,29 +1084,27 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
mGroup = null;
+ mWifiNative.p2pFlush();
if (changed) sendP2pPeersChangedBroadcast();
transitionTo(mInactiveState);
break;
case WifiMonitor.P2P_DEVICE_LOST_EVENT:
- WifiP2pDevice device = (WifiP2pDevice) message.obj;
- if (device.equals(mGroup.getOwner())) {
- logd("Lost the group owner, killing p2p connection");
- WifiNative.p2pGroupRemove(mGroup.getInterface());
- } else if (mGroup.removeClient(device)) {
- if (!mPersistGroup && mGroup.isClientListEmpty()) {
- Slog.d(TAG, "Client list empty, removing a non-persistent p2p group");
- WifiNative.p2pGroupRemove(mGroup.getInterface());
- }
+ device = (WifiP2pDevice) message.obj;
+ //Device loss for a connected device indicates it is not in discovery any more
+ if (mGroup.contains(device)) {
+ if (DBG) logd("Lost " + device +" , do nothing");
+ return HANDLED;
}
- return NOT_HANDLED; // Do the regular device lost handling
- case WifiP2pManager.DISABLE_P2P:
+ // Do the regular device lost handling
+ return NOT_HANDLED;
+ case WifiStateMachine.CMD_DISABLE_P2P:
sendMessage(WifiP2pManager.REMOVE_GROUP);
deferMessage(message);
break;
case WifiP2pManager.CONNECT:
WifiP2pConfig config = (WifiP2pConfig) message.obj;
logd("Inviting device : " + config.deviceAddress);
- if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) {
+ if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
@@ -1038,24 +1114,25 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
// TODO: figure out updating the status to declined when invitation is rejected
break;
- case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
- logd("===> INVITATION RESULT EVENT : " + message.obj);
- break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
- notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj);
- break;
case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
- notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj);
+ case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
+ mSavedPeerConfig = new WifiP2pConfig();
+ mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
+ if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
+ } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
+ mSavedPeerConfig.wps.pin = provDisc.pin;
+ } else {
+ mSavedPeerConfig.wps.setup = WpsInfo.PBC;
+ }
+ transitionTo(mUserAuthorizingJoinState);
break;
case WifiMonitor.P2P_GROUP_STARTED_EVENT:
Slog.e(TAG, "Duplicate group creation event notice, ignore");
break;
- case WPS_PBC:
- WifiNative.wpsPbc();
- break;
- case WPS_PIN:
- WifiNative.wpsPin((String) message.obj);
- break;
default:
return NOT_HANDLED;
}
@@ -1070,6 +1147,48 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
}
+ class UserAuthorizingJoinState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyInvitationReceived();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ //Ignore more client requests
+ break;
+ case PEER_CONNECTION_USER_ACCEPT:
+ if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
+ mWifiNative.startWpsPbc(null);
+ } else {
+ mWifiNative.startWpsPinKeypad(mSavedPeerConfig.wps.pin);
+ }
+ mSavedPeerConfig = null;
+ transitionTo(mGroupCreatedState);
+ break;
+ case PEER_CONNECTION_USER_REJECT:
+ if (DBG) logd("User rejected incoming request");
+ mSavedPeerConfig = null;
+ transitionTo(mGroupCreatedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ //TODO: dismiss dialog if not already done
+ }
+ }
+
private void sendP2pStateChangedBroadcast(boolean enabled) {
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1083,6 +1202,20 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
mContext.sendStickyBroadcast(intent);
}
+ private void sendP2pDiscoveryChangedBroadcast(boolean started) {
+ if (mDiscoveryStarted == started) return;
+ mDiscoveryStarted = started;
+
+ if (DBG) logd("discovery change broadcast " + started);
+
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
+ WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
+ WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
+ mContext.sendStickyBroadcast(intent);
+ }
+
private void sendThisDeviceChangedBroadcast() {
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1110,9 +1243,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
InterfaceConfiguration ifcg = null;
try {
ifcg = mNwService.getInterfaceConfig(intf);
- ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
- SERVER_ADDRESS), 24);
- ifcg.interfaceFlags = "[up]";
+ ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
+ SERVER_ADDRESS), 24));
+ ifcg.setInterfaceUp();
mNwService.setInterfaceConfig(intf, ifcg);
/* This starts the dnsmasq server */
mNwService.startTethering(DHCP_RANGE);
@@ -1146,140 +1279,80 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
dialog.show();
}
- private void notifyWpsPin(String pin, String peerAddress) {
+ private void addRowToDialog(ViewGroup group, int stringId, String value) {
Resources r = Resources.getSystem();
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
- .setMessage(r.getString(R.string.wifi_p2p_pin_display_message, pin, peerAddress))
- .setPositiveButton(r.getString(R.string.ok), null)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
+ View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
+ group, false);
+ ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
+ ((TextView) row.findViewById(R.id.value)).setText(value);
+ group.addView(row);
}
- private void notifyP2pGoNegotationRequest(WifiP2pConfig config) {
+ private void notifyInvitationSent(String pin, String peerAddress) {
Resources r = Resources.getSystem();
- WpsInfo wps = config.wps;
- final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
- final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
- .setView(textEntryView)
- .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " connect " + pin.getText());
-
- if (pin.getVisibility() == View.GONE) {
- mSavedGoNegotiationConfig.wps.setup = WpsInfo.PBC;
- } else {
- mSavedGoNegotiationConfig.wps.setup = WpsInfo.KEYPAD;
- mSavedGoNegotiationConfig.wps.pin = pin.getText().toString();
- }
- sendMessage(GROUP_NEGOTIATION_USER_ACCEPT);
- }
- })
- .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " ignore connect");
- sendMessage(GROUP_NEGOTIATION_USER_REJECT);
- }
- })
- .create();
-
- if (wps.setup == WpsInfo.PBC) {
- pin.setVisibility(View.GONE);
- dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
- config.deviceAddress));
- } else {
- dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
- config.deviceAddress));
- }
-
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- }
-
- private void notifyP2pProvDiscPbcRequest(WifiP2pDevice peer) {
- Resources r = Resources.getSystem();
final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
- final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+ .inflate(R.layout.wifi_p2p_dialog, null);
+
+ ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
+ addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
+ addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
.setView(textEntryView)
- .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " wps_pbc");
- sendMessage(WPS_PBC);
- }
- })
- .setNegativeButton(r.getString(R.string.cancel), null)
+ .setPositiveButton(r.getString(R.string.ok), null)
.create();
-
- pin.setVisibility(View.GONE);
- dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
- peer.deviceAddress));
-
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
- private void notifyP2pProvDiscPinRequest(WifiP2pDevice peer) {
+ private void notifyInvitationReceived() {
Resources r = Resources.getSystem();
+ final WpsInfo wps = mSavedPeerConfig.wps;
final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
- final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
-
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
- .setView(textEntryView)
- .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " wps_pin");
- sendMessage(WPS_PIN, pin.getText().toString());
- }
- })
- .setNegativeButton(r.getString(R.string.cancel), null)
- .create();
+ .inflate(R.layout.wifi_p2p_dialog, null);
- dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
- peer.deviceAddress));
+ ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
+ addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
+ mSavedPeerConfig.deviceAddress));
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- }
-
- private void notifyP2pInvitationReceived(WifiP2pGroup group) {
- mSavedP2pGroup = group;
- Resources r = Resources.getSystem();
- final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
- final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+ final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
.setView(textEntryView)
- .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
+ .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- sendMessage(GROUP_INVITATION_USER_ACCEPT);
+ if (wps.setup == WpsInfo.KEYPAD) {
+ mSavedPeerConfig.wps.pin = pin.getText().toString();
+ }
+ if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
+ sendMessage(PEER_CONNECTION_USER_ACCEPT);
}
})
- .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
+ .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " ignore invite");
- sendMessage(GROUP_INVITATION_USER_REJECT);
+ if (DBG) logd(getName() + " ignore connect");
+ sendMessage(PEER_CONNECTION_USER_REJECT);
}
})
.create();
- pin.setVisibility(View.GONE);
- dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
- group.getOwner().deviceAddress));
+ //make the enter pin area or the display pin area visible
+ switch (wps.setup) {
+ case WpsInfo.KEYPAD:
+ if (DBG) logd("Enter pin section visible");
+ textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
+ break;
+ case WpsInfo.DISPLAY:
+ if (DBG) logd("Shown pin section visible");
+ addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
+ break;
+ default:
+ break;
+ }
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
@@ -1293,15 +1366,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
}
}
- private boolean isGroupOwner(String deviceAddress) {
- for (WifiP2pDevice d : mPeers.getDeviceList()) {
- if (d.deviceAddress.equals(deviceAddress)) {
- return d.isGroupOwner();
- }
- }
- return false;
- }
-
//TODO: implement when wpa_supplicant is fixed
private int configuredNetworkId(String deviceAddress) {
return -1;
@@ -1319,23 +1383,56 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
mWifiP2pInfo.groupOwnerAddress = null;
}
- private String getDeviceAddress(String interfaceAddress) {
+ private String getDeviceName(String deviceAddress) {
for (WifiP2pDevice d : mPeers.getDeviceList()) {
- if (interfaceAddress.equals(WifiNative.p2pGetInterfaceAddress(d.deviceAddress))) {
- return d.deviceAddress;
+ if (d.deviceAddress.equals(deviceAddress)) {
+ return d.deviceName;
+ }
+ }
+ //Treat the address as name if there is no match
+ return deviceAddress;
+ }
+
+ private WifiP2pDevice getDeviceFromPeerList(String deviceAddress) {
+ for (WifiP2pDevice d : mPeers.getDeviceList()) {
+ if (d.deviceAddress.equals(deviceAddress)) {
+ return d;
}
}
return null;
}
- private void initializeP2pSettings() {
- WifiNative.setPersistentReconnect(true);
- WifiNative.setDeviceName(mThisDevice.deviceName);
- WifiNative.setDeviceType(mThisDevice.primaryDeviceType);
+ private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
+ String pin = mWifiNative.p2pConnect(config, join);
+ try {
+ Integer.parseInt(pin);
+ notifyInvitationSent(pin, config.deviceAddress);
+ } catch (NumberFormatException ignore) {
+ // do nothing if p2pConnect did not return a pin
+ }
+ }
- mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress();
+ private void initializeP2pSettings() {
+ mWifiNative.setPersistentReconnect(true);
+ mWifiNative.setDeviceName(mThisDevice.deviceName);
+ //DIRECT-XY-DEVICENAME (XY is randomly generated)
+ mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
+ mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
+ //The supplicant default is to support everything, but a bug necessitates
+ //the framework to specify this explicitly
+ mWifiNative.setConfigMethods("keypad display push_button");
+ //STA has higher priority over P2P
+ mWifiNative.setConcurrencyPriority("sta");
+
+ mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
updateThisDevice(WifiP2pDevice.AVAILABLE);
if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
+
+ mClientInfoList.clear();
+ mWifiNative.p2pFlush();
+ mWifiNative.p2pServiceFlush();
+ mServiceTransactionId = 0;
+ mServiceDiscReqId = null;
}
private void updateThisDevice(int status) {
@@ -1343,6 +1440,14 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
sendThisDeviceChangedBroadcast();
}
+ private void handleGroupCreationFailure() {
+ mSavedPeerConfig = null;
+ /* After cancelling group formation, new connections on existing peers can fail
+ * at supplicant. Flush and restart discovery */
+ mWifiNative.p2pFlush();
+ sendMessage(WifiP2pManager.DISCOVER_PEERS);
+ }
+
//State machine initiated requests can have replyTo set to null indicating
//there are no recepients, we ignore those reply actions
private void replyToMessage(Message msg, int what) {
@@ -1384,54 +1489,281 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
Slog.e(TAG, s);
}
- private void showNotification() {
- NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null || mNotification != null) {
+ /**
+ * Update service discovery request to wpa_supplicant.
+ */
+ private boolean updateSupplicantServiceRequest() {
+ clearSupplicantServiceRequest();
+
+ StringBuffer sb = new StringBuffer();
+ for (ClientInfo c: mClientInfoList.values()) {
+ int key;
+ WifiP2pServiceRequest req;
+ for (int i=0; i < c.mReqList.size(); i++) {
+ req = c.mReqList.valueAt(i);
+ if (req != null) {
+ sb.append(req.getSupplicantQuery());
+ }
+ }
+ }
+
+ if (sb.length() == 0) {
+ return false;
+ }
+
+ mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
+ if (mServiceDiscReqId == null) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Clear service discovery request in wpa_supplicant
+ */
+ private void clearSupplicantServiceRequest() {
+ if (mServiceDiscReqId == null) return;
+
+ mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
+ mServiceDiscReqId = null;
+ }
+
+ /* TODO: We could track individual service adds seperately and avoid
+ * having to do update all service requests on every new request
+ */
+ private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
+ clearClientDeadChannels();
+ ClientInfo clientInfo = getClientInfo(m, true);
+ if (clientInfo == null) {
+ return false;
+ }
+
+ ++mServiceTransactionId;
+ //The Wi-Fi p2p spec says transaction id should be non-zero
+ if (mServiceTransactionId == 0) ++mServiceTransactionId;
+ req.setTransactionId(mServiceTransactionId);
+ clientInfo.mReqList.put(mServiceTransactionId, req);
+
+ if (mServiceDiscReqId == null) {
+ return true;
+ }
+
+ return updateSupplicantServiceRequest();
+ }
+
+ private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
return;
}
- Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+ //Application does not have transaction id information
+ //go through stored requests to remove
+ boolean removed = false;
+ for (int i=0; i < clientInfo.mReqList.size(); i++) {
+ if (req.equals(clientInfo.mReqList.valueAt(i))) {
+ removed = true;
+ clientInfo.mReqList.removeAt(i);
+ break;
+ }
+ }
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+ if (!removed) return;
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
- CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
-
- mNotification = new Notification();
- mNotification.when = 0;
- //TODO: might change to be a seperate icon
- mNotification.icon = R.drawable.stat_sys_tether_wifi;
- mNotification.defaults &= ~Notification.DEFAULT_SOUND;
- mNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, message, pi);
-
- notificationManager.notify(mNotification.icon, mNotification);
+ if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+
+ if (mServiceDiscReqId == null) {
+ return;
+ }
+
+ updateSupplicantServiceRequest();
}
- private void clearNotification() {
- NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager != null && mNotification != null) {
- notificationManager.cancel(mNotification.icon);
- mNotification = null;
+ private void clearServiceRequests(Messenger m) {
+
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ if (clientInfo.mReqList.size() == 0) {
+ return;
+ }
+
+ clientInfo.mReqList.clear();
+
+ if (clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove channel information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+
+ if (mServiceDiscReqId == null) {
+ return;
}
+
+ updateSupplicantServiceRequest();
}
- private boolean isAirplaneSensitive() {
- String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_RADIOS);
- return airplaneModeRadios == null
- || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
+ private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
+ clearClientDeadChannels();
+ ClientInfo clientInfo = getClientInfo(m, true);
+ if (clientInfo == null) {
+ return false;
+ }
+
+ if (!clientInfo.mServList.add(servInfo)) {
+ return false;
+ }
+
+ if (!mWifiNative.p2pServiceAdd(servInfo)) {
+ clientInfo.mServList.remove(servInfo);
+ return false;
+ }
+
+ return true;
}
- private boolean isAirplaneModeOn() {
- return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1;
+ private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ mWifiNative.p2pServiceDel(servInfo);
+
+ clientInfo.mServList.remove(servInfo);
+ if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
}
+ private void clearLocalServices(Messenger m) {
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
+ mWifiNative.p2pServiceDel(servInfo);
+ }
+
+ clientInfo.mServList.clear();
+ if (clientInfo.mReqList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+ }
+
+ private void clearClientInfo(Messenger m) {
+ clearLocalServices(m);
+ clearServiceRequests(m);
+ }
+
+ /**
+ * Send the service response to the WifiP2pManager.Channel.
+ *
+ * @param resp
+ */
+ private void sendServiceResponse(WifiP2pServiceResponse resp) {
+ for (ClientInfo c : mClientInfoList.values()) {
+ WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
+ if (req != null) {
+ Message msg = Message.obtain();
+ msg.what = WifiP2pManager.RESPONSE_SERVICE;
+ msg.arg1 = 0;
+ msg.arg2 = 0;
+ msg.obj = resp;
+ try {
+ c.mMessenger.send(msg);
+ } catch (RemoteException e) {
+ if (DBG) logd("detect dead channel");
+ clearClientInfo(c.mMessenger);
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * We dont get notifications of clients that have gone away.
+ * We detect this actively when services are added and throw
+ * them away.
+ *
+ * TODO: This can be done better with full async channels.
+ */
+ private void clearClientDeadChannels() {
+ ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
+
+ for (ClientInfo c : mClientInfoList.values()) {
+ Message msg = Message.obtain();
+ msg.what = WifiP2pManager.PING;
+ msg.arg1 = 0;
+ msg.arg2 = 0;
+ msg.obj = null;
+ try {
+ c.mMessenger.send(msg);
+ } catch (RemoteException e) {
+ if (DBG) logd("detect dead channel");
+ deadClients.add(c.mMessenger);
+ }
+ }
+
+ for (Messenger m : deadClients) {
+ clearClientInfo(m);
+ }
+ }
+
+ /**
+ * Return the specified ClientInfo.
+ * @param m Messenger
+ * @param createIfNotExist if true and the specified channel info does not exist,
+ * create new client info.
+ * @return the specified ClientInfo.
+ */
+ private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
+ ClientInfo clientInfo = mClientInfoList.get(m);
+
+ if (clientInfo == null && createIfNotExist) {
+ if (DBG) logd("add a new client");
+ clientInfo = new ClientInfo(m);
+ mClientInfoList.put(m, clientInfo);
+ }
+
+ return clientInfo;
+ }
+
+ }
+
+ /**
+ * Information about a particular client and we track the service discovery requests
+ * and the local services registered by the client.
+ */
+ private class ClientInfo {
+
+ /*
+ * A reference to WifiP2pManager.Channel handler.
+ * The response of this request is notified to WifiP2pManager.Channel handler
+ */
+ private Messenger mMessenger;
+
+ /*
+ * A service discovery request list.
+ */
+ private SparseArray<WifiP2pServiceRequest> mReqList;
+
+ /*
+ * A local service information list.
+ */
+ private List<WifiP2pServiceInfo> mServList;
+
+ private ClientInfo(Messenger m) {
+ mMessenger = m;
+ mReqList = new SparseArray();
+ mServList = new ArrayList<WifiP2pServiceInfo>();
+ }
}
}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
new file mode 100644
index 0000000..54b7ac4
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.nsd.DnsSdTxtRecord;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class for storing Bonjour service information that is advertised
+ * over a Wi-Fi peer-to-peer setup.
+ *
+ * {@see android.net.wifi.p2p.WifiP2pManager#addLocalService}
+ * {@see android.net.wifi.p2p.WifiP2pManager#removeLocalService}
+ * {@see WifiP2pServiceInfo}
+ * {@see WifiP2pUpnpServiceInfo}
+ */
+public class WifiP2pDnsSdServiceInfo extends WifiP2pServiceInfo {
+
+ /**
+ * Bonjour version 1.
+ * @hide
+ */
+ public static final int VERSION_1 = 0x01;
+
+ /**
+ * Pointer record.
+ * @hide
+ */
+ public static final int DNS_TYPE_PTR = 12;
+
+ /**
+ * Text record.
+ * @hide
+ */
+ public static final int DNS_TYPE_TXT = 16;
+
+ /**
+ * virtual memory packet.
+ * see E.3 of the Wi-Fi Direct technical specification for the detail.<br>
+ * Key: domain name Value: pointer address.<br>
+ */
+ private final static Map<String, String> sVmPacket;
+
+ static {
+ sVmPacket = new HashMap<String, String>();
+ sVmPacket.put("_tcp.local.", "c00c");
+ sVmPacket.put("local.", "c011");
+ sVmPacket.put("_udp.local.", "c01c");
+ }
+
+ /**
+ * This constructor is only used in newInstance().
+ *
+ * @param queryList
+ */
+ private WifiP2pDnsSdServiceInfo(List<String> queryList) {
+ super(queryList);
+ }
+
+ /**
+ * Create a Bonjour service information object.
+ *
+ * @param instanceName instance name.<br>
+ * e.g) "MyPrinter"
+ * @param serviceType service type.<br>
+ * e.g) "_ipp._tcp"
+ * @param txtRecord TXT record as defined at
+ * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
+ * @return Bonjour service information object
+ */
+ public static WifiP2pDnsSdServiceInfo newInstance(String instanceName,
+ String serviceType, DnsSdTxtRecord txtRecord) {
+ if (TextUtils.isEmpty(instanceName) || TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException(
+ "instance name or service type cannot be empty");
+ }
+
+ if (txtRecord == null) {
+ txtRecord = new DnsSdTxtRecord();
+ }
+
+ ArrayList<String> queries = new ArrayList<String>();
+ queries.add(createPtrServiceQuery(instanceName, serviceType));
+ queries.add(createTxtServiceQuery(instanceName, serviceType, txtRecord));
+
+ return new WifiP2pDnsSdServiceInfo(queries);
+ }
+
+ /**
+ * Create wpa_supplicant service query for PTR record.
+ *
+ * @param instanceName instance name.<br>
+ * e.g) "MyPrinter"
+ * @param serviceType service type.<br>
+ * e.g) "_ipp._tcp"
+ * @return wpa_supplicant service query.
+ */
+ private static String createPtrServiceQuery(String instanceName,
+ String serviceType) {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("bonjour ");
+ sb.append(createRequest(serviceType + ".local.", DNS_TYPE_PTR, VERSION_1));
+ sb.append(" ");
+
+ byte[] data = instanceName.getBytes();
+ sb.append(String.format("%02x", data.length));
+ sb.append(WifiP2pServiceInfo.bin2HexStr(data));
+ // This is the start point of this response.
+ // Therefore, it indicates the request domain name.
+ sb.append("c027");
+ return sb.toString();
+ }
+
+ /**
+ * Create wpa_supplicant service query for TXT record.
+ *
+ * @param instanceName instance name.<br>
+ * e.g) "MyPrinter"
+ * @param serviceType service type.<br>
+ * e.g) "_ipp._tcp"
+ * @param txtRecord TXT record.<br>
+ * @return wpa_supplicant service query.
+ */
+ private static String createTxtServiceQuery(String instanceName,
+ String serviceType,
+ DnsSdTxtRecord txtRecord) {
+
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("bonjour ");
+
+ sb.append(createRequest((instanceName + "." + serviceType + ".local."),
+ DNS_TYPE_TXT, VERSION_1));
+ sb.append(" ");
+ byte[] rawData = txtRecord.getRawData();
+ if (rawData.length == 0) {
+ sb.append("00");
+ } else {
+ sb.append(bin2HexStr(rawData));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Create bonjour service discovery request.
+ *
+ * @param dnsName dns name
+ * @param dnsType dns type
+ * @param version version number
+ * @hide
+ */
+ static String createRequest(String dnsName, int dnsType, int version) {
+ StringBuffer sb = new StringBuffer();
+
+ /*
+ * The request format is as follows.
+ * ________________________________________________
+ * | Encoded and Compressed dns name (variable) |
+ * ________________________________________________
+ * | Type (2) | Version (1) |
+ */
+ if (dnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
+ dnsName = dnsName.toLowerCase();
+ }
+ sb.append(compressDnsName(dnsName));
+ sb.append(String.format("%04x", dnsType));
+ sb.append(String.format("%02x", version));
+
+ return sb.toString();
+ }
+
+ /**
+ * Compress DNS data.
+ *
+ * see E.3 of the Wi-Fi Direct technical specification for the detail.
+ *
+ * @param dnsName dns name
+ * @return compressed dns name
+ */
+ private static String compressDnsName(String dnsName) {
+ StringBuffer sb = new StringBuffer();
+
+ // The domain name is replaced with a pointer to a prior
+ // occurrence of the same name in virtual memory packet.
+ while (true) {
+ String data = sVmPacket.get(dnsName);
+ if (data != null) {
+ sb.append(data);
+ break;
+ }
+ int i = dnsName.indexOf('.');
+ if (i == -1) {
+ if (dnsName.length() > 0) {
+ sb.append(String.format("%02x", dnsName.length()));
+ sb.append(WifiP2pServiceInfo.bin2HexStr(dnsName.getBytes()));
+ }
+ // for a sequence of labels ending in a zero octet
+ sb.append("00");
+ break;
+ }
+
+ String name = dnsName.substring(0, i);
+ dnsName = dnsName.substring(i + 1);
+ sb.append(String.format("%02x", name.length()));
+ sb.append(WifiP2pServiceInfo.bin2HexStr(name.getBytes()));
+ }
+ return sb.toString();
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequest.java
new file mode 100644
index 0000000..d5415e0
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.wifi.p2p.WifiP2pManager;
+
+/**
+ * A class for creating a Bonjour service discovery request for use with
+ * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
+ *
+ * {@see WifiP2pManager}
+ * {@see WifiP2pServiceRequest}
+ * {@see WifiP2pUpnpServiceRequest}
+ */
+public class WifiP2pDnsSdServiceRequest extends WifiP2pServiceRequest {
+
+ /**
+ * This constructor is only used in newInstance().
+ *
+ * @param query The part of service specific query.
+ * @hide
+ */
+ private WifiP2pDnsSdServiceRequest(String query) {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, query);
+ }
+
+ /**
+ * This constructor is only used in newInstance().
+ * @hide
+ */
+ private WifiP2pDnsSdServiceRequest() {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, null);
+ }
+
+ private WifiP2pDnsSdServiceRequest(String dnsQuery, int dnsType, int version) {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, WifiP2pDnsSdServiceInfo.createRequest(
+ dnsQuery,
+ dnsType,
+ version));
+ }
+
+ /**
+ * Create a service discovery request to search all Bonjour services.
+ *
+ * @return service request for Bonjour.
+ */
+ public static WifiP2pDnsSdServiceRequest newInstance() {
+ return new WifiP2pDnsSdServiceRequest();
+ }
+
+ /**
+ * Create a service discovery to search for Bonjour services with the specified
+ * service type.
+ *
+ * @param serviceType service type. Cannot be null <br>
+ * "_afpovertcp._tcp."(Apple File Sharing over TCP)<br>
+ * "_ipp._tcp" (IP Printing over TCP)<br>
+ * "_http._tcp" (http service)
+ * @return service request for DnsSd.
+ */
+ public static WifiP2pDnsSdServiceRequest newInstance(String serviceType) {
+ if (serviceType == null) {
+ throw new IllegalArgumentException("service type cannot be null");
+ }
+ return new WifiP2pDnsSdServiceRequest(serviceType + ".local.",
+ WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR,
+ WifiP2pDnsSdServiceInfo.VERSION_1);
+ }
+
+ /**
+ * Create a service discovery request to get the TXT data from the specified
+ * Bonjour service.
+ *
+ * @param instanceName instance name. Cannot be null. <br>
+ * "MyPrinter"
+ * @param serviceType service type. Cannot be null. <br>
+ * e.g) <br>
+ * "_afpovertcp._tcp"(Apple File Sharing over TCP)<br>
+ * "_ipp._tcp" (IP Printing over TCP)<br>
+ * @return service request for Bonjour.
+ */
+ public static WifiP2pDnsSdServiceRequest newInstance(String instanceName,
+ String serviceType) {
+ if (instanceName == null || serviceType == null) {
+ throw new IllegalArgumentException(
+ "instance name or service type cannot be null");
+ }
+ String fullDomainName = instanceName + "." + serviceType + ".local.";
+ return new WifiP2pDnsSdServiceRequest(fullDomainName,
+ WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT,
+ WifiP2pDnsSdServiceInfo.VERSION_1);
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceResponse.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceResponse.java
new file mode 100644
index 0000000..c053c8a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceResponse.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.nsd.DnsSdTxtRecord;
+import android.net.wifi.p2p.WifiP2pDevice;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class for a response of bonjour service discovery.
+ *
+ * @hide
+ */
+public class WifiP2pDnsSdServiceResponse extends WifiP2pServiceResponse {
+
+ /**
+ * DNS query name.
+ * e.g)
+ * for PTR
+ * "_ipp._tcp.local."
+ * for TXT
+ * "MyPrinter._ipp._tcp.local."
+ */
+ private String mDnsQueryName;
+
+ /**
+ * Service instance name.
+ * e.g) "MyPrinter"
+ * This field is only used when the dns type equals to
+ * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR}.
+ */
+ private String mInstanceName;
+
+ /**
+ * DNS Type.
+ * Should be {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR} or
+ * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
+ */
+ private int mDnsType;
+
+ /**
+ * DnsSd version number.
+ * Should be {@link WifiP2pDnsSdServiceInfo#VERSION_1}.
+ */
+ private int mVersion;
+
+ /**
+ * Txt record.
+ * This field is only used when the dns type equals to
+ * {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
+ */
+ private DnsSdTxtRecord mTxtRecord;
+
+ /**
+ * Virtual memory packet.
+ * see E.3 of the Wi-Fi Direct technical specification for the detail.<br>
+ * The spec can be obtained from wi-fi.org
+ * Key: pointer Value: domain name.<br>
+ */
+ private final static Map<Integer, String> sVmpack;
+
+ static {
+ sVmpack = new HashMap<Integer, String>();
+ sVmpack.put(0x0c, "_tcp.local.");
+ sVmpack.put(0x11, "local.");
+ sVmpack.put(0x1c, "_udp.local.");
+ }
+
+ /**
+ * Returns query DNS name.
+ * @return DNS name.
+ */
+ public String getDnsQueryName() {
+ return mDnsQueryName;
+ }
+
+ /**
+ * Return query DNS type.
+ * @return DNS type.
+ */
+ public int getDnsType() {
+ return mDnsType;
+ }
+
+ /**
+ * Return bonjour version number.
+ * @return version number.
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Return instance name.
+ * @return
+ */
+ public String getInstanceName() {
+ return mInstanceName;
+ }
+
+ /**
+ * Return TXT record data.
+ * @return TXT record data.
+ */
+ public DnsSdTxtRecord getTxtRecord() {
+ return mTxtRecord;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("serviceType:DnsSd(").append(mServiceType).append(")");
+ sbuf.append(" status:").append(Status.toString(mStatus));
+ sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
+ sbuf.append(" version:").append(String.format("%02x", mVersion));
+ sbuf.append(" dnsName:").append(mDnsQueryName);
+ if (mTxtRecord != null) {
+ sbuf.append(" TxtRecord:").append(mTxtRecord);
+ }
+ if (mInstanceName != null) {
+ sbuf.append(" InsName:").append(mInstanceName);
+ }
+ return sbuf.toString();
+ }
+
+ /**
+ * This is only used in framework.
+ * @param status status code.
+ * @param dev source device.
+ * @param data RDATA.
+ * @hide
+ */
+ protected WifiP2pDnsSdServiceResponse(int status,
+ int tranId, WifiP2pDevice dev, byte[] data) {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR,
+ status, tranId, dev, data);
+ if (!parse()) {
+ throw new IllegalArgumentException("Malformed bonjour service response");
+ }
+ }
+
+ /**
+ * Parse DnsSd service discovery response.
+ *
+ * @return {@code true} if the operation succeeded
+ */
+ private boolean parse() {
+ /*
+ * The data format from Wi-Fi Direct spec is as follows.
+ * ________________________________________________
+ * | encoded and compressed dns name (variable) |
+ * ________________________________________________
+ * | dnstype(2byte) | version(1byte) |
+ * ________________________________________________
+ * | RDATA (variable) |
+ */
+ if (mData == null) {
+ // the empty is OK.
+ return true;
+ }
+
+ DataInputStream dis = new DataInputStream(new ByteArrayInputStream(mData));
+
+ mDnsQueryName = readDnsName(dis);
+ if (mDnsQueryName == null) {
+ return false;
+ }
+
+ try {
+ mDnsType = dis.readUnsignedShort();
+ mVersion = dis.readUnsignedByte();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR) {
+ String rData = readDnsName(dis);
+ if (rData == null) {
+ return false;
+ }
+ if (rData.length() <= mDnsQueryName.length()) {
+ return false;
+ }
+
+ mInstanceName = rData.substring(0,
+ rData.length() - mDnsQueryName.length() -1);
+ } else if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
+ mTxtRecord = readTxtData(dis);
+ if (mTxtRecord == null) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Read dns name.
+ *
+ * @param dis data input stream.
+ * @return dns name
+ */
+ private String readDnsName(DataInputStream dis) {
+ StringBuffer sb = new StringBuffer();
+
+ // copy virtual memory packet.
+ HashMap<Integer, String> vmpack = new HashMap<Integer, String>(sVmpack);
+ if (mDnsQueryName != null) {
+ vmpack.put(0x27, mDnsQueryName);
+ }
+ try {
+ while (true) {
+ int i = dis.readUnsignedByte();
+ if (i == 0x00) {
+ return sb.toString();
+ } else if (i == 0xc0) {
+ // refer to pointer.
+ String ref = vmpack.get(dis.readUnsignedByte());
+ if (ref == null) {
+ //invalid.
+ return null;
+ }
+ sb.append(ref);
+ return sb.toString();
+ } else {
+ byte[] data = new byte[i];
+ dis.readFully(data);
+ sb.append(new String(data));
+ sb.append(".");
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Read TXT record data.
+ *
+ * @param dis
+ * @return TXT record data
+ */
+ private DnsSdTxtRecord readTxtData(DataInputStream dis) {
+ DnsSdTxtRecord txtRecord = new DnsSdTxtRecord();
+ try {
+ while (dis.available() > 0) {
+ int len = dis.readUnsignedByte();
+ if (len == 0) {
+ break;
+ }
+ byte[] data = new byte[len];
+ dis.readFully(data);
+ String[] keyVal = new String(data).split("=");
+ if (keyVal.length != 2) {
+ return null;
+ }
+ txtRecord.set(keyVal[0], keyVal[1]);
+ }
+ return txtRecord;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Creates DnsSd service response.
+ * This is only called from WifiP2pServiceResponse
+ *
+ * @param status status code.
+ * @param dev source device.
+ * @param data DnsSd response data.
+ * @return DnsSd service response data.
+ * @hide
+ */
+ static WifiP2pDnsSdServiceResponse newInstance(int status,
+ int transId, WifiP2pDevice dev, byte[] data) {
+ if (status != WifiP2pServiceResponse.Status.SUCCESS) {
+ return new WifiP2pDnsSdServiceResponse(status,
+ transId, dev, null);
+ }
+ try {
+ return new WifiP2pDnsSdServiceResponse(status,
+ transId, dev, data);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl
new file mode 100644
index 0000000..cf2cb4a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2012 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.p2p.servicediscovery;
+
+parcelable WifiP2pServiceInfo;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
new file mode 100644
index 0000000..b931475
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for storing service information that is advertised
+ * over a Wi-Fi peer-to-peer setup
+ *
+ * @see WifiP2pUpnpServiceInfo
+ * @see WifiP2pDnsSdServiceInfo
+ */
+public class WifiP2pServiceInfo implements Parcelable {
+
+ /**
+ * All service protocol types.
+ */
+ public static final int SERVICE_TYPE_ALL = 0;
+
+ /**
+ * DNS based service discovery protocol.
+ */
+ public static final int SERVICE_TYPE_BONJOUR = 1;
+
+ /**
+ * UPnP protocol.
+ */
+ public static final int SERVICE_TYPE_UPNP = 2;
+
+ /**
+ * WS-Discovery protocol
+ * @hide
+ */
+ public static final int SERVICE_TYPE_WS_DISCOVERY = 3;
+
+ /**
+ * Vendor Specific protocol
+ */
+ public static final int SERVICE_TYPE_VENDOR_SPECIFIC = 255;
+
+ /**
+ * the list of query string for wpa_supplicant
+ *
+ * e.g)
+ * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+ * {"bonjour", "045f697070c00c000c01", "094d795072696e746572c027"
+ *
+ * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+ * {"bonjour", "096d797072696e746572045f697070c00c001001",
+ * "09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074"}
+ *
+ * [UPnP]
+ * # UPnP uuid
+ * {"upnp", "10", "uuid:6859dede-8574-59ab-9332-123456789012"}
+ *
+ * # UPnP rootdevice
+ * {"upnp", "10", "uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"}
+ *
+ * # UPnP device
+ * {"upnp", "10", "uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
+ * -org:device:InternetGatewayDevice:1"}
+ *
+ * # UPnP service
+ * {"upnp", "10", "uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
+ * -org:service:ContentDirectory:2"}
+ */
+ private List<String> mQueryList;
+
+ /**
+ * This is only used in subclass.
+ *
+ * @param queryList query string for wpa_supplicant
+ * @hide
+ */
+ protected WifiP2pServiceInfo(List<String> queryList) {
+ if (queryList == null) {
+ throw new IllegalArgumentException("query list cannot be null");
+ }
+ mQueryList = queryList;
+ }
+
+ /**
+ * Return the list of the query string for wpa_supplicant.
+ *
+ * @return the list of the query string for wpa_supplicant.
+ * @hide
+ */
+ public List<String> getSupplicantQueryList() {
+ return mQueryList;
+ }
+
+ /**
+ * Converts byte array to hex string.
+ *
+ * @param data
+ * @return hex string.
+ * @hide
+ */
+ static String bin2HexStr(byte[] data) {
+ StringBuffer sb = new StringBuffer();
+
+ for (byte b: data) {
+ String s = null;
+ try {
+ s = Integer.toHexString(b & 0xff);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ //add 0 padding
+ if (s.length() == 1) {
+ sb.append('0');
+ }
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof WifiP2pServiceInfo)) {
+ return false;
+ }
+
+ WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)o;
+ return mQueryList.equals(servInfo.mQueryList);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mQueryList == null ? 0 : mQueryList.hashCode());
+ return result;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringList(mQueryList);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pServiceInfo> CREATOR =
+ new Creator<WifiP2pServiceInfo>() {
+ public WifiP2pServiceInfo createFromParcel(Parcel in) {
+
+ List<String> data = new ArrayList<String>();
+ in.readStringList(data);
+ return new WifiP2pServiceInfo(data);
+ }
+
+ public WifiP2pServiceInfo[] newArray(int size) {
+ return new WifiP2pServiceInfo[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl
new file mode 100644
index 0000000..d5a1e8f
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2012 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.p2p.servicediscovery;
+
+parcelable WifiP2pServiceRequest;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
new file mode 100644
index 0000000..c7f0e5f
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class for creating a service discovery request for use with
+ * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
+ *
+ * <p>This class is used to create service discovery request for custom
+ * vendor specific service discovery protocol {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}
+ * or to search all service protocols {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}.
+ *
+ * <p>For the purpose of creating a UPnP or Bonjour service request, use
+ * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} respectively.
+ *
+ * {@see WifiP2pManager}
+ * {@see WifiP2pUpnpServiceRequest}
+ * {@see WifiP2pDnsSdServiceRequest}
+ */
+public class WifiP2pServiceRequest implements Parcelable {
+
+ /**
+ * Service discovery protocol. It's defined in table63 in Wi-Fi Direct specification.
+ */
+ private int mProtocolType;
+
+ /**
+ * The length of the service request TLV.
+ * The value is equal to 2 plus the number of octets in the
+ * query data field.
+ */
+ private int mLength;
+
+ /**
+ * Service transaction ID.
+ * This is a nonzero value used to match the service request/response TLVs.
+ */
+ private int mTransId;
+
+ /**
+ * The hex dump string of query data for the requested service information.
+ *
+ * e.g) DnsSd apple file sharing over tcp (dns name=_afpovertcp._tcp.local.)
+ * 0b5f6166706f766572746370c00c000c01
+ */
+ private String mQuery;
+
+ /**
+ * This constructor is only used in newInstance().
+ *
+ * @param protocolType service discovery protocol.
+ * @param query The part of service specific query.
+ * @hide
+ */
+ protected WifiP2pServiceRequest(int protocolType, String query) {
+ validateQuery(query);
+
+ mProtocolType = protocolType;
+ mQuery = query;
+ if (query != null) {
+ mLength = query.length()/2 + 2;
+ } else {
+ mLength = 2;
+ }
+ }
+
+ /**
+ * This constructor is only used in Parcelable.
+ *
+ * @param serviceType service discovery type.
+ * @param length the length of service discovery packet.
+ * @param transId the transaction id
+ * @param query The part of service specific query.
+ */
+ private WifiP2pServiceRequest(int serviceType, int length,
+ int transId, String query) {
+ mProtocolType = serviceType;
+ mLength = length;
+ mTransId = transId;
+ mQuery = query;
+ }
+
+ /**
+ * Return transaction id.
+ *
+ * @return transaction id
+ * @hide
+ */
+ public int getTransactionId() {
+ return mTransId;
+ }
+
+ /**
+ * Set transaction id.
+ *
+ * @param id
+ * @hide
+ */
+ public void setTransactionId(int id) {
+ mTransId = id;
+ }
+
+ /**
+ * Return wpa_supplicant request string.
+ *
+ * The format is the hex dump of the following frame.
+ * <pre>
+ * _______________________________________________________________
+ * | Length (2) | Type (1) | Transaction ID (1) |
+ * | Query Data (variable) |
+ * </pre>
+ *
+ * @return wpa_supplicant request string.
+ * @hide
+ */
+ public String getSupplicantQuery() {
+ StringBuffer sb = new StringBuffer();
+ // length is retained as little endian format.
+ sb.append(String.format("%02x", (mLength) & 0xff));
+ sb.append(String.format("%02x", (mLength >> 8) & 0xff));
+ sb.append(String.format("%02x", mProtocolType));
+ sb.append(String.format("%02x", mTransId));
+ if (mQuery != null) {
+ sb.append(mQuery);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Validate query.
+ *
+ * <p>If invalid, throw IllegalArgumentException.
+ * @param query The part of service specific query.
+ */
+ private void validateQuery(String query) {
+ if (query == null) {
+ return;
+ }
+
+ int UNSIGNED_SHORT_MAX = 0xffff;
+ if (query.length()%2 == 1) {
+ throw new IllegalArgumentException(
+ "query size is invalid. query=" + query);
+ }
+ if (query.length()/2 > UNSIGNED_SHORT_MAX) {
+ throw new IllegalArgumentException(
+ "query size is too large. len=" + query.length());
+ }
+
+ // check whether query is hex string.
+ query = query.toLowerCase();
+ char[] chars = query.toCharArray();
+ for (char c: chars) {
+ if (!((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f'))){
+ throw new IllegalArgumentException(
+ "query should be hex string. query=" + query);
+ }
+ }
+ }
+
+ /**
+ * Create a service discovery request.
+ *
+ * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
+ * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
+ * In order to create a UPnP or Bonjour service request, use
+ * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
+ * respectively
+ *
+ * @param queryData hex string that is vendor specific. Can be null.
+ * @return service discovery request.
+ */
+ public static WifiP2pServiceRequest newInstance(int protocolType, String queryData) {
+ return new WifiP2pServiceRequest(protocolType, queryData);
+ }
+
+ /**
+ * Create a service discovery request.
+ *
+ * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
+ * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
+ * In order to create a UPnP or Bonjour service request, use
+ * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
+ * respectively
+ *
+ * @return service discovery request.
+ */
+ public static WifiP2pServiceRequest newInstance(int protocolType ) {
+ return new WifiP2pServiceRequest(protocolType, null);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof WifiP2pServiceRequest)) {
+ return false;
+ }
+
+ WifiP2pServiceRequest req = (WifiP2pServiceRequest)o;
+
+ /*
+ * Not compare transaction id.
+ * Transaction id may be changed on each service discovery operation.
+ */
+ if ((req.mProtocolType != mProtocolType) ||
+ (req.mLength != mLength)) {
+ return false;
+ }
+
+ if (req.mQuery == null && mQuery == null) {
+ return true;
+ } else if (req.mQuery != null) {
+ return req.mQuery.equals(mQuery);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mProtocolType;
+ result = 31 * result + mLength;
+ result = 31 * result + (mQuery == null ? 0 : mQuery.hashCode());
+ return result;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mProtocolType);
+ dest.writeInt(mLength);
+ dest.writeInt(mTransId);
+ dest.writeString(mQuery);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pServiceRequest> CREATOR =
+ new Creator<WifiP2pServiceRequest>() {
+ public WifiP2pServiceRequest createFromParcel(Parcel in) {
+ int servType = in.readInt();
+ int length = in.readInt();
+ int transId = in.readInt();
+ String query = in.readString();
+ return new WifiP2pServiceRequest(servType, length, transId, query);
+ }
+
+ public WifiP2pServiceRequest[] newArray(int size) {
+ return new WifiP2pServiceRequest[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl
new file mode 100644
index 0000000..c81d1f9
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2012 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.p2p.servicediscovery;
+
+parcelable WifiP2pServiceResponse;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
new file mode 100644
index 0000000..ac31663
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The class for a response of service discovery.
+ *
+ * @hide
+ */
+public class WifiP2pServiceResponse implements Parcelable {
+
+ private static int MAX_BUF_SIZE = 1024;
+
+ /**
+ * Service type. It's defined in table63 in Wi-Fi Direct specification.
+ */
+ protected int mServiceType;
+
+ /**
+ * Status code of service discovery response.
+ * It's defined in table65 in Wi-Fi Direct specification.
+ * @see Status
+ */
+ protected int mStatus;
+
+ /**
+ * Service transaction ID.
+ * This is a nonzero value used to match the service request/response TLVs.
+ */
+ protected int mTransId;
+
+ /**
+ * Source device.
+ */
+ protected WifiP2pDevice mDevice;
+
+ /**
+ * Service discovery response data based on the requested on
+ * the service protocol type. The protocol format depends on the service type.
+ */
+ protected byte[] mData;
+
+
+ /**
+ * The status code of service discovery response.
+ * Currently 4 status codes are defined and the status codes from 4 to 255
+ * are reserved.
+ *
+ * See Wi-Fi Direct specification for the detail.
+ */
+ public static class Status {
+ /** success */
+ public static final int SUCCESS = 0;
+
+ /** the service protocol type is not available */
+ public static final int SERVICE_PROTOCOL_NOT_AVAILABLE = 1;
+
+ /** the requested information is not available */
+ public static final int REQUESTED_INFORMATION_NOT_AVAILABLE = 2;
+
+ /** bad request */
+ public static final int BAD_REQUEST = 3;
+
+ /** @hide */
+ public static String toString(int status) {
+ switch(status) {
+ case SUCCESS:
+ return "SUCCESS";
+ case SERVICE_PROTOCOL_NOT_AVAILABLE:
+ return "SERVICE_PROTOCOL_NOT_AVAILABLE";
+ case REQUESTED_INFORMATION_NOT_AVAILABLE:
+ return "REQUESTED_INFORMATION_NOT_AVAILABLE";
+ case BAD_REQUEST:
+ return "BAD_REQUEST";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /** not used */
+ private Status() {}
+ }
+
+ /**
+ * Hidden constructor. This is only used in framework.
+ *
+ * @param serviceType service discovery type.
+ * @param status status code.
+ * @param transId transaction id.
+ * @param device source device.
+ * @param data query data.
+ */
+ protected WifiP2pServiceResponse(int serviceType, int status, int transId,
+ WifiP2pDevice device, byte[] data) {
+ mServiceType = serviceType;
+ mStatus = status;
+ mTransId = transId;
+ mDevice = device;
+ mData = data;
+ }
+
+ /**
+ * Return the service type of service discovery response.
+ *
+ * @return service discovery type.<br>
+ * e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_BONJOUR}
+ */
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ /**
+ * Return the status code of service discovery response.
+ *
+ * @return status code.
+ * @see Status
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Return the transaction id of service discovery response.
+ *
+ * @return transaction id.
+ * @hide
+ */
+ public int getTransactionId() {
+ return mTransId;
+ }
+
+ /**
+ * Return response data.
+ *
+ * <pre>Data format depends on service type
+ *
+ * @return a query or response data.
+ */
+ public byte[] getRawData() {
+ return mData;
+ }
+
+ /**
+ * Returns the source device of service discovery response.
+ *
+ * <pre>This is valid only when service discovery response.
+ *
+ * @return the source device of service discovery response.
+ */
+ public WifiP2pDevice getSrcDevice() {
+ return mDevice;
+ }
+
+ /** @hide */
+ public void setSrcDevice(WifiP2pDevice dev) {
+ if (dev == null) return;
+ this.mDevice = dev;
+ }
+
+
+ /**
+ * Create the list of WifiP2pServiceResponse instance from supplicant event.
+ *
+ * <pre>The format is as follows.
+ * P2P-SERV-DISC-RESP &lt;address&gt; &lt;update indicator&gt; &lt;response data&gt;
+ * e.g) P2P-SERV-DISC-RESP 02:03:7f:11:62:da 1 0300000101
+ *
+ * @param supplicantEvent wpa_supplicant event string.
+ * @return if parse failed, return null
+ * @hide
+ */
+ public static List<WifiP2pServiceResponse> newInstance(String supplicantEvent) {
+
+ List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>();
+ String[] args = supplicantEvent.split(" ");
+ if (args.length != 4) {
+ return null;
+ }
+ WifiP2pDevice dev = new WifiP2pDevice();
+ String srcAddr = args[1];
+ dev.deviceAddress = srcAddr;
+ //String updateIndicator = args[2];//not used.
+ byte[] bin = hexStr2Bin(args[3]);
+ if (bin == null) {
+ return null;
+ }
+
+ DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bin));
+ try {
+ while (dis.available() > 0) {
+ /*
+ * Service discovery header is as follows.
+ * ______________________________________________________________
+ * | Length(2byte) | Type(1byte) | TransId(1byte)}|
+ * ______________________________________________________________
+ * | status(1byte) | vendor specific(variable) |
+ */
+ // The length equals to 3 plus the number of octets in the vendor
+ // specific content field. And this is little endian.
+ int length = ((dis.readByte() & 0xff) +
+ ((dis.readByte() & 0xff) << 8)) - 3;
+ int type = dis.readUnsignedByte();
+ byte transId = dis.readByte();
+ int status = dis.readUnsignedByte();
+ if (length < 0) {
+ return null;
+ }
+ if (length == 0) {
+ if (status == Status.SUCCESS) {
+ respList.add(new WifiP2pServiceResponse(type, status,
+ transId, dev, null));
+ }
+ continue;
+ }
+ if (length > MAX_BUF_SIZE) {
+ dis.skip(length);
+ continue;
+ }
+ byte[] data = new byte[length];
+ dis.readFully(data);
+
+ WifiP2pServiceResponse resp;
+ if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
+ resp = WifiP2pDnsSdServiceResponse.newInstance(status,
+ transId, dev, data);
+ } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
+ resp = WifiP2pUpnpServiceResponse.newInstance(status,
+ transId, dev, data);
+ } else {
+ resp = new WifiP2pServiceResponse(type, status, transId, dev, data);
+ }
+ if (resp != null && resp.getStatus() == Status.SUCCESS) {
+ respList.add(resp);
+ }
+ }
+ return respList;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (respList.size() > 0) {
+ return respList;
+ }
+ return null;
+ }
+
+ /**
+ * Converts hex string to byte array.
+ *
+ * @param hex hex string. if invalid, return null.
+ * @return binary data.
+ */
+ private static byte[] hexStr2Bin(String hex) {
+ int sz = hex.length()/2;
+ byte[] b = new byte[hex.length()/2];
+
+ for (int i=0;i<sz;i++) {
+ try {
+ b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ return b;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("serviceType:").append(mServiceType);
+ sbuf.append(" status:").append(Status.toString(mStatus));
+ sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
+ sbuf.append(" data:").append(mData);
+ return sbuf.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof WifiP2pServiceResponse)) {
+ return false;
+ }
+
+ WifiP2pServiceResponse req = (WifiP2pServiceResponse)o;
+
+ return (req.mServiceType == mServiceType) &&
+ (req.mStatus == mStatus) &&
+ equals(req.mDevice.deviceAddress, mDevice.deviceAddress) &&
+ Arrays.equals(req.mData, mData);
+ }
+
+ private boolean equals(Object a, Object b) {
+ if (a == null && b == null) {
+ return true;
+ } else if (a != null) {
+ return a.equals(b);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mServiceType;
+ result = 31 * result + mStatus;
+ result = 31 * result + mTransId;
+ result = 31 * result + (mDevice.deviceAddress == null ?
+ 0 : mDevice.deviceAddress.hashCode());
+ result = 31 * result + (mData == null ? 0 : mData.hashCode());
+ return result;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mServiceType);
+ dest.writeInt(mStatus);
+ dest.writeInt(mTransId);
+ dest.writeParcelable(mDevice, flags);
+ if (mData == null || mData.length == 0) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(mData.length);
+ dest.writeByteArray(mData);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pServiceResponse> CREATOR =
+ new Creator<WifiP2pServiceResponse>() {
+ public WifiP2pServiceResponse createFromParcel(Parcel in) {
+
+ int type = in.readInt();
+ int status = in.readInt();
+ int transId = in.readInt();
+ WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null);
+ int len = in.readInt();
+ byte[] data = null;
+ if (len > 0) {
+ data = new byte[len];
+ in.readByteArray(data);
+ }
+ if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
+ return WifiP2pDnsSdServiceResponse.newInstance(status,
+ transId, dev, data);
+ } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
+ return WifiP2pUpnpServiceResponse.newInstance(status,
+ transId, dev, data);
+ }
+ return new WifiP2pServiceResponse(type, status, transId, dev, data);
+ }
+
+ public WifiP2pServiceResponse[] newArray(int size) {
+ return new WifiP2pServiceResponse[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
new file mode 100644
index 0000000..40a0d61
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A class for storing Upnp service information that is advertised
+ * over a Wi-Fi peer-to-peer setup.
+ *
+ * {@see android.net.wifi.p2p.WifiP2pManager#addLocalService}
+ * {@see android.net.wifi.p2p.WifiP2pManager#removeLocalService}
+ * {@see WifiP2pServiceInfo}
+ * {@see WifiP2pDnsSdServiceInfo}
+ */
+public class WifiP2pUpnpServiceInfo extends WifiP2pServiceInfo {
+
+ /**
+ * UPnP version 1.0.
+ *
+ * <pre>Query Version should always be set to 0x10 if the query values are
+ * compatible with UPnP Device Architecture 1.0.
+ * @hide
+ */
+ public static final int VERSION_1_0 = 0x10;
+
+ /**
+ * This constructor is only used in newInstance().
+ *
+ * @param queryList
+ */
+ private WifiP2pUpnpServiceInfo(List<String> queryList) {
+ super(queryList);
+ }
+
+ /**
+ * Create UPnP service information object.
+ *
+ * @param uuid a string representation of this UUID in the following format,
+ * as per <a href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a>.<br>
+ * e.g) 6859dede-8574-59ab-9332-123456789012
+ * @param device a string representation of this device in the following format,
+ * as per
+ * <a href="http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf">
+ * UPnP Device Architecture1.1</a><br>
+ * e.g) urn:schemas-upnp-org:device:MediaServer:1
+ * @param services a string representation of this service in the following format,
+ * as per
+ * <a href="http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf">
+ * UPnP Device Architecture1.1</a><br>
+ * e.g) urn:schemas-upnp-org:service:ContentDirectory:1
+ * @return UPnP service information object.
+ */
+ public static WifiP2pUpnpServiceInfo newInstance(String uuid,
+ String device, List<String> services) {
+ if (uuid == null || device == null) {
+ throw new IllegalArgumentException("uuid or device cannnot be null");
+ }
+ UUID.fromString(uuid);
+
+ ArrayList<String> info = new ArrayList<String>();
+
+ info.add(createSupplicantQuery(uuid, null));
+ info.add(createSupplicantQuery(uuid, "upnp:rootdevice"));
+ info.add(createSupplicantQuery(uuid, device));
+ if (services != null) {
+ for (String service:services) {
+ info.add(createSupplicantQuery(uuid, service));
+ }
+ }
+
+ return new WifiP2pUpnpServiceInfo(info);
+ }
+
+ /**
+ * Create wpa_supplicant service query for upnp.
+ *
+ * @param uuid
+ * @param data
+ * @return wpa_supplicant service query for upnp
+ */
+ private static String createSupplicantQuery(String uuid, String data) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("upnp ");
+ sb.append(String.format("%02x ", VERSION_1_0));
+ sb.append("uuid:");
+ sb.append(uuid);
+ if (data != null) {
+ sb.append("::");
+ sb.append(data);
+ }
+ return sb.toString();
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java
new file mode 100644
index 0000000..b5cf144
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.wifi.p2p.WifiP2pManager;
+
+/**
+ * A class for creating a Upnp service discovery request for use with
+ * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
+ *
+ * {@see WifiP2pManager}
+ * {@see WifiP2pServiceRequest}
+ * {@see WifiP2pDnsSdServiceRequest}
+ */
+public class WifiP2pUpnpServiceRequest extends WifiP2pServiceRequest {
+
+ /**
+ * This constructor is only used in newInstance().
+ *
+ * @param query The part of service specific query.
+ * @hide
+ */
+ protected WifiP2pUpnpServiceRequest(String query) {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_UPNP, query);
+ }
+
+ /**
+ * This constructor is only used in newInstance().
+ * @hide
+ */
+ protected WifiP2pUpnpServiceRequest() {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_UPNP, null);
+ }
+
+ /**
+ * Create a service discovery request to search all UPnP services.
+ *
+ * @return service request for UPnP.
+ */
+ public static WifiP2pUpnpServiceRequest newInstance() {
+ return new WifiP2pUpnpServiceRequest();
+ }
+ /**
+ * Create a service discovery request to search specified UPnP services.
+ *
+ * @param st ssdp search target. Cannot be null.<br>
+ * e.g ) <br>
+ * <ul>
+ * <li>"ssdp:all"
+ * <li>"upnp:rootdevice"
+ * <li>"urn:schemas-upnp-org:device:MediaServer:2"
+ * <li>"urn:schemas-upnp-org:service:ContentDirectory:2"
+ * <li>"uuid:6859dede-8574-59ab-9332-123456789012"
+ * </ul>
+ * @return service request for UPnP.
+ */
+ public static WifiP2pUpnpServiceRequest newInstance(String st) {
+ if (st == null) {
+ throw new IllegalArgumentException("search target cannot be null");
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append(String.format("%02x", WifiP2pUpnpServiceInfo.VERSION_1_0));
+ sb.append(WifiP2pServiceInfo.bin2HexStr(st.getBytes()));
+ return new WifiP2pUpnpServiceRequest(sb.toString());
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceResponse.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceResponse.java
new file mode 100644
index 0000000..ab95af6
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceResponse.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2012 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.p2p.nsd;
+
+import android.net.wifi.p2p.WifiP2pDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for a response of upnp service discovery.
+ *
+ * @hide
+ */
+public class WifiP2pUpnpServiceResponse extends WifiP2pServiceResponse {
+
+ /**
+ * UPnP version. should be {@link WifiP2pUpnpServiceInfo#VERSION_1_0}
+ */
+ private int mVersion;
+
+ /**
+ * The list of Unique Service Name.
+ * e.g)
+ *{"uuid:6859dede-8574-59ab-9332-123456789012",
+ *"uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"}
+ */
+ private List<String> mUniqueServiceNames;
+
+ /**
+ * Return UPnP version number.
+ *
+ * @return version number.
+ * @see WifiP2pUpnpServiceInfo#VERSION_1_0
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Return Unique Service Name strings.
+ *
+ * @return Unique Service Name.<br>
+ * e.g ) <br>
+ * <ul>
+ * <li>"uuid:6859dede-8574-59ab-9332-123456789012"
+ * <li>"uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"
+ * <li>"uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:
+ * MediaServer:2"
+ * <li>"uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:service:
+ * ContentDirectory:2"
+ * </ul>
+ */
+ public List<String> getUniqueServiceNames() {
+ return mUniqueServiceNames;
+ }
+
+ /**
+ * hidden constructor.
+ *
+ * @param status status code
+ * @param transId transaction id
+ * @param dev source device
+ * @param data UPnP response data.
+ */
+ protected WifiP2pUpnpServiceResponse(int status,
+ int transId, WifiP2pDevice dev, byte[] data) {
+ super(WifiP2pServiceInfo.SERVICE_TYPE_UPNP,
+ status, transId, dev, data);
+ if (!parse()) {
+ throw new IllegalArgumentException("Malformed upnp service response");
+ }
+ }
+
+ /**
+ * Parse UPnP service discovery response
+ *
+ * @return {@code true} if the operation succeeded
+ */
+ private boolean parse() {
+ /*
+ * The data format is as follows.
+ *
+ * ______________________________________________________
+ * | Version (1) | USN (Variable) |
+ */
+ if (mData == null) {
+ // the empty is OK.
+ return true;
+ }
+
+ if (mData.length < 1) {
+ return false;
+ }
+
+ mVersion = mData[0] & 0xff;
+ String[] names = new String(mData, 1, mData.length-1).split(",");
+ mUniqueServiceNames = new ArrayList<String>();
+ for (String name : names) {
+ mUniqueServiceNames.add(name);
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("serviceType:UPnP(").append(mServiceType).append(")");
+ sbuf.append(" status:").append(Status.toString(mStatus));
+ sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
+ sbuf.append(" version:").append(String.format("%02x", mVersion));
+ if (mUniqueServiceNames != null) {
+ for (String name : mUniqueServiceNames) {
+ sbuf.append(" usn:").append(name);
+ }
+ }
+ return sbuf.toString();
+ }
+
+ /**
+ * Create upnp service response.
+ *
+ * <pre>This is only used in{@link WifiP2pServiceResponse}
+ *
+ * @param status status code.
+ * @param transId transaction id.
+ * @param device source device.
+ * @param data UPnP response data.
+ * @return UPnP service response data.
+ * @hide
+ */
+ static WifiP2pUpnpServiceResponse newInstance(int status,
+ int transId, WifiP2pDevice device, byte[] data) {
+ if (status != WifiP2pServiceResponse.Status.SUCCESS) {
+ return new WifiP2pUpnpServiceResponse(status, transId, device, null);
+ }
+
+ try {
+ return new WifiP2pUpnpServiceResponse(status, transId, device, data);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}