Merge "Pre-association service discovery support"
This commit is contained in:
committed by
Android (Google) Code Review
commit
d4fecc2567
@@ -24,6 +24,8 @@ package android.net.nsd;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Parcel;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class handles TXT record data for DNS based service discovery as specified at
|
||||
* http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11
|
||||
@@ -160,7 +162,7 @@ public class DnsSdTxtRecord implements Parcelable {
|
||||
|
||||
/* Gets the raw data in bytes */
|
||||
public byte[] getRawData() {
|
||||
return mData;
|
||||
return (byte[]) mData.clone();
|
||||
}
|
||||
|
||||
private void insert(byte[] keyBytes, byte[] value, int index) {
|
||||
@@ -279,6 +281,24 @@ public class DnsSdTxtRecord implements Parcelable {
|
||||
return result != null ? result : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof DnsSdTxtRecord)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DnsSdTxtRecord record = (DnsSdTxtRecord)o;
|
||||
return Arrays.equals(record.mData, mData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(mData);
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#define WIFI_PKG_NAME "android/net/wifi/WifiNative"
|
||||
#define BUF_SIZE 256
|
||||
#define EVENT_BUF_SIZE 2048
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -140,7 +141,7 @@ static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject, jst
|
||||
|
||||
static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject, jstring jIface)
|
||||
{
|
||||
char buf[BUF_SIZE];
|
||||
char buf[EVENT_BUF_SIZE];
|
||||
ScopedUtfChars ifname(env, jIface);
|
||||
int nread = ::wifi_wait_for_event(ifname.c_str(), buf, sizeof buf);
|
||||
if (nread > 0) {
|
||||
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
@@ -29,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;
|
||||
|
||||
@@ -214,6 +216,52 @@ 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 dev_addr=02:90:4c:a0:92:54 */
|
||||
private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
|
||||
@@ -268,6 +316,7 @@ public class WifiMonitor {
|
||||
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;
|
||||
@@ -558,6 +607,13 @@ public class WifiMonitor {
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +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.os.SystemProperties;
|
||||
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;
|
||||
@@ -644,4 +645,78 @@ public class WifiNative {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.WifiP2pBonjourServiceInfo;
|
||||
import android.net.wifi.p2p.nsd.WifiP2pBonjourServiceResponse;
|
||||
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
|
||||
@@ -290,6 +299,61 @@ public class WifiP2pManager {
|
||||
/** @hide */
|
||||
public static final int RESPONSE_GROUP_INFO = BASE + 24;
|
||||
|
||||
/** @hide */
|
||||
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 ADD_LOCAL_SERVICE_SUCCEEDED = BASE + 30;
|
||||
|
||||
/** @hide */
|
||||
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 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
|
||||
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
|
||||
@@ -321,6 +385,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 {
|
||||
/**
|
||||
@@ -369,6 +441,93 @@ public class WifiP2pManager {
|
||||
public void onGroupInfoAvailable(WifiP2pGroup group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback invocation when service discovery response other than
|
||||
* UPnP or Bonjour is received
|
||||
* @hide
|
||||
*/
|
||||
public interface ServiceResponseListener {
|
||||
|
||||
/**
|
||||
* The requested service response is available.
|
||||
*
|
||||
* @param serviceType service type. see the service type of
|
||||
* {@link WifiP2pServiceInfo}
|
||||
* @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 serviceType,
|
||||
byte[] responseData, WifiP2pDevice srcDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback invocation when Bonjour service discovery response
|
||||
* is received
|
||||
* @hide
|
||||
*/
|
||||
public interface BonjourServiceResponseListener {
|
||||
|
||||
/**
|
||||
* 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 onBonjourServiceAvailable(String instanceName,
|
||||
String registrationType, WifiP2pDevice srcDevice);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback invocation when Bonjour TXT record is available
|
||||
* for a service
|
||||
* @hide
|
||||
*/
|
||||
public interface BonjourTxtRecordListener {
|
||||
/**
|
||||
* 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 onBonjourTxtRecordAvailable(String fullDomainName,
|
||||
DnsSdTxtRecord record,
|
||||
WifiP2pDevice srcDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback invocation when upnp service discovery response
|
||||
* is received
|
||||
* @hide
|
||||
* */
|
||||
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
|
||||
@@ -382,6 +541,10 @@ public class WifiP2pManager {
|
||||
}
|
||||
private final static int INVALID_LISTENER_KEY = 0;
|
||||
private ChannelListener mChannelListener;
|
||||
private ServiceResponseListener mServRspListener;
|
||||
private BonjourServiceResponseListener mBonjourServRspListener;
|
||||
private BonjourTxtRecordListener mBonjourTxtListener;
|
||||
private UpnpServiceResponseListener mUpnpServRspListener;
|
||||
private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
|
||||
private Object mListenerMapLock = new Object();
|
||||
private int mListenerKey = 0;
|
||||
@@ -406,10 +569,17 @@ public class WifiP2pManager {
|
||||
/* 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);
|
||||
}
|
||||
@@ -417,10 +587,17 @@ public class WifiP2pManager {
|
||||
/* 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();
|
||||
}
|
||||
@@ -443,6 +620,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;
|
||||
@@ -450,7 +631,47 @@ public class WifiP2pManager {
|
||||
}
|
||||
}
|
||||
|
||||
int putListener(Object listener) {
|
||||
private void handleServiceResponse(WifiP2pServiceResponse resp) {
|
||||
if (resp instanceof WifiP2pBonjourServiceResponse) {
|
||||
handleBonjourServiceResponse((WifiP2pBonjourServiceResponse)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 handleBonjourServiceResponse(WifiP2pBonjourServiceResponse resp) {
|
||||
if (resp.getDnsType() == WifiP2pBonjourServiceInfo.DNS_TYPE_PTR) {
|
||||
if (mBonjourServRspListener != null) {
|
||||
mBonjourServRspListener.onBonjourServiceAvailable(
|
||||
resp.getInstanceName(),
|
||||
resp.getDnsQueryName(),
|
||||
resp.getSrcDevice());
|
||||
}
|
||||
} else if (resp.getDnsType() == WifiP2pBonjourServiceInfo.DNS_TYPE_TXT) {
|
||||
if (mBonjourTxtListener != null) {
|
||||
mBonjourTxtListener.onBonjourTxtRecordAvailable(
|
||||
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) {
|
||||
@@ -462,7 +683,7 @@ public class WifiP2pManager {
|
||||
return key;
|
||||
}
|
||||
|
||||
Object getListener(int key) {
|
||||
private Object getListener(int key) {
|
||||
if (key == INVALID_LISTENER_KEY) return null;
|
||||
synchronized (mListenerMapLock) {
|
||||
return mListenerMap.remove(key);
|
||||
@@ -470,6 +691,18 @@ public class WifiP2pManager {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -512,7 +745,7 @@ 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));
|
||||
}
|
||||
|
||||
@@ -522,7 +755,7 @@ public class WifiP2pManager {
|
||||
* @hide
|
||||
*/
|
||||
public void stopPeerDiscovery(Channel c, ActionListener listener) {
|
||||
if (c == null) return;
|
||||
checkChannel(c);
|
||||
c.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, c.putListener(listener));
|
||||
}
|
||||
|
||||
@@ -549,7 +782,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);
|
||||
}
|
||||
|
||||
@@ -565,7 +798,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));
|
||||
}
|
||||
|
||||
@@ -589,7 +822,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));
|
||||
}
|
||||
|
||||
@@ -605,10 +838,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 of service discovery.
|
||||
*
|
||||
* <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 the subclass of {@link WifiP2pServiceInfo}.<br>
|
||||
* e.g ) {@link WifiP2pUpnpServiceInfo#newInstance} or
|
||||
* {@link WifiP2pBonjourServiceInfo#newInstance}
|
||||
*
|
||||
* <p>If a local service is added, the framework responds the appropriate service discovery
|
||||
* request automatically.
|
||||
*
|
||||
* <p>These service information will be clear when p2p is disabled or call
|
||||
* {@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.
|
||||
* @hide
|
||||
*/
|
||||
public void addLocalService(Channel c, WifiP2pServiceInfo servInfo, ActionListener listener) {
|
||||
checkChannel(c);
|
||||
checkServiceInfo(servInfo);
|
||||
c.mAsyncChannel.sendMessage(ADD_LOCAL_SERVICE, 0, c.putListener(listener), servInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a specified local service of service discovery.
|
||||
*
|
||||
* <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.
|
||||
* @hide
|
||||
*/
|
||||
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.
|
||||
* @hide
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <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.
|
||||
* @hide
|
||||
*/
|
||||
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
|
||||
* @hide
|
||||
*/
|
||||
public void setBonjourResponseListeners(Channel c,
|
||||
BonjourServiceResponseListener servListener, BonjourTxtRecordListener txtListener) {
|
||||
checkChannel(c);
|
||||
c.mBonjourServRspListener = servListener;
|
||||
c.mBonjourTxtListener = 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.
|
||||
* @hide
|
||||
*/
|
||||
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 #setBonjourServiceResponseListener}, or {@link #setUpnpServiceResponseListener}.
|
||||
*
|
||||
* @param c is the channel created at {@link #initialize}
|
||||
* @param listener for callbacks on success or failure. Can be null.
|
||||
* @hide
|
||||
*/
|
||||
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>These information will be clear when wifi p2p is disabled or
|
||||
* {@link #removeServiceRequest(Channel, WifiP2pServiceRequest, ActionListener)} or
|
||||
* {@link #clearServiceRequests(Channel, ActionListener)} is called.
|
||||
*
|
||||
* @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.
|
||||
* @hide
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <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.
|
||||
* @hide
|
||||
*/
|
||||
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.
|
||||
* @hide
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -616,7 +1056,7 @@ public class WifiP2pManager {
|
||||
* @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));
|
||||
}
|
||||
|
||||
@@ -627,7 +1067,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));
|
||||
}
|
||||
|
||||
@@ -638,7 +1078,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -55,6 +58,7 @@ 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;
|
||||
@@ -71,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
|
||||
@@ -150,6 +157,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
|
||||
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"};
|
||||
@@ -313,6 +330,10 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
|
||||
WifiP2pManager.BUSY);
|
||||
break;
|
||||
case WifiP2pManager.DISCOVER_SERVICES:
|
||||
replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
|
||||
WifiP2pManager.BUSY);
|
||||
break;
|
||||
case WifiP2pManager.CONNECT:
|
||||
replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
|
||||
WifiP2pManager.BUSY);
|
||||
@@ -329,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;
|
||||
@@ -381,6 +428,10 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.DISCOVER_SERVICES:
|
||||
replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.CONNECT:
|
||||
replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
@@ -397,6 +448,32 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.ADD_LOCAL_SERVICE:
|
||||
replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.REMOVE_LOCAL_SERVICE:
|
||||
replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.CLEAR_LOCAL_SERVICES:
|
||||
replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.ADD_SERVICE_REQUEST:
|
||||
replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.REMOVE_SERVICE_REQUEST:
|
||||
replyToMessage(message,
|
||||
WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
|
||||
replyToMessage(message,
|
||||
WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
|
||||
WifiP2pManager.P2P_UNSUPPORTED);
|
||||
break;
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
@@ -507,6 +584,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
transitionTo(mP2pDisablingState);
|
||||
break;
|
||||
case WifiP2pManager.DISCOVER_PEERS:
|
||||
// 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);
|
||||
@@ -526,6 +605,20 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
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;
|
||||
@@ -536,7 +629,55 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
device = (WifiP2pDevice) message.obj;
|
||||
if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
|
||||
break;
|
||||
default:
|
||||
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 {
|
||||
replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
return HANDLED;
|
||||
@@ -1286,6 +1427,12 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
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) {
|
||||
@@ -1342,5 +1489,262 @@ public class WifiP2pService extends IWifiP2pManager.Stub {
|
||||
Slog.e(TAG, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
key = c.mReqList.keyAt(i);
|
||||
req = c.mReqList.get(key);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
clientInfo.mReqList.remove(req.getTransactionId());
|
||||
|
||||
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 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 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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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");
|
||||
clearClientInfo(c.mMessenger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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 Bonjour service information.
|
||||
* @hide
|
||||
*/
|
||||
public class WifiP2pBonjourServiceInfo 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 WifiP2pBonjourServiceInfo(List<String> queryList) {
|
||||
super(queryList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Bonjour service information object.
|
||||
*
|
||||
* @param instanceName instance name.<br>
|
||||
* e.g) "MyPrinter"
|
||||
* @param registrationType registration type.<br>
|
||||
* e.g) "_ipp._tcp.local."
|
||||
* @param txtRecord text record.
|
||||
* @return Bonjour service information object
|
||||
*/
|
||||
public static WifiP2pBonjourServiceInfo newInstance(String instanceName,
|
||||
String registrationType, DnsSdTxtRecord txtRecord) {
|
||||
if (TextUtils.isEmpty(instanceName) || TextUtils.isEmpty(registrationType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"instance name or registration type cannot be empty");
|
||||
}
|
||||
|
||||
if (txtRecord == null) {
|
||||
txtRecord = new DnsSdTxtRecord();
|
||||
}
|
||||
|
||||
ArrayList<String> queries = new ArrayList<String>();
|
||||
queries.add(createPtrServiceQuery(instanceName, registrationType));
|
||||
queries.add(createTxtServiceQuery(instanceName, registrationType, txtRecord));
|
||||
|
||||
return new WifiP2pBonjourServiceInfo(queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create wpa_supplicant service query for PTR record.
|
||||
*
|
||||
* @param instanceName instance name.<br>
|
||||
* e.g) "MyPrinter"
|
||||
* @param registrationType registration type.<br>
|
||||
* e.g) "_ipp._tcp.local."
|
||||
* @return wpa_supplicant service query.
|
||||
*/
|
||||
private static String createPtrServiceQuery(String instanceName,
|
||||
String registrationType) {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("bonjour ");
|
||||
sb.append(createRequest(registrationType, 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 registrationType registration type.<br>
|
||||
* e.g) "_ipp._tcp.local."
|
||||
* @param txtRecord TXT record.<br>
|
||||
* @return wpa_supplicant service query.
|
||||
*/
|
||||
public static String createTxtServiceQuery(String instanceName,
|
||||
String registrationType,
|
||||
DnsSdTxtRecord txtRecord) {
|
||||
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("bonjour ");
|
||||
|
||||
sb.append(createRequest((instanceName + "." + registrationType),
|
||||
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 == WifiP2pBonjourServiceInfo.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/**
|
||||
* A class for a request of bonjour service discovery.
|
||||
* @hide
|
||||
*/
|
||||
public class WifiP2pBonjourServiceRequest extends WifiP2pServiceRequest {
|
||||
|
||||
/**
|
||||
* This constructor is only used in newInstance().
|
||||
*
|
||||
* @param query The part of service specific query.
|
||||
* @hide
|
||||
*/
|
||||
private WifiP2pBonjourServiceRequest(String query) {
|
||||
super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is only used in newInstance().
|
||||
* @hide
|
||||
*/
|
||||
private WifiP2pBonjourServiceRequest() {
|
||||
super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, null);
|
||||
}
|
||||
|
||||
private WifiP2pBonjourServiceRequest(String registrationType, int dnsType, int version) {
|
||||
super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR, WifiP2pBonjourServiceInfo.createRequest(
|
||||
registrationType,
|
||||
WifiP2pBonjourServiceInfo.DNS_TYPE_PTR,
|
||||
WifiP2pBonjourServiceInfo.VERSION_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a service discovery request to search all Bonjour services.
|
||||
*
|
||||
* @return service request for Bonjour.
|
||||
*/
|
||||
public static WifiP2pBonjourServiceRequest newInstance() {
|
||||
return new WifiP2pBonjourServiceRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a service discovery request to resolve the instance name with the specified
|
||||
* registration type.
|
||||
*
|
||||
* @param registrationType registration type. Cannot be null <br>
|
||||
* e.g) <br>
|
||||
* "_afpovertcp._tcp.local."(Apple File Sharing over TCP)<br>
|
||||
* "_ipp._tcp.local." (IP Printing over TCP)<br>
|
||||
* @return service request for Bonjour.
|
||||
*/
|
||||
public static WifiP2pBonjourServiceRequest newInstance(String registrationType) {
|
||||
if (registrationType == null) {
|
||||
throw new IllegalArgumentException("registration type cannot be null");
|
||||
}
|
||||
return new WifiP2pBonjourServiceRequest(registrationType,
|
||||
WifiP2pBonjourServiceInfo.DNS_TYPE_PTR,
|
||||
WifiP2pBonjourServiceInfo.VERSION_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a service discovery request to get the TXT data from the specified
|
||||
* service.
|
||||
*
|
||||
* @param instanceName instance name. Cannot be null. <br>
|
||||
* "MyPrinter"
|
||||
* @param registrationType registration type. Cannot be null. <br>
|
||||
* e.g) <br>
|
||||
* "_afpovertcp._tcp.local."(Apple File Sharing over TCP)<br>
|
||||
* "_ipp._tcp.local." (IP Printing over TCP)<br>
|
||||
* @return service request for Bonjour.
|
||||
*/
|
||||
public static WifiP2pBonjourServiceRequest newInstance(String instanceName,
|
||||
String registrationType) {
|
||||
if (instanceName == null || registrationType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"instance name or registration type cannot be null");
|
||||
}
|
||||
String fullDomainName = instanceName + "." + registrationType;
|
||||
return new WifiP2pBonjourServiceRequest(fullDomainName,
|
||||
WifiP2pBonjourServiceInfo.DNS_TYPE_TXT,
|
||||
WifiP2pBonjourServiceInfo.VERSION_1);
|
||||
}
|
||||
}
|
||||
@@ -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 WifiP2pBonjourServiceResponse 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 WifiP2pBonjourServiceInfo#DNS_TYPE_PTR}.
|
||||
*/
|
||||
private String mInstanceName;
|
||||
|
||||
/**
|
||||
* DNS Type.
|
||||
* Should be {@link WifiP2pBonjourServiceInfo#DNS_TYPE_PTR} or
|
||||
* {@link WifiP2pBonjourServiceInfo#DNS_TYPE_TXT}.
|
||||
*/
|
||||
private int mDnsType;
|
||||
|
||||
/**
|
||||
* Bonjour version number.
|
||||
* Should be {@link WifiP2pBonjourServiceInfo#VERSION_1}.
|
||||
*/
|
||||
private int mVersion;
|
||||
|
||||
/**
|
||||
* Txt record.
|
||||
* This field is only used when the dns type equals to
|
||||
* {@link WifiP2pBonjourServiceInfo#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:Bonjour(").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 WifiP2pBonjourServiceResponse(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 Bonjour 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 == WifiP2pBonjourServiceInfo.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 == WifiP2pBonjourServiceInfo.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 Bonjour service response.
|
||||
* This is only called from WifiP2pServiceResponse
|
||||
*
|
||||
* @param status status code.
|
||||
* @param dev source device.
|
||||
* @param data Bonjour response data.
|
||||
* @return Bonjour service response data.
|
||||
* @hide
|
||||
*/
|
||||
static WifiP2pBonjourServiceResponse newInstance(int status,
|
||||
int transId, WifiP2pDevice dev, byte[] data) {
|
||||
if (status != WifiP2pServiceResponse.Status.SUCCESS) {
|
||||
return new WifiP2pBonjourServiceResponse(status,
|
||||
transId, dev, null);
|
||||
}
|
||||
try {
|
||||
return new WifiP2pBonjourServiceResponse(status,
|
||||
transId, dev, data);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
19
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl
Normal file
19
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl
Normal file
@@ -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;
|
||||
183
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
Normal file
183
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The class for service information.
|
||||
*
|
||||
* <p>Currently UPnP and Bonjour are only supported.
|
||||
*
|
||||
* @see WifiP2pUpnpServiceInfo
|
||||
* @see WifiP2pBonjourServiceInfo
|
||||
* @hide
|
||||
*/
|
||||
public class WifiP2pServiceInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* All service protocol types.
|
||||
*/
|
||||
public static final int SERVICE_TYPE_ALL = 0;
|
||||
|
||||
/**
|
||||
* Bonjour protocol.
|
||||
*/
|
||||
public static final int SERVICE_TYPE_BONJOUR = 1;
|
||||
|
||||
/**
|
||||
* UPnP protocol.
|
||||
*/
|
||||
public static final int SERVICE_TYPE_UPNP = 2;
|
||||
|
||||
/**
|
||||
* WS-Discovery protocol
|
||||
*/
|
||||
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];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
283
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
Normal file
283
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 a request of service discovery.
|
||||
*
|
||||
* <p>This class is used when you create customized service discovery request.
|
||||
* e.g) vendor specific request/ws discovery etc.
|
||||
*
|
||||
* <p>If you want to create UPnP or Bonjour service request, then you had better
|
||||
* use {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pBonjourServiceRequest}.
|
||||
*
|
||||
* @see WifiP2pUpnpServiceRequest
|
||||
* @see WifiP2pBonjourServiceRequest
|
||||
* @hide
|
||||
*/
|
||||
public class WifiP2pServiceRequest implements Parcelable {
|
||||
|
||||
/**
|
||||
* Service type. It's defined in table63 in Wi-Fi Direct specification.
|
||||
*/
|
||||
private int mServiceType;
|
||||
|
||||
/**
|
||||
* 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) Bonjour apple file sharing over tcp (dns name=_afpovertcp._tcp.local.)
|
||||
* 0b5f6166706f766572746370c00c000c01
|
||||
*/
|
||||
private String mQuery;
|
||||
|
||||
/**
|
||||
* This constructor is only used in newInstance().
|
||||
*
|
||||
* @param serviceType service discovery type.
|
||||
* @param query The part of service specific query.
|
||||
* @hide
|
||||
*/
|
||||
protected WifiP2pServiceRequest(int serviceType, String query) {
|
||||
validateQuery(query);
|
||||
|
||||
mServiceType = serviceType;
|
||||
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) {
|
||||
mServiceType = 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", mServiceType));
|
||||
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 service discovery request.
|
||||
*
|
||||
* <p>The created instance is set to framework by
|
||||
* {@link WifiP2pManager#addLocalService}.
|
||||
*
|
||||
* @param serviceType service type.<br>
|
||||
* e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL},
|
||||
* {@link WifiP2pServiceInfo#SERVICE_TYPE_WS_DISCOVERY},
|
||||
* {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
|
||||
* If you want to use UPnP or Bonjour, you create the request by
|
||||
* {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pBonjourServiceRequest}
|
||||
*
|
||||
* @param query hex string. if null, all specified services are requested.
|
||||
* @return service discovery request.
|
||||
*/
|
||||
public static WifiP2pServiceRequest newInstance(int serviceType, String query) {
|
||||
return new WifiP2pServiceRequest(serviceType, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all service discovery request.
|
||||
*
|
||||
* <p>The created instance is set to framework by
|
||||
* {@link WifiP2pManager#addLocalService}.
|
||||
*
|
||||
* @param serviceType service type.<br>
|
||||
* e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL},
|
||||
* {@link WifiP2pServiceInfo#SERVICE_TYPE_WS_DISCOVERY},
|
||||
* {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
|
||||
* If you want to use UPnP or Bonjour, you create the request by
|
||||
* {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pBonjourServiceRequest}
|
||||
*
|
||||
* @return service discovery request.
|
||||
*/
|
||||
public static WifiP2pServiceRequest newInstance(int serviceType) {
|
||||
return new WifiP2pServiceRequest(serviceType, 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.mServiceType != mServiceType) ||
|
||||
(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 + mServiceType;
|
||||
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(mServiceType);
|
||||
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];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
389
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
Normal file
389
wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
Normal file
@@ -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 <address> <update indicator> <response data>
|
||||
* 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 = WifiP2pBonjourServiceResponse.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 WifiP2pBonjourServiceResponse.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];
|
||||
}
|
||||
};
|
||||
}
|
||||
105
wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
Normal file
105
wifi/java/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceInfo.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The class for UPnP service information.
|
||||
* @hide
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The class for a request of upnp service discovery.
|
||||
* @hide
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user