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.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.net.wifi.p2p.WifiP2pManager; 25 import android.net.wifi.util.Environment; 26 import android.os.Build; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 30 import com.android.wifi.flags.Flags; 31 32 import java.util.Locale; 33 import java.util.Objects; 34 35 /** 36 * A class for creating a service discovery request for use with 37 * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest} 38 * 39 * <p>This class is used to create service discovery request for custom 40 * vendor specific service discovery protocol {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC} 41 * or to search all service protocols {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}. 42 * 43 * <p>For the purpose of creating a UPnP or Bonjour service request, use 44 * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} respectively. 45 * 46 * {@see WifiP2pManager} 47 * {@see WifiP2pUpnpServiceRequest} 48 * {@see WifiP2pDnsSdServiceRequest} 49 */ 50 public class WifiP2pServiceRequest implements Parcelable { 51 52 /** 53 * Service discovery protocol. It's defined in table63 in Wi-Fi Direct specification. 54 */ 55 private int mProtocolType; 56 57 /** 58 * The length of the service request TLV. 59 * The value is equal to 2 plus the number of octets in the 60 * query data field. 61 */ 62 private int mLength; 63 64 /** 65 * Service transaction ID. 66 * This is a nonzero value used to match the service request/response TLVs. 67 */ 68 private int mTransId; 69 70 /** 71 * The hex dump string of query data for the requested service information. 72 * 73 * e.g) DnsSd apple file sharing over tcp (dns name=_afpovertcp._tcp.local.) 74 * 0b5f6166706f766572746370c00c000c01 75 */ 76 private String mQuery; 77 78 /** 79 * This field is used only when the service discovery request is using un-synchronized service 80 * discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 specification section 3.7 - 81 * "Unsynchronized Service Discovery (USD)" for the details. 82 */ 83 private WifiP2pUsdBasedServiceConfig mUsdServiceConfig; 84 85 /** 86 * Service discovery request session ID (Seeker ID) for USD based service discovery. 87 * The session ID is used to match the USD based service discovery request/response frames. 88 * A nonzero ID in the range of 1 to 255 is filled in the Service descriptor attribute (SDA) - 89 * instance ID field of the service discovery request frame (Subscribe frame). The responding 90 * device copies this ID in the Service descriptor attribute (SDA) - requester instance ID 91 * field of the service discovery response frame (Publish frame). 92 * Zero by default indicates that the USD session for this service is not running. 93 */ 94 private int mUsdSessionId = 0; 95 96 /** 97 * This constructor is only used in newInstance(). 98 * 99 * @param protocolType service discovery protocol. 100 * @param query The part of service specific query. 101 * @hide 102 */ 103 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) WifiP2pServiceRequest(int protocolType, String query)104 protected WifiP2pServiceRequest(int protocolType, String query) { 105 validateQuery(query); 106 107 mProtocolType = protocolType; 108 mQuery = query; 109 if (query != null) { 110 mLength = query.length()/2 + 2; 111 } else { 112 mLength = 2; 113 } 114 } 115 116 /** 117 * This constructor is only used in parcelable. 118 * 119 * @param serviceType service discovery type. 120 * @param length the length of service discovery packet. 121 * @param transId the transaction id 122 * @param query The part of service specific query. 123 * @param usdConfig The USD based service config. 124 * @param usdSessionId The USD based service discovery request session ID. 125 */ WifiP2pServiceRequest(int serviceType, int length, int transId, String query, @NonNull WifiP2pUsdBasedServiceConfig usdConfig, int usdSessionId)126 private WifiP2pServiceRequest(int serviceType, int length, 127 int transId, String query, @NonNull WifiP2pUsdBasedServiceConfig usdConfig, 128 int usdSessionId) { 129 mProtocolType = serviceType; 130 mLength = length; 131 mTransId = transId; 132 mQuery = query; 133 mUsdServiceConfig = usdConfig; 134 mUsdSessionId = usdSessionId; 135 } 136 137 /** 138 * Return transaction id. 139 * 140 * @return transaction id 141 * @hide 142 */ getTransactionId()143 public int getTransactionId() { 144 return mTransId; 145 } 146 147 /** 148 * Set transaction id. 149 * 150 * @param id 151 * @hide 152 */ setTransactionId(int id)153 public void setTransactionId(int id) { 154 mTransId = id; 155 } 156 157 /** 158 * Return wpa_supplicant request string. 159 * 160 * The format is the hex dump of the following frame. 161 * <pre> 162 * _______________________________________________________________ 163 * | Length (2) | Type (1) | Transaction ID (1) | 164 * | Query Data (variable) | 165 * </pre> 166 * 167 * @return wpa_supplicant request string. 168 * @hide 169 */ getSupplicantQuery()170 public String getSupplicantQuery() { 171 StringBuffer sb = new StringBuffer(); 172 // length is retained as little endian format. 173 sb.append(String.format(Locale.US, "%02x", (mLength) & 0xff)); 174 sb.append(String.format(Locale.US, "%02x", (mLength >> 8) & 0xff)); 175 sb.append(String.format(Locale.US, "%02x", mProtocolType)); 176 sb.append(String.format(Locale.US, "%02x", mTransId)); 177 if (mQuery != null) { 178 sb.append(mQuery); 179 } 180 181 return sb.toString(); 182 } 183 184 /** 185 * Return the USD based service discovery request session ID. 186 * This ID is used to match the USD based service request/response frames. 187 * 188 * @return A nonzero ID in the range of 1 to 255 when the session is running. 189 * @hide 190 */ getUsdSessionId()191 public int getUsdSessionId() { 192 return mUsdSessionId; 193 } 194 195 /** 196 * Set the USD based service discovery request session ID. 197 * Default value is zero. 198 * 199 * @param sessionId nonzero session ID is set when the USD session for this service is started. 200 * @hide 201 */ setUsdSessionId(int sessionId)202 public void setUsdSessionId(int sessionId) { 203 mUsdSessionId = sessionId; 204 } 205 206 /** 207 /** 208 * Get the service information configured to discover a service using un-synchronized service 209 * discovery (USD) protocol. 210 * See {@link #WifiP2pServiceRequest(WifiP2pUsdBasedServiceConfig)}. 211 * 212 * @return A valid or not null {@link WifiP2pUsdBasedServiceConfig} if the service information 213 * is configured to discover a service using un-synchronized service discovery (USD) protocol. 214 * Otherwise, it is null. 215 */ 216 @RequiresApi(Build.VERSION_CODES.BAKLAVA) 217 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 218 @Nullable getWifiP2pUsdBasedServiceConfig()219 public WifiP2pUsdBasedServiceConfig getWifiP2pUsdBasedServiceConfig() { 220 if (!Environment.isSdkAtLeastB()) { 221 throw new UnsupportedOperationException(); 222 } 223 return mUsdServiceConfig; 224 } 225 226 /** 227 * Validate query. 228 * 229 * <p>If invalid, throw IllegalArgumentException. 230 * @param query The part of service specific query. 231 */ validateQuery(String query)232 private void validateQuery(String query) { 233 if (query == null) { 234 return; 235 } 236 237 int UNSIGNED_SHORT_MAX = 0xffff; 238 if (query.length()%2 == 1) { 239 throw new IllegalArgumentException( 240 "query size is invalid. query=" + query); 241 } 242 if (query.length()/2 > UNSIGNED_SHORT_MAX) { 243 throw new IllegalArgumentException( 244 "query size is too large. len=" + query.length()); 245 } 246 247 // check whether query is hex string. 248 query = query.toLowerCase(Locale.ROOT); 249 char[] chars = query.toCharArray(); 250 for (char c: chars) { 251 if (!((c >= '0' && c <= '9') || 252 (c >= 'a' && c <= 'f'))){ 253 throw new IllegalArgumentException( 254 "query should be hex string. query=" + query); 255 } 256 } 257 } 258 259 /** 260 * Create a service discovery request. 261 * 262 * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL} 263 * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}. 264 * In order to create a UPnP or Bonjour service request, use 265 * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} 266 * respectively 267 * 268 * @param queryData hex string that is vendor specific. Can be null. 269 * @return service discovery request. 270 */ newInstance(int protocolType, String queryData)271 public static WifiP2pServiceRequest newInstance(int protocolType, String queryData) { 272 return new WifiP2pServiceRequest(protocolType, queryData); 273 } 274 275 /** 276 * Create a service discovery request. 277 * 278 * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL} 279 * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}. 280 * In order to create a UPnP or Bonjour service request, use 281 * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} 282 * respectively 283 * 284 * @return service discovery request. 285 */ newInstance(int protocolType )286 public static WifiP2pServiceRequest newInstance(int protocolType ) { 287 return new WifiP2pServiceRequest(protocolType, null); 288 } 289 290 /** 291 * Constructor for creating a service discovery request for discovering the service using 292 * un-synchronized service discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 293 * specification section 3.7 - "Unsynchronized Service Discovery (USD)" for the details. 294 * 295 * @param usdConfig See {@link WifiP2pUsdBasedServiceConfig} 296 * 297 * @return service discovery request containing USD based service configuration. 298 */ 299 @RequiresApi(Build.VERSION_CODES.BAKLAVA) 300 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) WifiP2pServiceRequest(@onNull WifiP2pUsdBasedServiceConfig usdConfig)301 public WifiP2pServiceRequest(@NonNull WifiP2pUsdBasedServiceConfig usdConfig) { 302 if (!Environment.isSdkAtLeastB()) { 303 throw new UnsupportedOperationException(); 304 } 305 Objects.requireNonNull(usdConfig, "usdConfig cannot be null"); 306 mUsdServiceConfig = usdConfig; 307 } 308 309 @Override equals(Object o)310 public boolean equals(Object o) { 311 if (o == this) { 312 return true; 313 } 314 if (!(o instanceof WifiP2pServiceRequest)) { 315 return false; 316 } 317 318 WifiP2pServiceRequest req = (WifiP2pServiceRequest)o; 319 320 /* 321 * Not compare transaction id. 322 * Transaction id may be changed on each service discovery operation. 323 */ 324 return mProtocolType == req.mProtocolType 325 && mLength == req.mLength 326 && Objects.equals(mQuery, req.mQuery) 327 && Objects.equals(mUsdServiceConfig, req.mUsdServiceConfig); 328 } 329 330 @Override hashCode()331 public int hashCode() { 332 int result = 17; 333 result = 31 * result + mProtocolType; 334 result = 31 * result + mLength; 335 result = 31 * result + (mQuery == null ? 0 : mQuery.hashCode()); 336 result = 31 * result + (mUsdServiceConfig == null ? 0 : mUsdServiceConfig.hashCode()); 337 return result; 338 } 339 340 /** Implement the Parcelable interface {@hide} */ describeContents()341 public int describeContents() { 342 return 0; 343 } 344 345 /** Implement the Parcelable interface {@hide} */ writeToParcel(Parcel dest, int flags)346 public void writeToParcel(Parcel dest, int flags) { 347 dest.writeInt(mProtocolType); 348 dest.writeInt(mLength); 349 dest.writeInt(mTransId); 350 dest.writeString(mQuery); 351 if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { 352 dest.writeParcelable(mUsdServiceConfig, flags); 353 dest.writeInt(mUsdSessionId); 354 } 355 } 356 357 /** Implement the Parcelable interface {@hide} */ 358 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 359 public static final @android.annotation.NonNull Creator<WifiP2pServiceRequest> CREATOR = 360 new Creator<WifiP2pServiceRequest>() { 361 public WifiP2pServiceRequest createFromParcel(Parcel in) { 362 int servType = in.readInt(); 363 int length = in.readInt(); 364 int transId = in.readInt(); 365 String query = in.readString(); 366 WifiP2pUsdBasedServiceConfig config = null; 367 int usdSessionId = 0; 368 if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { 369 config = in.readParcelable( 370 WifiP2pUsdBasedServiceConfig.class.getClassLoader()); 371 usdSessionId = in.readInt(); 372 } 373 return new WifiP2pServiceRequest(servType, length, transId, query, config, 374 usdSessionId); 375 } 376 public WifiP2pServiceRequest[] newArray(int size) { 377 return new WifiP2pServiceRequest[size]; 378 } 379 }; 380 } 381