| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.wifi.p2p.nsd; |
| |
| import android.net.wifi.p2p.WifiP2pDevice; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.DataInputStream; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * The class for a response of service discovery. |
| * |
| * @hide |
| */ |
| public class WifiP2pServiceResponse implements Parcelable { |
| |
| private static int MAX_BUF_SIZE = 1024; |
| |
| /** |
| * Service type. It's defined in table63 in Wi-Fi Direct specification. |
| */ |
| protected int mServiceType; |
| |
| /** |
| * Status code of service discovery response. |
| * It's defined in table65 in Wi-Fi Direct specification. |
| * @see Status |
| */ |
| protected int mStatus; |
| |
| /** |
| * Service transaction ID. |
| * This is a nonzero value used to match the service request/response TLVs. |
| */ |
| protected int mTransId; |
| |
| /** |
| * Source device. |
| */ |
| protected WifiP2pDevice mDevice; |
| |
| /** |
| * Service discovery response data based on the requested on |
| * the service protocol type. The protocol format depends on the service type. |
| */ |
| protected byte[] mData; |
| |
| |
| /** |
| * The status code of service discovery response. |
| * Currently 4 status codes are defined and the status codes from 4 to 255 |
| * are reserved. |
| * |
| * See Wi-Fi Direct specification for the detail. |
| */ |
| public static class Status { |
| /** success */ |
| public static final int SUCCESS = 0; |
| |
| /** the service protocol type is not available */ |
| public static final int SERVICE_PROTOCOL_NOT_AVAILABLE = 1; |
| |
| /** the requested information is not available */ |
| public static final int REQUESTED_INFORMATION_NOT_AVAILABLE = 2; |
| |
| /** bad request */ |
| public static final int BAD_REQUEST = 3; |
| |
| /** @hide */ |
| public static String toString(int status) { |
| switch(status) { |
| case SUCCESS: |
| return "SUCCESS"; |
| case SERVICE_PROTOCOL_NOT_AVAILABLE: |
| return "SERVICE_PROTOCOL_NOT_AVAILABLE"; |
| case REQUESTED_INFORMATION_NOT_AVAILABLE: |
| return "REQUESTED_INFORMATION_NOT_AVAILABLE"; |
| case BAD_REQUEST: |
| return "BAD_REQUEST"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| /** not used */ |
| private Status() {} |
| } |
| |
| /** |
| * Hidden constructor. This is only used in framework. |
| * |
| * @param serviceType service discovery type. |
| * @param status status code. |
| * @param transId transaction id. |
| * @param device source device. |
| * @param data query data. |
| */ |
| protected WifiP2pServiceResponse(int serviceType, int status, int transId, |
| WifiP2pDevice device, byte[] data) { |
| mServiceType = serviceType; |
| mStatus = status; |
| mTransId = transId; |
| mDevice = device; |
| mData = data; |
| } |
| |
| /** |
| * Return the service type of service discovery response. |
| * |
| * @return service discovery type.<br> |
| * e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_BONJOUR} |
| */ |
| public int getServiceType() { |
| return mServiceType; |
| } |
| |
| /** |
| * Return the status code of service discovery response. |
| * |
| * @return status code. |
| * @see Status |
| */ |
| public int getStatus() { |
| return mStatus; |
| } |
| |
| /** |
| * Return the transaction id of service discovery response. |
| * |
| * @return transaction id. |
| * @hide |
| */ |
| public int getTransactionId() { |
| return mTransId; |
| } |
| |
| /** |
| * Return response data. |
| * |
| * <pre>Data format depends on service type |
| * |
| * @return a query or response data. |
| */ |
| public byte[] getRawData() { |
| return mData; |
| } |
| |
| /** |
| * Returns the source device of service discovery response. |
| * |
| * <pre>This is valid only when service discovery response. |
| * |
| * @return the source device of service discovery response. |
| */ |
| public WifiP2pDevice getSrcDevice() { |
| return mDevice; |
| } |
| |
| /** @hide */ |
| public void setSrcDevice(WifiP2pDevice dev) { |
| if (dev == null) return; |
| this.mDevice = dev; |
| } |
| |
| |
| /** |
| * Create the list of WifiP2pServiceResponse instance from supplicant event. |
| * |
| * <pre>The format is as follows. |
| * P2P-SERV-DISC-RESP <address> <update indicator> <response data> |
| * e.g) P2P-SERV-DISC-RESP 02:03:7f:11:62:da 1 0300000101 |
| * |
| * @param supplicantEvent wpa_supplicant event string. |
| * @return if parse failed, return null |
| * @hide |
| */ |
| public static List<WifiP2pServiceResponse> newInstance(String supplicantEvent) { |
| |
| List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>(); |
| String[] args = supplicantEvent.split(" "); |
| if (args.length != 4) { |
| return null; |
| } |
| WifiP2pDevice dev = new WifiP2pDevice(); |
| String srcAddr = args[1]; |
| dev.deviceAddress = srcAddr; |
| //String updateIndicator = args[2];//not used. |
| byte[] bin = hexStr2Bin(args[3]); |
| if (bin == null) { |
| return null; |
| } |
| |
| DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bin)); |
| try { |
| while (dis.available() > 0) { |
| /* |
| * Service discovery header is as follows. |
| * ______________________________________________________________ |
| * | Length(2byte) | Type(1byte) | TransId(1byte)}| |
| * ______________________________________________________________ |
| * | status(1byte) | vendor specific(variable) | |
| */ |
| // The length equals to 3 plus the number of octets in the vendor |
| // specific content field. And this is little endian. |
| int length = ((dis.readByte() & 0xff) + |
| ((dis.readByte() & 0xff) << 8)) - 3; |
| int type = dis.readUnsignedByte(); |
| byte transId = dis.readByte(); |
| int status = dis.readUnsignedByte(); |
| if (length < 0) { |
| return null; |
| } |
| if (length == 0) { |
| if (status == Status.SUCCESS) { |
| respList.add(new WifiP2pServiceResponse(type, status, |
| transId, dev, null)); |
| } |
| continue; |
| } |
| if (length > MAX_BUF_SIZE) { |
| dis.skip(length); |
| continue; |
| } |
| byte[] data = new byte[length]; |
| dis.readFully(data); |
| |
| WifiP2pServiceResponse resp; |
| if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { |
| resp = WifiP2pDnsSdServiceResponse.newInstance(status, |
| transId, dev, data); |
| } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { |
| resp = WifiP2pUpnpServiceResponse.newInstance(status, |
| transId, dev, data); |
| } else { |
| resp = new WifiP2pServiceResponse(type, status, transId, dev, data); |
| } |
| if (resp != null && resp.getStatus() == Status.SUCCESS) { |
| respList.add(resp); |
| } |
| } |
| return respList; |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| if (respList.size() > 0) { |
| return respList; |
| } |
| return null; |
| } |
| |
| /** |
| * Converts hex string to byte array. |
| * |
| * @param hex hex string. if invalid, return null. |
| * @return binary data. |
| */ |
| private static byte[] hexStr2Bin(String hex) { |
| int sz = hex.length()/2; |
| byte[] b = new byte[hex.length()/2]; |
| |
| for (int i=0;i<sz;i++) { |
| try { |
| b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| return null; |
| } |
| } |
| return b; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuffer sbuf = new StringBuffer(); |
| sbuf.append("serviceType:").append(mServiceType); |
| sbuf.append(" status:").append(Status.toString(mStatus)); |
| sbuf.append(" srcAddr:").append(mDevice.deviceAddress); |
| sbuf.append(" data:").append(mData); |
| return sbuf.toString(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof WifiP2pServiceResponse)) { |
| return false; |
| } |
| |
| WifiP2pServiceResponse req = (WifiP2pServiceResponse)o; |
| |
| return (req.mServiceType == mServiceType) && |
| (req.mStatus == mStatus) && |
| equals(req.mDevice.deviceAddress, mDevice.deviceAddress) && |
| Arrays.equals(req.mData, mData); |
| } |
| |
| private boolean equals(Object a, Object b) { |
| if (a == null && b == null) { |
| return true; |
| } else if (a != null) { |
| return a.equals(b); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = 17; |
| result = 31 * result + mServiceType; |
| result = 31 * result + mStatus; |
| result = 31 * result + mTransId; |
| result = 31 * result + (mDevice.deviceAddress == null ? |
| 0 : mDevice.deviceAddress.hashCode()); |
| result = 31 * result + (mData == null ? 0 : mData.hashCode()); |
| return result; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(mServiceType); |
| dest.writeInt(mStatus); |
| dest.writeInt(mTransId); |
| dest.writeParcelable(mDevice, flags); |
| if (mData == null || mData.length == 0) { |
| dest.writeInt(0); |
| } else { |
| dest.writeInt(mData.length); |
| dest.writeByteArray(mData); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final Creator<WifiP2pServiceResponse> CREATOR = |
| new Creator<WifiP2pServiceResponse>() { |
| public WifiP2pServiceResponse createFromParcel(Parcel in) { |
| |
| int type = in.readInt(); |
| int status = in.readInt(); |
| int transId = in.readInt(); |
| WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null); |
| int len = in.readInt(); |
| byte[] data = null; |
| if (len > 0) { |
| data = new byte[len]; |
| in.readByteArray(data); |
| } |
| if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { |
| return WifiP2pDnsSdServiceResponse.newInstance(status, |
| transId, dev, data); |
| } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { |
| return WifiP2pUpnpServiceResponse.newInstance(status, |
| transId, dev, data); |
| } |
| return new WifiP2pServiceResponse(type, status, transId, dev, data); |
| } |
| |
| public WifiP2pServiceResponse[] newArray(int size) { |
| return new WifiP2pServiceResponse[size]; |
| } |
| }; |
| } |