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