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 17package android.net.wifi.p2p.nsd; 18 19import android.net.wifi.p2p.WifiP2pDevice; 20import android.os.Parcel; 21import android.os.Parcelable; 22 23import java.io.ByteArrayInputStream; 24import java.io.DataInputStream; 25import java.io.IOException; 26import java.util.ArrayList; 27import java.util.Arrays; 28import java.util.List; 29 30/** 31 * The class for a response of service discovery. 32 * 33 * @hide 34 */ 35public class WifiP2pServiceResponse implements Parcelable { 36 37 private static int MAX_BUF_SIZE = 1024; 38 39 /** 40 * Service type. It's defined in table63 in Wi-Fi Direct specification. 41 */ 42 protected int mServiceType; 43 44 /** 45 * Status code of service discovery response. 46 * It's defined in table65 in Wi-Fi Direct specification. 47 * @see Status 48 */ 49 protected int mStatus; 50 51 /** 52 * Service transaction ID. 53 * This is a nonzero value used to match the service request/response TLVs. 54 */ 55 protected int mTransId; 56 57 /** 58 * Source device. 59 */ 60 protected WifiP2pDevice mDevice; 61 62 /** 63 * Service discovery response data based on the requested on 64 * the service protocol type. The protocol format depends on the service type. 65 */ 66 protected byte[] mData; 67 68 69 /** 70 * The status code of service discovery response. 71 * Currently 4 status codes are defined and the status codes from 4 to 255 72 * are reserved. 73 * 74 * See Wi-Fi Direct specification for the detail. 75 */ 76 public static class Status { 77 /** success */ 78 public static final int SUCCESS = 0; 79 80 /** the service protocol type is not available */ 81 public static final int SERVICE_PROTOCOL_NOT_AVAILABLE = 1; 82 83 /** the requested information is not available */ 84 public static final int REQUESTED_INFORMATION_NOT_AVAILABLE = 2; 85 86 /** bad request */ 87 public static final int BAD_REQUEST = 3; 88 89 /** @hide */ 90 public static String toString(int status) { 91 switch(status) { 92 case SUCCESS: 93 return "SUCCESS"; 94 case SERVICE_PROTOCOL_NOT_AVAILABLE: 95 return "SERVICE_PROTOCOL_NOT_AVAILABLE"; 96 case REQUESTED_INFORMATION_NOT_AVAILABLE: 97 return "REQUESTED_INFORMATION_NOT_AVAILABLE"; 98 case BAD_REQUEST: 99 return "BAD_REQUEST"; 100 default: 101 return "UNKNOWN"; 102 } 103 } 104 105 /** not used */ 106 private Status() {} 107 } 108 109 /** 110 * Hidden constructor. This is only used in framework. 111 * 112 * @param serviceType service discovery type. 113 * @param status status code. 114 * @param transId transaction id. 115 * @param device source device. 116 * @param data query data. 117 */ 118 protected WifiP2pServiceResponse(int serviceType, int status, int transId, 119 WifiP2pDevice device, byte[] data) { 120 mServiceType = serviceType; 121 mStatus = status; 122 mTransId = transId; 123 mDevice = device; 124 mData = data; 125 } 126 127 /** 128 * Return the service type of service discovery response. 129 * 130 * @return service discovery type.<br> 131 * e.g) {@link WifiP2pServiceInfo#SERVICE_TYPE_BONJOUR} 132 */ 133 public int getServiceType() { 134 return mServiceType; 135 } 136 137 /** 138 * Return the status code of service discovery response. 139 * 140 * @return status code. 141 * @see Status 142 */ 143 public int getStatus() { 144 return mStatus; 145 } 146 147 /** 148 * Return the transaction id of service discovery response. 149 * 150 * @return transaction id. 151 * @hide 152 */ 153 public int getTransactionId() { 154 return mTransId; 155 } 156 157 /** 158 * Return response data. 159 * 160 * <pre>Data format depends on service type 161 * 162 * @return a query or response data. 163 */ 164 public byte[] getRawData() { 165 return mData; 166 } 167 168 /** 169 * Returns the source device of service discovery response. 170 * 171 * <pre>This is valid only when service discovery response. 172 * 173 * @return the source device of service discovery response. 174 */ 175 public WifiP2pDevice getSrcDevice() { 176 return mDevice; 177 } 178 179 /** @hide */ 180 public void setSrcDevice(WifiP2pDevice dev) { 181 if (dev == null) return; 182 this.mDevice = dev; 183 } 184 185 186 /** 187 * Create the list of WifiP2pServiceResponse instance from supplicant event. 188 * 189 * <pre>The format is as follows. 190 * P2P-SERV-DISC-RESP <address> <update indicator> <response data> 191 * e.g) P2P-SERV-DISC-RESP 02:03:7f:11:62:da 1 0300000101 192 * 193 * @param supplicantEvent wpa_supplicant event string. 194 * @return if parse failed, return null 195 * @hide 196 */ 197 public static List<WifiP2pServiceResponse> newInstance(String supplicantEvent) { 198 199 List<WifiP2pServiceResponse> respList = new ArrayList<WifiP2pServiceResponse>(); 200 String[] args = supplicantEvent.split(" "); 201 if (args.length != 4) { 202 return null; 203 } 204 WifiP2pDevice dev = new WifiP2pDevice(); 205 String srcAddr = args[1]; 206 dev.deviceAddress = srcAddr; 207 //String updateIndicator = args[2];//not used. 208 byte[] bin = hexStr2Bin(args[3]); 209 if (bin == null) { 210 return null; 211 } 212 213 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bin)); 214 try { 215 while (dis.available() > 0) { 216 /* 217 * Service discovery header is as follows. 218 * ______________________________________________________________ 219 * | Length(2byte) | Type(1byte) | TransId(1byte)}| 220 * ______________________________________________________________ 221 * | status(1byte) | vendor specific(variable) | 222 */ 223 // The length equals to 3 plus the number of octets in the vendor 224 // specific content field. And this is little endian. 225 int length = (dis.readUnsignedByte() + 226 (dis.readUnsignedByte() << 8)) - 3; 227 int type = dis.readUnsignedByte(); 228 int transId = dis.readUnsignedByte(); 229 int status = dis.readUnsignedByte(); 230 if (length < 0) { 231 return null; 232 } 233 if (length == 0) { 234 if (status == Status.SUCCESS) { 235 respList.add(new WifiP2pServiceResponse(type, status, 236 transId, dev, null)); 237 } 238 continue; 239 } 240 if (length > MAX_BUF_SIZE) { 241 dis.skip(length); 242 continue; 243 } 244 byte[] data = new byte[length]; 245 dis.readFully(data); 246 247 WifiP2pServiceResponse resp; 248 if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { 249 resp = WifiP2pDnsSdServiceResponse.newInstance(status, 250 transId, dev, data); 251 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { 252 resp = WifiP2pUpnpServiceResponse.newInstance(status, 253 transId, dev, data); 254 } else { 255 resp = new WifiP2pServiceResponse(type, status, transId, dev, data); 256 } 257 if (resp != null && resp.getStatus() == Status.SUCCESS) { 258 respList.add(resp); 259 } 260 } 261 return respList; 262 } catch (IOException e) { 263 e.printStackTrace(); 264 } 265 266 if (respList.size() > 0) { 267 return respList; 268 } 269 return null; 270 } 271 272 /** 273 * Converts hex string to byte array. 274 * 275 * @param hex hex string. if invalid, return null. 276 * @return binary data. 277 */ 278 private static byte[] hexStr2Bin(String hex) { 279 int sz = hex.length()/2; 280 byte[] b = new byte[hex.length()/2]; 281 282 for (int i=0;i<sz;i++) { 283 try { 284 b[i] = (byte)Integer.parseInt(hex.substring(i*2, i*2+2), 16); 285 } catch (Exception e) { 286 e.printStackTrace(); 287 return null; 288 } 289 } 290 return b; 291 } 292 293 @Override 294 public String toString() { 295 StringBuffer sbuf = new StringBuffer(); 296 sbuf.append("serviceType:").append(mServiceType); 297 sbuf.append(" status:").append(Status.toString(mStatus)); 298 sbuf.append(" srcAddr:").append(mDevice.deviceAddress); 299 sbuf.append(" data:").append(Arrays.toString(mData)); 300 return sbuf.toString(); 301 } 302 303 @Override 304 public boolean equals(Object o) { 305 if (o == this) { 306 return true; 307 } 308 if (!(o instanceof WifiP2pServiceResponse)) { 309 return false; 310 } 311 312 WifiP2pServiceResponse req = (WifiP2pServiceResponse)o; 313 314 return (req.mServiceType == mServiceType) && 315 (req.mStatus == mStatus) && 316 equals(req.mDevice.deviceAddress, mDevice.deviceAddress) && 317 Arrays.equals(req.mData, mData); 318 } 319 320 private boolean equals(Object a, Object b) { 321 if (a == null && b == null) { 322 return true; 323 } else if (a != null) { 324 return a.equals(b); 325 } 326 return false; 327 } 328 329 @Override 330 public int hashCode() { 331 int result = 17; 332 result = 31 * result + mServiceType; 333 result = 31 * result + mStatus; 334 result = 31 * result + mTransId; 335 result = 31 * result + (mDevice.deviceAddress == null ? 336 0 : mDevice.deviceAddress.hashCode()); 337 result = 31 * result + (mData == null ? 0 : Arrays.hashCode(mData)); 338 return result; 339 } 340 341 /** Implement the Parcelable interface {@hide} */ 342 public int describeContents() { 343 return 0; 344 } 345 346 /** Implement the Parcelable interface {@hide} */ 347 public void writeToParcel(Parcel dest, int flags) { 348 dest.writeInt(mServiceType); 349 dest.writeInt(mStatus); 350 dest.writeInt(mTransId); 351 dest.writeParcelable(mDevice, flags); 352 if (mData == null || mData.length == 0) { 353 dest.writeInt(0); 354 } else { 355 dest.writeInt(mData.length); 356 dest.writeByteArray(mData); 357 } 358 } 359 360 /** Implement the Parcelable interface {@hide} */ 361 public static final Creator<WifiP2pServiceResponse> CREATOR = 362 new Creator<WifiP2pServiceResponse>() { 363 public WifiP2pServiceResponse createFromParcel(Parcel in) { 364 365 int type = in.readInt(); 366 int status = in.readInt(); 367 int transId = in.readInt(); 368 WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null); 369 int len = in.readInt(); 370 byte[] data = null; 371 if (len > 0) { 372 data = new byte[len]; 373 in.readByteArray(data); 374 } 375 if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { 376 return WifiP2pDnsSdServiceResponse.newInstance(status, 377 transId, dev, data); 378 } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { 379 return WifiP2pUpnpServiceResponse.newInstance(status, 380 transId, dev, data); 381 } 382 return new WifiP2pServiceResponse(type, status, transId, dev, data); 383 } 384 385 public WifiP2pServiceResponse[] newArray(int size) { 386 return new WifiP2pServiceResponse[size]; 387 } 388 }; 389} 390