• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.wifi.p2p.WifiP2pDevice;
22 import android.net.wifi.util.Environment;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import com.android.wifi.flags.Flags;
27 
28 import java.io.ByteArrayInputStream;
29 import java.io.DataInputStream;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * The class for a response of service discovery.
38  *
39  * @hide
40  */
41 public class WifiP2pServiceResponse implements Parcelable {
42 
43     private static int MAX_BUF_SIZE = 1024;
44 
45     /**
46      * Service type. It's defined in table63 in Wi-Fi Direct specification.
47      */
48     protected int mServiceType;
49 
50     /**
51      * Status code of service discovery response.
52      * It's defined in table65 in Wi-Fi Direct specification.
53      * @see Status
54      */
55     protected int mStatus;
56 
57     /**
58      * Service transaction ID.
59      * This is a nonzero value used to match the service request/response TLVs.
60      */
61     protected int mTransId;
62 
63     /**
64      * Source device.
65      */
66     protected WifiP2pDevice mDevice;
67 
68     /**
69      * Service discovery response data based on the requested on
70      * the service protocol type. The protocol format depends on the service type.
71      */
72     protected byte[] mData;
73 
74     /**
75      * This field is used only for USD based service discovery response.
76      */
77     private WifiP2pUsdBasedServiceResponse mUsdBasedServiceResponse;
78 
79     /**
80      * Service discovery response requester session ID (Seeker ID) for USD based service discovery.
81      * The session ID is used to match the USD based service discovery request/response frames.
82      * A nonzero ID in the range of 1 to 255 is filled in the Service descriptor attribute (SDA) -
83      * instance ID field of the service discovery request frame (Subscribe frame). The responding
84      * device copies this ID in the Service descriptor attribute (SDA) - requester instance ID
85      * field of the service discovery response frame (Publish frame).
86      *
87      */
88     private int mUsdSessionId;
89 
90 
91     /**
92      * The status code of service discovery response.
93      * Currently 4 status codes are defined and the status codes from  4 to 255
94      * are reserved.
95      *
96      * See Wi-Fi Direct specification for the detail.
97      */
98     public static class Status {
99         /** success */
100         public static final int SUCCESS = 0;
101 
102         /** the service protocol type is not available */
103         public static final int SERVICE_PROTOCOL_NOT_AVAILABLE = 1;
104 
105         /** the requested information is not available */
106         public static final int REQUESTED_INFORMATION_NOT_AVAILABLE = 2;
107 
108         /** bad request */
109         public static final int BAD_REQUEST = 3;
110 
111         /** @hide */
toString(int status)112         public static String toString(int status) {
113             switch(status) {
114             case SUCCESS:
115                 return "SUCCESS";
116             case SERVICE_PROTOCOL_NOT_AVAILABLE:
117                 return "SERVICE_PROTOCOL_NOT_AVAILABLE";
118             case REQUESTED_INFORMATION_NOT_AVAILABLE:
119                 return "REQUESTED_INFORMATION_NOT_AVAILABLE";
120             case BAD_REQUEST:
121                 return "BAD_REQUEST";
122             default:
123                 return "UNKNOWN";
124             }
125         }
126 
127         /** not used */
Status()128         private Status() {}
129     }
130 
131     /**
132      * Hidden constructor. This is only used in framework.
133      *
134      * @param serviceType service discovery type.
135      * @param status status code.
136      * @param transId transaction id.
137      * @param device source device.
138      * @param data query data.
139      */
WifiP2pServiceResponse(int serviceType, int status, int transId, WifiP2pDevice device, byte[] data)140     protected WifiP2pServiceResponse(int serviceType, int status, int transId,
141             WifiP2pDevice device, byte[] data) {
142         mServiceType = serviceType;
143         mStatus = status;
144         mTransId = transId;
145         mDevice = device;
146         mData = data;
147     }
148 
149     /**
150      * Hidden constructor. This is only used in framework.
151      *
152      * @param device source device.
153      * @param usdResponseData USD based service response data.
154      * @param usdSessionId The USD based service discovery request/response session ID.
155      * @hide
156      */
WifiP2pServiceResponse(WifiP2pDevice device, @NonNull WifiP2pUsdBasedServiceResponse usdResponseData, int usdSessionId)157     public WifiP2pServiceResponse(WifiP2pDevice device,
158             @NonNull WifiP2pUsdBasedServiceResponse usdResponseData, int usdSessionId) {
159         mServiceType = 0;
160         mStatus = 0;
161         mTransId = 0;
162         mDevice = device;
163         mData = null;
164         mUsdBasedServiceResponse = usdResponseData;
165         mUsdSessionId = usdSessionId;
166     }
167 
168     /**
169      * Return the USD based service discovery session ID.
170      *
171      * @return A nonzero ID in the range of 1 to 255.
172      * @hide
173      */
getUsdSessionId()174     public int getUsdSessionId() {
175         return mUsdSessionId;
176     }
177 
178     /**
179      * Set the USD based service discovery session ID.
180      *
181      * @param sessionId A nonzero ID in the range of 1 to 255.
182      * @hide
183      */
setUsdSessionId(int sessionId)184     public void setUsdSessionId(int sessionId) {
185         mUsdSessionId = sessionId;
186     }
187 
188     /**
189      * Return the service type of service discovery response.
190      *
191      * @return service discovery type.<br>
192      * e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_BONJOUR}
193      */
getServiceType()194     public int getServiceType() {
195         return mServiceType;
196     }
197 
198     /**
199      * Return the status code of service discovery response.
200      *
201      * @return status code.
202      * @see Status
203      */
getStatus()204     public int getStatus() {
205         return mStatus;
206     }
207 
208     /**
209      * Return the transaction id of service discovery response.
210      *
211      * @return transaction id.
212      * @hide
213      */
getTransactionId()214     public int getTransactionId() {
215         return mTransId;
216     }
217 
218     /**
219      * Return response data.
220      *
221      * <pre>Data format depends on service type
222      *
223      * @return a query or response data.
224      */
getRawData()225     public byte[] getRawData() {
226         return mData;
227     }
228 
229     /**
230      * Returns the source device of service discovery response.
231      *
232      * <pre>This is valid only when service discovery response.
233      *
234      * @return the source device of service discovery response.
235      */
getSrcDevice()236     public WifiP2pDevice getSrcDevice() {
237         return mDevice;
238     }
239 
240     /** @hide */
setSrcDevice(WifiP2pDevice dev)241     public void setSrcDevice(WifiP2pDevice dev) {
242         if (dev == null) return;
243         this.mDevice = dev;
244     }
245 
246     /**
247      * Get the service response data received through un-synchronized service
248      * discovery (USD) protocol.
249      *
250      * @return A valid or not null {@link WifiP2pUsdBasedServiceResponse} if the service response
251      * data is received through un-synchronized service discovery (USD) protocol.
252      * Otherwise, it is null.
253      * @hide
254      */
255     @Nullable
getWifiP2pUsdBasedServiceResponse()256     public WifiP2pUsdBasedServiceResponse getWifiP2pUsdBasedServiceResponse() {
257         return mUsdBasedServiceResponse;
258     }
259 
260 
261     /**
262      * Create the list of  WifiP2pServiceResponse instance from supplicant event.
263      *
264      * @param srcAddr source address of the service response
265      * @param tlvsBin byte array containing the binary tlvs data
266      * @return if parse failed, return null
267      * @hide
268      */
newInstance(String srcAddr, byte[] tlvsBin)269     public static List<WifiP2pServiceResponse> newInstance(String srcAddr, byte[] tlvsBin) {
270         //updateIndicator not used, and not passed up from supplicant
271 
272         List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>();
273         WifiP2pDevice dev = new WifiP2pDevice();
274         dev.deviceAddress = srcAddr;
275         if (tlvsBin == null) {
276             return null;
277         }
278 
279 
280         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(tlvsBin));
281         try {
282             while (dis.available() > 0) {
283                 /*
284                  * Service discovery header is as follows.
285                  * ______________________________________________________________
286                  * |           Length(2byte)     | Type(1byte) | TransId(1byte)}|
287                  * ______________________________________________________________
288                  * | status(1byte)  |            vendor specific(variable)      |
289                  */
290                 // The length equals to 3 plus the number of octets in the vendor
291                 // specific content field. And this is little endian.
292                 int length = (dis.readUnsignedByte() +
293                         (dis.readUnsignedByte() << 8)) - 3;
294                 int type = dis.readUnsignedByte();
295                 int transId = dis.readUnsignedByte();
296                 int status = dis.readUnsignedByte();
297                 if (length < 0) {
298                     return null;
299                 }
300                 if (length == 0) {
301                     if (status == Status.SUCCESS) {
302                         respList.add(new WifiP2pServiceResponse(type, status,
303                             transId, dev, null));
304                     }
305                     continue;
306                 }
307                 if (length > MAX_BUF_SIZE) {
308                     dis.skip(length);
309                     continue;
310                 }
311                 byte[] data = new byte[length];
312                 dis.readFully(data);
313 
314                 WifiP2pServiceResponse resp;
315                 if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
316                     resp = WifiP2pDnsSdServiceResponse.newInstance(status,
317                             transId, dev, data);
318                 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
319                     resp = WifiP2pUpnpServiceResponse.newInstance(status,
320                             transId, dev, data);
321                 } else {
322                     resp = new WifiP2pServiceResponse(type, status, transId, dev, data);
323                 }
324                 if (resp != null && resp.getStatus() == Status.SUCCESS) {
325                     respList.add(resp);
326                 }
327             }
328             return respList;
329         } catch (IOException e) {
330             e.printStackTrace();
331         }
332 
333         if (respList.size() > 0) {
334             return respList;
335         }
336         return null;
337     }
338 
339     /**
340      * Converts hex string to byte array.
341      *
342      * @param hex hex string. if invalid, return null.
343      * @return binary data.
344      */
hexStr2Bin(String hex)345     private static byte[] hexStr2Bin(String hex) {
346         int sz = hex.length()/2;
347         byte[] b = new byte[hex.length()/2];
348 
349         for (int i=0;i<sz;i++) {
350             try {
351                 b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16);
352             } catch (Exception e) {
353                 e.printStackTrace();
354                 return null;
355             }
356         }
357         return b;
358     }
359 
360     @Override
toString()361     public String toString() {
362         StringBuffer sbuf = new StringBuffer();
363         sbuf.append("serviceType:").append(mServiceType);
364         sbuf.append(" status:").append(Status.toString(mStatus));
365         sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
366         sbuf.append(" data:").append(Arrays.toString(mData));
367         if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
368             sbuf.append(" USD based service response:")
369                     .append((mUsdBasedServiceResponse == null)
370                             ? "<null>" : mUsdBasedServiceResponse.toString());
371         }
372         return sbuf.toString();
373     }
374 
375     @Override
equals(Object o)376     public boolean equals(Object o) {
377         if (o == this) {
378             return true;
379         }
380         if (!(o instanceof WifiP2pServiceResponse)) {
381             return false;
382         }
383 
384         WifiP2pServiceResponse req = (WifiP2pServiceResponse)o;
385 
386         return mServiceType == req.mServiceType
387                 && mStatus == req.mStatus
388                 && Objects.equals(mDevice.deviceAddress, req.mDevice.deviceAddress)
389                 && Arrays.equals(mData, req.mData)
390                 && Objects.equals(mUsdBasedServiceResponse, req.mUsdBasedServiceResponse);
391     }
392 
equals(Object a, Object b)393     private boolean equals(Object a, Object b) {
394         if (a == null && b == null) {
395             return true;
396         } else if (a != null) {
397             return a.equals(b);
398         }
399         return false;
400     }
401 
402     @Override
hashCode()403     public int hashCode() {
404         int result = 17;
405         result = 31 * result + mServiceType;
406         result = 31 * result + mStatus;
407         result = 31 * result + mTransId;
408         result = 31 * result + (mDevice.deviceAddress == null ?
409                 0 : mDevice.deviceAddress.hashCode());
410         result = 31 * result + (mData == null ? 0 : Arrays.hashCode(mData));
411         result = 31 * result + (mUsdBasedServiceResponse == null
412                 ? 0 : mUsdBasedServiceResponse.hashCode());
413         return result;
414     }
415 
416     /** Implement the Parcelable interface {@hide} */
describeContents()417     public int describeContents() {
418         return 0;
419     }
420 
421     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)422     public void writeToParcel(Parcel dest, int flags) {
423         dest.writeInt(mServiceType);
424         dest.writeInt(mStatus);
425         dest.writeInt(mTransId);
426         dest.writeParcelable(mDevice, flags);
427         if (mData == null || mData.length == 0) {
428             dest.writeInt(0);
429         } else {
430             dest.writeInt(mData.length);
431             dest.writeByteArray(mData);
432         }
433         if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
434             dest.writeParcelable(mUsdBasedServiceResponse, flags);
435             dest.writeInt(mUsdSessionId);
436         }
437     }
438 
439     /** Implement the Parcelable interface {@hide} */
440     public static final @android.annotation.NonNull Creator<WifiP2pServiceResponse> CREATOR =
441             new Creator<WifiP2pServiceResponse>() {
442                 public WifiP2pServiceResponse createFromParcel(Parcel in) {
443                     int type = in.readInt();
444                     int status = in.readInt();
445                     int transId = in.readInt();
446                     WifiP2pDevice dev = in.readParcelable(WifiP2pDevice.class.getClassLoader());
447                     int len = in.readInt();
448                     byte[] data = null;
449                     if (len > 0) {
450                         data = new byte[len];
451                         in.readByteArray(data);
452                     }
453                     WifiP2pUsdBasedServiceResponse usdServResponse = null;
454                     int usdSessionId = 0;
455                     if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
456                         usdServResponse = in.readParcelable(
457                                 WifiP2pUsdBasedServiceResponse.class.getClassLoader());
458                         usdSessionId = in.readInt();
459                     }
460                     if (type ==  WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) {
461                         return WifiP2pDnsSdServiceResponse.newInstance(status, transId, dev, data);
462                     } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) {
463                         return WifiP2pUpnpServiceResponse.newInstance(status, transId, dev, data);
464                     } else if (usdServResponse != null) {
465                         return new WifiP2pServiceResponse(dev, usdServResponse, usdSessionId);
466                     }
467                     return new WifiP2pServiceResponse(type, status, transId, dev, data);
468                 }
469                 public WifiP2pServiceResponse[] newArray(int size) {
470                     return new WifiP2pServiceResponse[size];
471                 }
472             };
473 }
474