/* * 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.
* 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. * *
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.
     *
     * 
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.
     *
     * 
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 newInstance(String supplicantEvent) {

        List respList = new ArrayList();
        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.readUnsignedByte() +
                        (dis.readUnsignedByte() << 8)) - 3;
                int type = dis.readUnsignedByte();
                int transId = dis.readUnsignedByte();
                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 CREATOR =
        new Creator() {
            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];
            }
        };
}