Irfan Sheriff 633fe60b78 Fix suspend bugs
Listen to screen broadcast all the time to avoid situations of losing
this information when wifi is turned on and off

Avoid duplicate suspend commands to driver

Bug: 7156907
Change-Id: I162b7ba249ae8925119a534229045965c0c13d1f
2012-09-14 16:42:58 -07:00

802 lines
25 KiB
Java

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.wifi;
import android.net.wifi.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;
import java.lang.Process;
import java.util.ArrayList;
import java.util.List;
/**
* Native calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
*
* 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;
String mInterface = "";
private boolean mSuspendOptEnabled = false;
public native static boolean loadDriver();
public native static boolean isDriverLoaded();
public native static boolean unloadDriver();
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();
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 WifiNative(String iface) {
mInterface = iface;
mTAG = "WifiNative-" + iface;
}
public boolean connectToSupplicant() {
return connectToSupplicant(mInterface);
}
public void closeSupplicantConnection() {
closeSupplicantConnection(mInterface);
}
public String waitForEvent() {
return waitForEvent(mInterface);
}
private boolean doBooleanCommand(String command) {
if (DBG) Log.d(mTAG, "doBoolean: " + command);
return doBooleanCommand(mInterface, command);
}
private int doIntCommand(String command) {
if (DBG) Log.d(mTAG, "doInt: " + command);
return doIntCommand(mInterface, command);
}
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 String getNetworkVariable(int netId, String name) {
if (TextUtils.isEmpty(name)) return null;
return doStringCommand("GET_NETWORK " + netId + " " + name);
}
public boolean removeNetwork(int netId) {
return doBooleanCommand("REMOVE_NETWORK " + netId);
}
public boolean enableNetwork(int netId, boolean disableOthers) {
if (disableOthers) {
return doBooleanCommand("SELECT_NETWORK " + netId);
} else {
return doBooleanCommand("ENABLE_NETWORK " + netId);
}
}
public boolean disableNetwork(int netId) {
return doBooleanCommand("DISABLE_NETWORK " + netId);
}
public boolean reconnect() {
return doBooleanCommand("RECONNECT");
}
public boolean reassociate() {
return doBooleanCommand("REASSOCIATE");
}
public boolean disconnect() {
return doBooleanCommand("DISCONNECT");
}
public String status() {
return doStringCommand("STATUS");
}
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;
}
/**
* Format of results:
* =================
* bssid=68:7f:74:d7:1b:6e
* freq=2412
* level=-43
* tsf=1344621975160944
* age=2623
* flags=[WPA2-PSK-CCMP][WPS][ESS]
* ssid=zubyb
*
* RANGE=ALL gets all scan results
* MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
*/
public String scanResults() {
return doStringCommand("BSS RANGE=ALL MASK=0x1986");
}
public boolean startDriver() {
return doBooleanCommand("DRIVER START");
}
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 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 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 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 boolean stopFilteringMulticastV6Packets() {
return doBooleanCommand("DRIVER RXFILTER-STOP")
&& doBooleanCommand("DRIVER RXFILTER-ADD 3")
&& doBooleanCommand("DRIVER RXFILTER-START");
}
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 boolean setBand(int band) {
return doBooleanCommand("DRIVER SETBAND " + band);
}
/**
* Sets the bluetooth coexistence mode.
*
* @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
* {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
* {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
* @return Whether the mode was successfully set.
*/
public boolean setBluetoothCoexistenceMode(int mode) {
return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
}
/**
* Enable or disable Bluetooth coexistence scan mode. When this mode is on,
* some of the low-level scan parameters used by the driver are changed to
* reduce interference with A2DP streaming.
*
* @param isSet whether to enable or disable this mode
* @return {@code true} if the command succeeded, {@code false} otherwise.
*/
public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
if (setCoexScanMode) {
return doBooleanCommand("DRIVER BTCOEXSCAN-START");
} else {
return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
}
}
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 boolean setScanResultHandling(int mode) {
return doBooleanCommand("AP_SCAN " + mode);
}
public boolean addToBlacklist(String bssid) {
if (TextUtils.isEmpty(bssid)) return false;
return doBooleanCommand("BLACKLIST " + bssid);
}
public boolean clearBlacklist() {
return doBooleanCommand("BLACKLIST clear");
}
public boolean setSuspendOptimizations(boolean enabled) {
if (mSuspendOptEnabled == enabled) return true;
mSuspendOptEnabled = enabled;
if (enabled) {
return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
} else {
return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
}
}
public boolean setCountryCode(String countryCode) {
return doBooleanCommand("DRIVER COUNTRY " + countryCode);
}
public void enableBackgroundScan(boolean enable) {
if (enable) {
doBooleanCommand("SET pno 1");
} else {
doBooleanCommand("SET pno 0");
}
}
public void setScanInterval(int scanInterval) {
doBooleanCommand("SCAN_INTERVAL " + scanInterval);
}
/** Example output:
* RSSI=-65
* LINKSPEED=48
* NOISE=9999
* FREQUENCY=0
*/
public String signalPoll() {
return doStringCommand("SIGNAL_POLL");
}
/** Example outout:
* TXGOOD=396
* TXBAD=1
*/
public String pktcntPoll() {
return doStringCommand("PKTCNT_POLL");
}
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 boolean startWpsPinKeypad(String pin) {
if (TextUtils.isEmpty(pin)) return false;
return doBooleanCommand("WPS_PIN any " + pin);
}
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 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 setManufacturer(String value) {
return doBooleanCommand("SET manufacturer " + value);
}
public boolean setModelName(String value) {
return doBooleanCommand("SET model_name " + value);
}
public boolean setModelNumber(String value) {
return doBooleanCommand("SET model_number " + value);
}
public boolean setSerialNumber(String value) {
return doBooleanCommand("SET serial_number " + value);
}
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 void setPowerSave(boolean enabled) {
if (enabled) {
doBooleanCommand("SET ps 1");
} else {
doBooleanCommand("SET ps 0");
}
}
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 boolean setWfdEnable(boolean enable) {
return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
}
public boolean setWfdDeviceInfo(String hex) {
return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
}
/**
* "sta" prioritizes STA connection over P2P and "p2p" prioritizes
* P2P connection over STA
*/
public boolean setConcurrencyPriority(String s) {
return doBooleanCommand("P2P_SET conc_pref " + s);
}
public boolean p2pFind() {
return doBooleanCommand("P2P_FIND");
}
public boolean p2pFind(int timeout) {
if (timeout <= 0) {
return p2pFind();
}
return doBooleanCommand("P2P_FIND " + timeout);
}
public boolean p2pStopFind() {
return doBooleanCommand("P2P_STOP_FIND");
}
public boolean p2pListen() {
return doBooleanCommand("P2P_LISTEN");
}
public boolean p2pListen(int timeout) {
if (timeout <= 0) {
return p2pListen();
}
return doBooleanCommand("P2P_LISTEN " + timeout);
}
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 String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
if (config == null) return null;
List<String> args = new ArrayList<String>();
WpsInfo wps = config.wps;
args.add(config.deviceAddress);
switch (wps.setup) {
case WpsInfo.PBC:
args.add("pbc");
break;
case WpsInfo.DISPLAY:
if (TextUtils.isEmpty(wps.pin)) {
args.add("pin");
} else {
args.add(wps.pin);
}
args.add("display");
break;
case WpsInfo.KEYPAD:
args.add(wps.pin);
args.add("keypad");
break;
case WpsInfo.LABEL:
args.add(wps.pin);
args.add("label");
default:
break;
}
if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
args.add("persistent");
}
if (joinExistingGroup) {
args.add("join");
} else {
//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 = DEFAULT_GROUP_OWNER_INTENT;
}
args.add("go_intent=" + groupOwnerIntent);
}
String command = "P2P_CONNECT ";
for (String s : args) command += s + " ";
return doStringCommand(command);
}
public boolean p2pCancelConnect() {
return doBooleanCommand("P2P_CANCEL");
}
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(boolean persistent) {
if (persistent) {
return doBooleanCommand("P2P_GROUP_ADD persistent");
}
return doBooleanCommand("P2P_GROUP_ADD");
}
public boolean p2pGroupAdd(int netId) {
return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
}
public boolean p2pGroupRemove(String iface) {
if (TextUtils.isEmpty(iface)) return false;
return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
}
public boolean p2pReject(String deviceAddress) {
return doBooleanCommand("P2P_REJECT " + deviceAddress);
}
/* Invite a peer to a group */
public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
if (TextUtils.isEmpty(deviceAddress)) return false;
if (group == null) {
return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
} else {
return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
+ " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
}
}
/* Reinvoke a persistent connection */
public boolean p2pReinvoke(int netId, String deviceAddress) {
if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
}
public String p2pGetSsid(String deviceAddress) {
return p2pGetParam(deviceAddress, "oper_ssid");
}
public String p2pGetDeviceAddress() {
String status = status();
if (status == null) return "";
String[] tokens = status.split("\n");
for (String token : tokens) {
if (token.startsWith("p2p_device_address=")) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) break;
return nameValue[1];
}
}
return "";
}
public int getGroupCapability(String deviceAddress) {
int gc = 0;
if (TextUtils.isEmpty(deviceAddress)) return gc;
String peerInfo = p2pPeer(deviceAddress);
if (TextUtils.isEmpty(peerInfo)) return gc;
String[] tokens = peerInfo.split("\n");
for (String token : tokens) {
if (token.startsWith("group_capab=")) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) break;
try {
return Integer.decode(nameValue[1]);
} catch(NumberFormatException e) {
return gc;
}
}
}
return gc;
}
public String p2pPeer(String deviceAddress) {
return doStringCommand("P2P_PEER " + deviceAddress);
}
private String p2pGetParam(String deviceAddress, String key) {
if (deviceAddress == null) return null;
String peerInfo = p2pPeer(deviceAddress);
if (peerInfo == null) return null;
String[] tokens= peerInfo.split("\n");
key += "=";
for (String token : tokens) {
if (token.startsWith(key)) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) break;
return nameValue[1];
}
}
return null;
}
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);
}
}