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      * <pre>The format is as follows.
190      * P2P-SERV-DISC-RESP &lt;address&gt; &lt;update indicator&gt; &lt;response data&gt;
191      * e.g) P2P-SERV-DISC-RESP 02:03:7f:11:62:da 1 0300000101
192      *
193      * @param supplicantEvent wpa_supplicant event string.
194      * @return if parse failed, return null
195      * @hide
196      */
newInstance(String supplicantEvent)197     public static List<WifiP2pServiceResponse> newInstance(String supplicantEvent) {
198 
199         List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>();
200         String[] args = supplicantEvent.split(" ");
201         if (args.length != 4) {
202             return null;
203         }
204         WifiP2pDevice dev = new WifiP2pDevice();
205         String srcAddr = args[1];
206         dev.deviceAddress = srcAddr;
207         //String updateIndicator = args[2];//not used.
208         byte[] bin = hexStr2Bin(args[3]);
209         if (bin == null) {
210             return null;
211         }
212 
213         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bin));
214         try {
215             while (dis.available() > 0) {
216                 /*
217                  * Service discovery header is as follows.
218                  * ______________________________________________________________
219                  * |           Length(2byte)     | Type(1byte) | TransId(1byte)}|
220                  * ______________________________________________________________
221                  * | status(1byte)  |            vendor specific(variable)      |
222                  */
223                 // The length equals to 3 plus the number of octets in the vendor
224                 // specific content field. And this is little endian.
225                 int length = (dis.readUnsignedByte() +
226                         (dis.readUnsignedByte() << 8)) - 3;
227                 int type = dis.readUnsignedByte();
228                 int transId = dis.readUnsignedByte();
229                 int status = dis.readUnsignedByte();
230                 if (length < 0) {
231                     return null;
232                 }
233                 if (length == 0) {
234                     if (status == Status.SUCCESS) {
235                         respList.add(new WifiP2pServiceResponse(type, status,
236                             transId, dev, null));
237                     }
238                     continue;
239                 }
240                 if (length > MAX_BUF_SIZE) {
241                     dis.skip(length);
242                     continue;
243                 }
244                 byte[] data = new byte[length];
245                 dis.readFully(data);
246 
247                 WifiP2pServiceResponse resp;
248                 if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
249                     resp = WifiP2pDnsSdServiceResponse.newInstance(status,
250                             transId, dev, data);
251                 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
252                     resp = WifiP2pUpnpServiceResponse.newInstance(status,
253                             transId, dev, data);
254                 } else {
255                     resp = new WifiP2pServiceResponse(type, status, transId, dev, data);
256                 }
257                 if (resp != null && resp.getStatus() == Status.SUCCESS) {
258                     respList.add(resp);
259                 }
260             }
261             return respList;
262         } catch (IOException e) {
263             e.printStackTrace();
264         }
265 
266         if (respList.size() > 0) {
267             return respList;
268         }
269         return null;
270     }
271 
272     /**
273      * Converts hex string to byte array.
274      *
275      * @param hex hex string. if invalid, return null.
276      * @return binary data.
277      */
hexStr2Bin(String hex)278     private static byte[] hexStr2Bin(String hex) {
279         int sz = hex.length()/2;
280         byte[] b = new byte[hex.length()/2];
281 
282         for (int i=0;i<sz;i++) {
283             try {
284                 b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16);
285             } catch (Exception e) {
286                 e.printStackTrace();
287                 return null;
288             }
289         }
290         return b;
291     }
292 
293     @Override
toString()294     public String toString() {
295         StringBuffer sbuf = new StringBuffer();
296         sbuf.append("serviceType:").append(mServiceType);
297         sbuf.append(" status:").append(Status.toString(mStatus));
298         sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
299         sbuf.append(" data:").append(Arrays.toString(mData));
300         return sbuf.toString();
301     }
302 
303     @Override
equals(Object o)304     public boolean equals(Object o) {
305         if (o == this) {
306             return true;
307         }
308         if (!(o instanceof WifiP2pServiceResponse)) {
309             return false;
310         }
311 
312         WifiP2pServiceResponse req = (WifiP2pServiceResponse)o;
313 
314         return (req.mServiceType == mServiceType) &&
315             (req.mStatus == mStatus) &&
316                 equals(req.mDevice.deviceAddress, mDevice.deviceAddress) &&
317                 Arrays.equals(req.mData, mData);
318     }
319 
equals(Object a, Object b)320     private boolean equals(Object a, Object b) {
321         if (a == null && b == null) {
322             return true;
323         } else if (a != null) {
324             return a.equals(b);
325         }
326         return false;
327     }
328 
329     @Override
hashCode()330     public int hashCode() {
331         int result = 17;
332         result = 31 * result + mServiceType;
333         result = 31 * result + mStatus;
334         result = 31 * result + mTransId;
335         result = 31 * result + (mDevice.deviceAddress == null ?
336                 0 : mDevice.deviceAddress.hashCode());
337         result = 31 * result + (mData == null ? 0 : Arrays.hashCode(mData));
338         return result;
339     }
340 
341     /** Implement the Parcelable interface {@hide} */
describeContents()342     public int describeContents() {
343         return 0;
344     }
345 
346     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)347     public void writeToParcel(Parcel dest, int flags) {
348         dest.writeInt(mServiceType);
349         dest.writeInt(mStatus);
350         dest.writeInt(mTransId);
351         dest.writeParcelable(mDevice, flags);
352         if (mData == null || mData.length == 0) {
353             dest.writeInt(0);
354         } else {
355             dest.writeInt(mData.length);
356             dest.writeByteArray(mData);
357         }
358     }
359 
360     /** Implement the Parcelable interface {@hide} */
361     public static final Creator<WifiP2pServiceResponse> CREATOR =
362         new Creator<WifiP2pServiceResponse>() {
363             public WifiP2pServiceResponse createFromParcel(Parcel in) {
364 
365                 int type = in.readInt();
366                 int status = in.readInt();
367                 int transId = in.readInt();
368                 WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null);
369                 int len = in.readInt();
370                 byte[] data = null;
371                 if (len > 0) {
372                     data = new byte[len];
373                     in.readByteArray(data);
374                 }
375                 if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
376                     return WifiP2pDnsSdServiceResponse.newInstance(status,
377                             transId, dev, data);
378                 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
379                     return WifiP2pUpnpServiceResponse.newInstance(status,
380                             transId, dev, data);
381                 }
382                 return new WifiP2pServiceResponse(type, status, transId, dev, data);
383             }
384 
385             public WifiP2pServiceResponse[] newArray(int size) {
386                 return new WifiP2pServiceResponse[size];
387             }
388         };
389 }
390