Merge "Pre-association service discovery support"

This commit is contained in:
Irfan Sheriff
2012-04-12 18:33:05 -07:00
committed by Android (Google) Code Review
19 changed files with 2932 additions and 38 deletions

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}

View File

@@ -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>();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View 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;

View 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];
}
};
}

View 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 WifiP2pServiceRequest;

View 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];
}
};
}

View 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 WifiP2pServiceResponse;

View 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 &lt;address&gt; &lt;update indicator&gt; &lt;response data&gt;
* e.g) P2P-SERV-DISC-RESP 02:03:7f:11:62:da 1 0300000101
*
* @param supplicantEvent wpa_supplicant event string.
* @return if parse failed, return null
* @hide
*/
public static List<WifiP2pServiceResponse> newInstance(String supplicantEvent) {
List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>();
String[] args = supplicantEvent.split(" ");
if (args.length != 4) {
return null;
}
WifiP2pDevice dev = new WifiP2pDevice();
String srcAddr = args[1];
dev.deviceAddress = srcAddr;
//String updateIndicator = args[2];//not used.
byte[] bin = hexStr2Bin(args[3]);
if (bin == null) {
return null;
}
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bin));
try {
while (dis.available() > 0) {
/*
* Service discovery header is as follows.
* ______________________________________________________________
* | Length(2byte) | Type(1byte) | TransId(1byte)}|
* ______________________________________________________________
* | status(1byte) | vendor specific(variable) |
*/
// The length equals to 3 plus the number of octets in the vendor
// specific content field. And this is little endian.
int length = ((dis.readByte() & 0xff) +
((dis.readByte() & 0xff) << 8)) - 3;
int type = dis.readUnsignedByte();
byte transId = dis.readByte();
int status = dis.readUnsignedByte();
if (length < 0) {
return null;
}
if (length == 0) {
if (status == Status.SUCCESS) {
respList.add(new WifiP2pServiceResponse(type, status,
transId, dev, null));
}
continue;
}
if (length > MAX_BUF_SIZE) {
dis.skip(length);
continue;
}
byte[] data = new byte[length];
dis.readFully(data);
WifiP2pServiceResponse resp;
if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
resp = 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];
}
};
}

View 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();
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}