1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi.p2p.nsd;
18 
19 import android.net.wifi.p2p.WifiP2pDevice;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.DataInputStream;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 
30 /**
31  * The class for a response of service discovery.
32  *
33  * @hide
34  */
35 public class WifiP2pServiceResponse implements Parcelable {
36 
37     private static int MAX_BUF_SIZE = 1024;
38 
39     /**
40      * Service type. It's defined in table63 in Wi-Fi Direct specification.
41      */
42     protected int mServiceType;
43 
44     /**
45      * Status code of service discovery response.
46      * It's defined in table65 in Wi-Fi Direct specification.
47      * @see Status
48      */
49     protected int mStatus;
50 
51     /**
52      * Service transaction ID.
53      * This is a nonzero value used to match the service request/response TLVs.
54      */
55     protected int mTransId;
56 
57     /**
58      * Source device.
59      */
60     protected WifiP2pDevice mDevice;
61 
62     /**
63      * Service discovery response data based on the requested on
64      * the service protocol type. The protocol format depends on the service type.
65      */
66     protected byte[] mData;
67 
68 
69     /**
70      * The status code of service discovery response.
71      * Currently 4 status codes are defined and the status codes from  4 to 255
72      * are reserved.
73      *
74      * See Wi-Fi Direct specification for the detail.
75      */
76     public static class Status {
77         /** success */
78         public static final int SUCCESS = 0;
79 
80         /** the service protocol type is not available */
81         public static final int SERVICE_PROTOCOL_NOT_AVAILABLE = 1;
82 
83         /** the requested information is not available */
84         public static final int REQUESTED_INFORMATION_NOT_AVAILABLE = 2;
85 
86         /** bad request */
87         public static final int BAD_REQUEST = 3;
88 
89         /** @hide */
toString(int status)90         public static String toString(int status) {
91             switch(status) {
92             case SUCCESS:
93                 return "SUCCESS";
94             case SERVICE_PROTOCOL_NOT_AVAILABLE:
95                 return "SERVICE_PROTOCOL_NOT_AVAILABLE";
96             case REQUESTED_INFORMATION_NOT_AVAILABLE:
97                 return "REQUESTED_INFORMATION_NOT_AVAILABLE";
98             case BAD_REQUEST:
99                 return "BAD_REQUEST";
100             default:
101                 return "UNKNOWN";
102             }
103         }
104 
105         /** not used */
Status()106         private Status() {}
107     }
108 
109     /**
110      * Hidden constructor. This is only used in framework.
111      *
112      * @param serviceType service discovery type.
113      * @param status status code.
114      * @param transId transaction id.
115      * @param device source device.
116      * @param data query data.
117      */
WifiP2pServiceResponse(int serviceType, int status, int transId, WifiP2pDevice device, byte[] data)118     protected WifiP2pServiceResponse(int serviceType, int status, int transId,
119             WifiP2pDevice device, byte[] data) {
120         mServiceType = serviceType;
121         mStatus = status;
122         mTransId = transId;
123         mDevice = device;
124         mData = data;
125     }
126 
127     /**
128      * Return the service type of service discovery response.
129      *
130      * @return service discovery type.<br>
131      * e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_BONJOUR}
132      */
getServiceType()133     public int getServiceType() {
134         return mServiceType;
135     }
136 
137     /**
138      * Return the status code of service discovery response.
139      *
140      * @return status code.
141      * @see Status
142      */
getStatus()143     public int getStatus() {
144         return mStatus;
145     }
146 
147     /**
148      * Return the transaction id of service discovery response.
149      *
150      * @return transaction id.
151      * @hide
152      */
getTransactionId()153     public int getTransactionId() {
154         return mTransId;
155     }
156 
157     /**
158      * Return response data.
159      *
160      * <pre>Data format depends on service type
161      *
162      * @return a query or response data.
163      */
getRawData()164     public byte[] getRawData() {
165         return mData;
166     }
167 
168     /**
169      * Returns the source device of service discovery response.
170      *
171      * <pre>This is valid only when service discovery response.
172      *
173      * @return the source device of service discovery response.
174      */
getSrcDevice()175     public WifiP2pDevice getSrcDevice() {
176         return mDevice;
177     }
178 
179     /** @hide */
setSrcDevice(WifiP2pDevice dev)180     public void setSrcDevice(WifiP2pDevice dev) {
181         if (dev == null) return;
182         this.mDevice = dev;
183     }
184 
185 
186     /**
187      * Create the list of  WifiP2pServiceResponse instance from supplicant event.
188      *
189      * @param srcAddr source address of the service response
190      * @param tlvsBin byte array containing the binary tlvs data
191      * @return if parse failed, return null
192      * @hide
193      */
newInstance(String srcAddr, byte[] tlvsBin)194     public static List<WifiP2pServiceResponse> newInstance(String srcAddr, byte[] tlvsBin) {
195         //updateIndicator not used, and not passed up from supplicant
196 
197         List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>();
198         WifiP2pDevice dev = new WifiP2pDevice();
199         dev.deviceAddress = srcAddr;
200         if (tlvsBin == null) {
201             return null;
202         }
203 
204 
205         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(tlvsBin));
206         try {
207             while (dis.available() > 0) {
208                 /*
209                  * Service discovery header is as follows.
210                  * ______________________________________________________________
211                  * |           Length(2byte)     | Type(1byte) | TransId(1byte)}|
212                  * ______________________________________________________________
213                  * | status(1byte)  |            vendor specific(variable)      |
214                  */
215                 // The length equals to 3 plus the number of octets in the vendor
216                 // specific content field. And this is little endian.
217                 int length = (dis.readUnsignedByte() +
218                         (dis.readUnsignedByte() << 8)) - 3;
219                 int type = dis.readUnsignedByte();
220                 int transId = dis.readUnsignedByte();
221                 int status = dis.readUnsignedByte();
222                 if (length < 0) {
223                     return null;
224                 }
225                 if (length == 0) {
226                     if (status == Status.SUCCESS) {
227                         respList.add(new WifiP2pServiceResponse(type, status,
228                             transId, dev, null));
229                     }
230                     continue;
231                 }
232                 if (length > MAX_BUF_SIZE) {
233                     dis.skip(length);
234                     continue;
235                 }
236                 byte[] data = new byte[length];
237                 dis.readFully(data);
238 
239                 WifiP2pServiceResponse resp;
240                 if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
241                     resp = WifiP2pDnsSdServiceResponse.newInstance(status,
242                             transId, dev, data);
243                 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
244                     resp = WifiP2pUpnpServiceResponse.newInstance(status,
245                             transId, dev, data);
246                 } else {
247                     resp = new WifiP2pServiceResponse(type, status, transId, dev, data);
248                 }
249                 if (resp != null && resp.getStatus() == Status.SUCCESS) {
250                     respList.add(resp);
251                 }
252             }
253             return respList;
254         } catch (IOException e) {
255             e.printStackTrace();
256         }
257 
258         if (respList.size() > 0) {
259             return respList;
260         }
261         return null;
262     }
263 
264     /**
265      * Converts hex string to byte array.
266      *
267      * @param hex hex string. if invalid, return null.
268      * @return binary data.
269      */
hexStr2Bin(String hex)270     private static byte[] hexStr2Bin(String hex) {
271         int sz = hex.length()/2;
272         byte[] b = new byte[hex.length()/2];
273 
274         for (int i=0;i<sz;i++) {
275             try {
276                 b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16);
277             } catch (Exception e) {
278                 e.printStackTrace();
279                 return null;
280             }
281         }
282         return b;
283     }
284 
285     @Override
toString()286     public String toString() {
287         StringBuffer sbuf = new StringBuffer();
288         sbuf.append("serviceType:").append(mServiceType);
289         sbuf.append(" status:").append(Status.toString(mStatus));
290         sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
291         sbuf.append(" data:").append(Arrays.toString(mData));
292         return sbuf.toString();
293     }
294 
295     @Override
equals(Object o)296     public boolean equals(Object o) {
297         if (o == this) {
298             return true;
299         }
300         if (!(o instanceof WifiP2pServiceResponse)) {
301             return false;
302         }
303 
304         WifiP2pServiceResponse req = (WifiP2pServiceResponse)o;
305 
306         return (req.mServiceType == mServiceType) &&
307             (req.mStatus == mStatus) &&
308                 equals(req.mDevice.deviceAddress, mDevice.deviceAddress) &&
309                 Arrays.equals(req.mData, mData);
310     }
311 
equals(Object a, Object b)312     private boolean equals(Object a, Object b) {
313         if (a == null && b == null) {
314             return true;
315         } else if (a != null) {
316             return a.equals(b);
317         }
318         return false;
319     }
320 
321     @Override
hashCode()322     public int hashCode() {
323         int result = 17;
324         result = 31 * result + mServiceType;
325         result = 31 * result + mStatus;
326         result = 31 * result + mTransId;
327         result = 31 * result + (mDevice.deviceAddress == null ?
328                 0 : mDevice.deviceAddress.hashCode());
329         result = 31 * result + (mData == null ? 0 : Arrays.hashCode(mData));
330         return result;
331     }
332 
333     /** Implement the Parcelable interface {@hide} */
describeContents()334     public int describeContents() {
335         return 0;
336     }
337 
338     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)339     public void writeToParcel(Parcel dest, int flags) {
340         dest.writeInt(mServiceType);
341         dest.writeInt(mStatus);
342         dest.writeInt(mTransId);
343         dest.writeParcelable(mDevice, flags);
344         if (mData == null || mData.length == 0) {
345             dest.writeInt(0);
346         } else {
347             dest.writeInt(mData.length);
348             dest.writeByteArray(mData);
349         }
350     }
351 
352     /** Implement the Parcelable interface {@hide} */
353     public static final @android.annotation.NonNull Creator<WifiP2pServiceResponse> CREATOR =
354         new Creator<WifiP2pServiceResponse>() {
355             public WifiP2pServiceResponse createFromParcel(Parcel in) {
356 
357                 int type = in.readInt();
358                 int status = in.readInt();
359                 int transId = in.readInt();
360                 WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null);
361                 int len = in.readInt();
362                 byte[] data = null;
363                 if (len > 0) {
364                     data = new byte[len];
365                     in.readByteArray(data);
366                 }
367                 if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
368                     return WifiP2pDnsSdServiceResponse.newInstance(status,
369                             transId, dev, data);
370                 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
371                     return WifiP2pUpnpServiceResponse.newInstance(status,
372                             transId, dev, data);
373                 }
374                 return new WifiP2pServiceResponse(type, status, transId, dev, data);
375             }
376 
377             public WifiP2pServiceResponse[] newArray(int size) {
378                 return new WifiP2pServiceResponse[size];
379             }
380         };
381 }
382