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.WifiP2pManager;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import java.util.Locale;
24 
25 /**
26  * A class for creating a service discovery request for use with
27  * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
28  *
29  * <p>This class is used to create service discovery request for custom
30  * vendor specific service discovery protocol {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}
31  * or to search all service protocols {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}.
32  *
33  * <p>For the purpose of creating a UPnP or Bonjour service request, use
34  * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} respectively.
35  *
36  * {@see WifiP2pManager}
37  * {@see WifiP2pUpnpServiceRequest}
38  * {@see WifiP2pDnsSdServiceRequest}
39  */
40 public class WifiP2pServiceRequest implements Parcelable {
41 
42     /**
43      * Service discovery protocol. It's defined in table63 in Wi-Fi Direct specification.
44      */
45     private int mProtocolType;
46 
47     /**
48      * The length of the service request TLV.
49      * The value is equal to 2 plus the number of octets in the
50      * query data field.
51      */
52     private int mLength;
53 
54     /**
55      * Service transaction ID.
56      * This is a nonzero value used to match the service request/response TLVs.
57      */
58     private int mTransId;
59 
60     /**
61      * The hex dump string of query data for the requested service information.
62      *
63      * e.g) DnsSd apple file sharing over tcp (dns name=_afpovertcp._tcp.local.)
64      * 0b5f6166706f766572746370c00c000c01
65      */
66     private String mQuery;
67 
68     /**
69      * This constructor is only used in newInstance().
70      *
71      * @param protocolType service discovery protocol.
72      * @param query The part of service specific query.
73      * @hide
74      */
WifiP2pServiceRequest(int protocolType, String query)75     protected WifiP2pServiceRequest(int protocolType, String query) {
76         validateQuery(query);
77 
78         mProtocolType = protocolType;
79         mQuery = query;
80         if (query != null) {
81             mLength = query.length()/2 + 2;
82         } else {
83             mLength = 2;
84         }
85     }
86 
87     /**
88      * This constructor is only used in Parcelable.
89      *
90      * @param serviceType service discovery type.
91      * @param length the length of service discovery packet.
92      * @param transId the transaction id
93      * @param query The part of service specific query.
94      */
WifiP2pServiceRequest(int serviceType, int length, int transId, String query)95     private WifiP2pServiceRequest(int serviceType, int length,
96             int transId, String query) {
97         mProtocolType = serviceType;
98         mLength = length;
99         mTransId = transId;
100         mQuery = query;
101     }
102 
103     /**
104      * Return transaction id.
105      *
106      * @return transaction id
107      * @hide
108      */
getTransactionId()109     public int getTransactionId() {
110         return mTransId;
111     }
112 
113     /**
114      * Set transaction id.
115      *
116      * @param id
117      * @hide
118      */
setTransactionId(int id)119     public void setTransactionId(int id) {
120         mTransId = id;
121     }
122 
123     /**
124      * Return wpa_supplicant request string.
125      *
126      * The format is the hex dump of the following frame.
127      * <pre>
128      * _______________________________________________________________
129      * |        Length (2)        |   Type (1)   | Transaction ID (1) |
130      * |                  Query Data (variable)                       |
131      * </pre>
132      *
133      * @return wpa_supplicant request string.
134      * @hide
135      */
getSupplicantQuery()136     public String getSupplicantQuery() {
137         StringBuffer sb = new StringBuffer();
138         // length is retained as little endian format.
139         sb.append(String.format(Locale.US, "%02x", (mLength) & 0xff));
140         sb.append(String.format(Locale.US, "%02x", (mLength >> 8) & 0xff));
141         sb.append(String.format(Locale.US, "%02x", mProtocolType));
142         sb.append(String.format(Locale.US, "%02x", mTransId));
143         if (mQuery != null) {
144             sb.append(mQuery);
145         }
146 
147         return sb.toString();
148     }
149 
150     /**
151      * Validate query.
152      *
153      * <p>If invalid, throw IllegalArgumentException.
154      * @param query The part of service specific query.
155      */
validateQuery(String query)156     private void validateQuery(String query) {
157         if (query == null) {
158             return;
159         }
160 
161         int UNSIGNED_SHORT_MAX = 0xffff;
162         if (query.length()%2 == 1) {
163             throw new IllegalArgumentException(
164                     "query size is invalid. query=" + query);
165         }
166         if (query.length()/2 > UNSIGNED_SHORT_MAX) {
167             throw new IllegalArgumentException(
168                     "query size is too large. len=" + query.length());
169         }
170 
171         // check whether query is hex string.
172         query = query.toLowerCase(Locale.ROOT);
173         char[] chars = query.toCharArray();
174         for (char c: chars) {
175             if (!((c >= '0' && c <= '9') ||
176                     (c >= 'a' && c <= 'f'))){
177                 throw new IllegalArgumentException(
178                         "query should be hex string. query=" + query);
179             }
180         }
181     }
182 
183     /**
184      * Create a service discovery request.
185      *
186      * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
187      * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
188      * In order to create a UPnP or Bonjour service request, use
189      * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
190      * respectively
191      *
192      * @param queryData hex string that is vendor specific.  Can be null.
193      * @return service discovery request.
194      */
newInstance(int protocolType, String queryData)195     public static WifiP2pServiceRequest newInstance(int protocolType, String queryData) {
196         return new WifiP2pServiceRequest(protocolType, queryData);
197     }
198 
199     /**
200      * Create a service discovery request.
201      *
202      * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
203      * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
204      * In order to create a UPnP or Bonjour service request, use
205      * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
206      * respectively
207      *
208      * @return service discovery request.
209      */
newInstance(int protocolType )210     public static WifiP2pServiceRequest newInstance(int protocolType ) {
211         return new WifiP2pServiceRequest(protocolType, null);
212     }
213 
214     @Override
equals(Object o)215     public boolean equals(Object o) {
216         if (o == this) {
217             return true;
218         }
219         if (!(o instanceof WifiP2pServiceRequest)) {
220             return false;
221         }
222 
223         WifiP2pServiceRequest req = (WifiP2pServiceRequest)o;
224 
225         /*
226          * Not compare transaction id.
227          * Transaction id may be changed on each service discovery operation.
228          */
229         if ((req.mProtocolType != mProtocolType) ||
230                 (req.mLength != mLength)) {
231             return false;
232         }
233 
234         if (req.mQuery == null && mQuery == null) {
235             return true;
236         } else if (req.mQuery != null) {
237             return req.mQuery.equals(mQuery);
238         }
239         return false;
240    }
241 
242     @Override
hashCode()243     public int hashCode() {
244         int result = 17;
245         result = 31 * result + mProtocolType;
246         result = 31 * result + mLength;
247         result = 31 * result + (mQuery == null ? 0 : mQuery.hashCode());
248         return result;
249     }
250 
251     /** Implement the Parcelable interface {@hide} */
describeContents()252     public int describeContents() {
253         return 0;
254     }
255 
256     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)257     public void writeToParcel(Parcel dest, int flags) {
258         dest.writeInt(mProtocolType);
259         dest.writeInt(mLength);
260         dest.writeInt(mTransId);
261         dest.writeString(mQuery);
262     }
263 
264     /** Implement the Parcelable interface {@hide} */
265     public static final Creator<WifiP2pServiceRequest> CREATOR =
266         new Creator<WifiP2pServiceRequest>() {
267             public WifiP2pServiceRequest createFromParcel(Parcel in) {
268                 int servType = in.readInt();
269                 int length = in.readInt();
270                 int transId = in.readInt();
271                 String query = in.readString();
272                 return new WifiP2pServiceRequest(servType, length, transId, query);
273             }
274 
275             public WifiP2pServiceRequest[] newArray(int size) {
276                 return new WifiP2pServiceRequest[size];
277             }
278         };
279 }
280