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