1 /* 2 * Copyright (c) 2014 The Android Open Source Project 3 * Copyright (c) 2008-2009, Motorola, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * - Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * - Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * - Neither the name of the Motorola, Inc. nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 package javax.obex; 35 36 import java.io.ByteArrayOutputStream; 37 import java.io.IOException; 38 import java.util.Calendar; 39 import java.security.SecureRandom; 40 41 /** 42 * This class implements the javax.obex.HeaderSet interface for OBEX over 43 * RFCOMM. 44 * @hide 45 */ 46 public final class HeaderSet { 47 48 /** 49 * Represents the OBEX Count header. This allows the connection statement to 50 * tell the server how many objects it plans to send or retrieve. 51 * <P> 52 * The value of <code>COUNT</code> is 0xC0 (192). 53 */ 54 public static final int COUNT = 0xC0; 55 56 /** 57 * Represents the OBEX Name header. This specifies the name of the object. 58 * <P> 59 * The value of <code>NAME</code> is 0x01 (1). 60 */ 61 public static final int NAME = 0x01; 62 63 /** 64 * Represents the OBEX Type header. This allows a request to specify the 65 * type of the object (e.g. text, html, binary, etc.). 66 * <P> 67 * The value of <code>TYPE</code> is 0x42 (66). 68 */ 69 public static final int TYPE = 0x42; 70 71 /** 72 * Represents the OBEX Length header. This is the length of the object in 73 * bytes. 74 * <P> 75 * The value of <code>LENGTH</code> is 0xC3 (195). 76 */ 77 public static final int LENGTH = 0xC3; 78 79 /** 80 * Represents the OBEX Time header using the ISO 8601 standards. This is the 81 * preferred time header. 82 * <P> 83 * The value of <code>TIME_ISO_8601</code> is 0x44 (68). 84 */ 85 public static final int TIME_ISO_8601 = 0x44; 86 87 /** 88 * Represents the OBEX Time header using the 4 byte representation. This is 89 * only included for backwards compatibility. It represents the number of 90 * seconds since January 1, 1970. 91 * <P> 92 * The value of <code>TIME_4_BYTE</code> is 0xC4 (196). 93 */ 94 public static final int TIME_4_BYTE = 0xC4; 95 96 /** 97 * Represents the OBEX Description header. This is a text description of the 98 * object. 99 * <P> 100 * The value of <code>DESCRIPTION</code> is 0x05 (5). 101 */ 102 public static final int DESCRIPTION = 0x05; 103 104 /** 105 * Represents the OBEX Target header. This is the name of the service an 106 * operation is targeted to. 107 * <P> 108 * The value of <code>TARGET</code> is 0x46 (70). 109 */ 110 public static final int TARGET = 0x46; 111 112 /** 113 * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be 114 * included in a request or reply. 115 * <P> 116 * The value of <code>HTTP</code> is 0x47 (71). 117 */ 118 public static final int HTTP = 0x47; 119 120 /** 121 * Represents the OBEX BODY header. 122 * <P> 123 * The value of <code>BODY</code> is 0x48 (72). 124 */ 125 public static final int BODY = 0x48; 126 127 /** 128 * Represents the OBEX End of BODY header. 129 * <P> 130 * The value of <code>BODY</code> is 0x49 (73). 131 */ 132 public static final int END_OF_BODY = 0x49; 133 134 /** 135 * Represents the OBEX Who header. Identifies the OBEX application to 136 * determine if the two peers are talking to each other. 137 * <P> 138 * The value of <code>WHO</code> is 0x4A (74). 139 */ 140 public static final int WHO = 0x4A; 141 142 /** 143 * Represents the OBEX Connection ID header. Identifies used for OBEX 144 * connection multiplexing. 145 * <P> 146 * The value of <code>CONNECTION_ID</code> is 0xCB (203). 147 */ 148 149 public static final int CONNECTION_ID = 0xCB; 150 151 /** 152 * Represents the OBEX Application Parameter header. This header specifies 153 * additional application request and response information. 154 * <P> 155 * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76). 156 */ 157 public static final int APPLICATION_PARAMETER = 0x4C; 158 159 /** 160 * Represents the OBEX authentication digest-challenge. 161 * <P> 162 * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77). 163 */ 164 public static final int AUTH_CHALLENGE = 0x4D; 165 166 /** 167 * Represents the OBEX authentication digest-response. 168 * <P> 169 * The value of <code>AUTH_RESPONSE</code> is 0x4E (78). 170 */ 171 public static final int AUTH_RESPONSE = 0x4E; 172 173 /** 174 * Represents the OBEX Object Class header. This header specifies the OBEX 175 * object class of the object. 176 * <P> 177 * The value of <code>OBJECT_CLASS</code> is 0x4F (79). 178 */ 179 public static final int OBJECT_CLASS = 0x4F; 180 181 private Long mCount; // 4 byte unsigned integer 182 183 private String mName; // null terminated Unicode text string 184 185 private boolean mEmptyName; 186 187 private String mType; // null terminated ASCII text string 188 189 private Long mLength; // 4 byte unsigend integer 190 191 private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ 192 193 private Calendar mByteTime; // 4 byte unsigned integer 194 195 private String mDescription; // null terminated Unicode text String 196 197 private byte[] mTarget; // byte sequence 198 199 private byte[] mHttpHeader; // byte sequence 200 201 private byte[] mWho; // length prefixed byte sequence 202 203 private byte[] mAppParam; // byte sequence of the form tag length value 204 205 private byte[] mObjectClass; // byte sequence 206 207 private String[] mUnicodeUserDefined; //null terminated unicode string 208 209 private byte[][] mSequenceUserDefined; // byte sequence user defined 210 211 private Byte[] mByteUserDefined; // 1 byte 212 213 private Long[] mIntegerUserDefined; // 4 byte unsigned integer 214 215 private final SecureRandom mRandom; 216 217 /*package*/ byte[] nonce; 218 219 public byte[] mAuthChall; // The authentication challenge header 220 221 public byte[] mAuthResp; // The authentication response header 222 223 public byte[] mConnectionID; // THe connection ID 224 225 public int responseCode; 226 227 /** 228 * Creates new <code>HeaderSet</code> object. 229 * @param size the max packet size for this connection 230 */ 231 public HeaderSet() { 232 mUnicodeUserDefined = new String[16]; 233 mSequenceUserDefined = new byte[16][]; 234 mByteUserDefined = new Byte[16]; 235 mIntegerUserDefined = new Long[16]; 236 responseCode = -1; 237 mRandom = new SecureRandom(); 238 } 239 240 /** 241 * Sets flag for special "value" of NAME header which should be empty. This 242 * is not the same as NAME header with empty string in which case it will 243 * have length of 5 bytes. It should be 3 bytes with only header id and 244 * length field. 245 */ 246 public void setEmptyNameHeader() { 247 mName = null; 248 mEmptyName = true; 249 } 250 251 /** 252 * Gets flag for special "value" of NAME header which should be empty. See 253 * above. 254 */ 255 public boolean getEmptyNameHeader() { 256 return mEmptyName; 257 } 258 259 /** 260 * Sets the value of the header identifier to the value provided. The type 261 * of object must correspond to the Java type defined in the description of 262 * this interface. If <code>null</code> is passed as the 263 * <code>headerValue</code> then the header will be removed from the set of 264 * headers to include in the next request. 265 * @param headerID the identifier to include in the message 266 * @param headerValue the value of the header identifier 267 * @throws IllegalArgumentException if the header identifier provided is not 268 * one defined in this interface or a user-defined header; if the 269 * type of <code>headerValue</code> is not the correct Java type as 270 * defined in the description of this interface\ 271 */ 272 public void setHeader(int headerID, Object headerValue) { 273 long temp = -1; 274 275 switch (headerID) { 276 case COUNT: 277 if (!(headerValue instanceof Long)) { 278 if (headerValue == null) { 279 mCount = null; 280 break; 281 } 282 throw new IllegalArgumentException("Count must be a Long"); 283 } 284 temp = ((Long)headerValue).longValue(); 285 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 286 throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF"); 287 } 288 mCount = (Long)headerValue; 289 break; 290 case NAME: 291 if ((headerValue != null) && (!(headerValue instanceof String))) { 292 throw new IllegalArgumentException("Name must be a String"); 293 } 294 mEmptyName = false; 295 mName = (String)headerValue; 296 break; 297 case TYPE: 298 if ((headerValue != null) && (!(headerValue instanceof String))) { 299 throw new IllegalArgumentException("Type must be a String"); 300 } 301 mType = (String)headerValue; 302 break; 303 case LENGTH: 304 if (!(headerValue instanceof Long)) { 305 if (headerValue == null) { 306 mLength = null; 307 break; 308 } 309 throw new IllegalArgumentException("Length must be a Long"); 310 } 311 temp = ((Long)headerValue).longValue(); 312 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 313 throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF"); 314 } 315 mLength = (Long)headerValue; 316 break; 317 case TIME_ISO_8601: 318 if ((headerValue != null) && (!(headerValue instanceof Calendar))) { 319 throw new IllegalArgumentException("Time ISO 8601 must be a Calendar"); 320 } 321 mIsoTime = (Calendar)headerValue; 322 break; 323 case TIME_4_BYTE: 324 if ((headerValue != null) && (!(headerValue instanceof Calendar))) { 325 throw new IllegalArgumentException("Time 4 Byte must be a Calendar"); 326 } 327 mByteTime = (Calendar)headerValue; 328 break; 329 case DESCRIPTION: 330 if ((headerValue != null) && (!(headerValue instanceof String))) { 331 throw new IllegalArgumentException("Description must be a String"); 332 } 333 mDescription = (String)headerValue; 334 break; 335 case TARGET: 336 if (headerValue == null) { 337 mTarget = null; 338 } else { 339 if (!(headerValue instanceof byte[])) { 340 throw new IllegalArgumentException("Target must be a byte array"); 341 } else { 342 mTarget = new byte[((byte[])headerValue).length]; 343 System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length); 344 } 345 } 346 break; 347 case HTTP: 348 if (headerValue == null) { 349 mHttpHeader = null; 350 } else { 351 if (!(headerValue instanceof byte[])) { 352 throw new IllegalArgumentException("HTTP must be a byte array"); 353 } else { 354 mHttpHeader = new byte[((byte[])headerValue).length]; 355 System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length); 356 } 357 } 358 break; 359 case WHO: 360 if (headerValue == null) { 361 mWho = null; 362 } else { 363 if (!(headerValue instanceof byte[])) { 364 throw new IllegalArgumentException("WHO must be a byte array"); 365 } else { 366 mWho = new byte[((byte[])headerValue).length]; 367 System.arraycopy(headerValue, 0, mWho, 0, mWho.length); 368 } 369 } 370 break; 371 case OBJECT_CLASS: 372 if (headerValue == null) { 373 mObjectClass = null; 374 } else { 375 if (!(headerValue instanceof byte[])) { 376 throw new IllegalArgumentException("Object Class must be a byte array"); 377 } else { 378 mObjectClass = new byte[((byte[])headerValue).length]; 379 System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length); 380 } 381 } 382 break; 383 case APPLICATION_PARAMETER: 384 if (headerValue == null) { 385 mAppParam = null; 386 } else { 387 if (!(headerValue instanceof byte[])) { 388 throw new IllegalArgumentException( 389 "Application Parameter must be a byte array"); 390 } else { 391 mAppParam = new byte[((byte[])headerValue).length]; 392 System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length); 393 } 394 } 395 break; 396 default: 397 // Verify that it was not a Unicode String user Defined 398 if ((headerID >= 0x30) && (headerID <= 0x3F)) { 399 if ((headerValue != null) && (!(headerValue instanceof String))) { 400 throw new IllegalArgumentException( 401 "Unicode String User Defined must be a String"); 402 } 403 mUnicodeUserDefined[headerID - 0x30] = (String)headerValue; 404 405 break; 406 } 407 // Verify that it was not a byte sequence user defined value 408 if ((headerID >= 0x70) && (headerID <= 0x7F)) { 409 410 if (headerValue == null) { 411 mSequenceUserDefined[headerID - 0x70] = null; 412 } else { 413 if (!(headerValue instanceof byte[])) { 414 throw new IllegalArgumentException( 415 "Byte Sequence User Defined must be a byte array"); 416 } else { 417 mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length]; 418 System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70], 419 0, mSequenceUserDefined[headerID - 0x70].length); 420 } 421 } 422 break; 423 } 424 // Verify that it was not a Byte user Defined 425 if ((headerID >= 0xB0) && (headerID <= 0xBF)) { 426 if ((headerValue != null) && (!(headerValue instanceof Byte))) { 427 throw new IllegalArgumentException("ByteUser Defined must be a Byte"); 428 } 429 mByteUserDefined[headerID - 0xB0] = (Byte)headerValue; 430 431 break; 432 } 433 // Verify that is was not the 4 byte unsigned integer user 434 // defined header 435 if ((headerID >= 0xF0) && (headerID <= 0xFF)) { 436 if (!(headerValue instanceof Long)) { 437 if (headerValue == null) { 438 mIntegerUserDefined[headerID - 0xF0] = null; 439 break; 440 } 441 throw new IllegalArgumentException("Integer User Defined must be a Long"); 442 } 443 temp = ((Long)headerValue).longValue(); 444 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 445 throw new IllegalArgumentException( 446 "Integer User Defined must be between 0 and 0xFFFFFFFF"); 447 } 448 mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue; 449 break; 450 } 451 throw new IllegalArgumentException("Invalid Header Identifier"); 452 } 453 } 454 455 /** 456 * Retrieves the value of the header identifier provided. The type of the 457 * Object returned is defined in the description of this interface. 458 * @param headerID the header identifier whose value is to be returned 459 * @return the value of the header provided or <code>null</code> if the 460 * header identifier specified is not part of this 461 * <code>HeaderSet</code> object 462 * @throws IllegalArgumentException if the <code>headerID</code> is not one 463 * defined in this interface or any of the user-defined headers 464 * @throws IOException if an error occurred in the transport layer during 465 * the operation or if the connection has been closed 466 */ 467 public Object getHeader(int headerID) throws IOException { 468 469 switch (headerID) { 470 case COUNT: 471 return mCount; 472 case NAME: 473 return mName; 474 case TYPE: 475 return mType; 476 case LENGTH: 477 return mLength; 478 case TIME_ISO_8601: 479 return mIsoTime; 480 case TIME_4_BYTE: 481 return mByteTime; 482 case DESCRIPTION: 483 return mDescription; 484 case TARGET: 485 return mTarget; 486 case HTTP: 487 return mHttpHeader; 488 case WHO: 489 return mWho; 490 case CONNECTION_ID: 491 return mConnectionID; 492 case OBJECT_CLASS: 493 return mObjectClass; 494 case APPLICATION_PARAMETER: 495 return mAppParam; 496 default: 497 // Verify that it was not a Unicode String user Defined 498 if ((headerID >= 0x30) && (headerID <= 0x3F)) { 499 return mUnicodeUserDefined[headerID - 0x30]; 500 } 501 // Verify that it was not a byte sequence user defined header 502 if ((headerID >= 0x70) && (headerID <= 0x7F)) { 503 return mSequenceUserDefined[headerID - 0x70]; 504 } 505 // Verify that it was not a byte user defined header 506 if ((headerID >= 0xB0) && (headerID <= 0xBF)) { 507 return mByteUserDefined[headerID - 0xB0]; 508 } 509 // Verify that it was not a integer user defined header 510 if ((headerID >= 0xF0) && (headerID <= 0xFF)) { 511 return mIntegerUserDefined[headerID - 0xF0]; 512 } 513 throw new IllegalArgumentException("Invalid Header Identifier"); 514 } 515 } 516 517 /** 518 * Retrieves the list of headers that may be retrieved via the 519 * <code>getHeader</code> method that will not return <code>null</code>. In 520 * other words, this method returns all the headers that are available in 521 * this object. 522 * @see #getHeader 523 * @return the array of headers that are set in this object or 524 * <code>null</code> if no headers are available 525 * @throws IOException if an error occurred in the transport layer during 526 * the operation or the connection has been closed 527 */ 528 public int[] getHeaderList() throws IOException { 529 ByteArrayOutputStream out = new ByteArrayOutputStream(); 530 531 if (mCount != null) { 532 out.write(COUNT); 533 } 534 if (mName != null) { 535 out.write(NAME); 536 } 537 if (mType != null) { 538 out.write(TYPE); 539 } 540 if (mLength != null) { 541 out.write(LENGTH); 542 } 543 if (mIsoTime != null) { 544 out.write(TIME_ISO_8601); 545 } 546 if (mByteTime != null) { 547 out.write(TIME_4_BYTE); 548 } 549 if (mDescription != null) { 550 out.write(DESCRIPTION); 551 } 552 if (mTarget != null) { 553 out.write(TARGET); 554 } 555 if (mHttpHeader != null) { 556 out.write(HTTP); 557 } 558 if (mWho != null) { 559 out.write(WHO); 560 } 561 if (mAppParam != null) { 562 out.write(APPLICATION_PARAMETER); 563 } 564 if (mObjectClass != null) { 565 out.write(OBJECT_CLASS); 566 } 567 568 for (int i = 0x30; i < 0x40; i++) { 569 if (mUnicodeUserDefined[i - 0x30] != null) { 570 out.write(i); 571 } 572 } 573 574 for (int i = 0x70; i < 0x80; i++) { 575 if (mSequenceUserDefined[i - 0x70] != null) { 576 out.write(i); 577 } 578 } 579 580 for (int i = 0xB0; i < 0xC0; i++) { 581 if (mByteUserDefined[i - 0xB0] != null) { 582 out.write(i); 583 } 584 } 585 586 for (int i = 0xF0; i < 0x100; i++) { 587 if (mIntegerUserDefined[i - 0xF0] != null) { 588 out.write(i); 589 } 590 } 591 592 byte[] headers = out.toByteArray(); 593 out.close(); 594 595 if ((headers == null) || (headers.length == 0)) { 596 return null; 597 } 598 599 int[] result = new int[headers.length]; 600 for (int i = 0; i < headers.length; i++) { 601 // Convert the byte to a positive integer. That is, an integer 602 // between 0 and 256. 603 result[i] = headers[i] & 0xFF; 604 } 605 606 return result; 607 } 608 609 /** 610 * Sets the authentication challenge header. The <code>realm</code> will be 611 * encoded based upon the default encoding scheme used by the implementation 612 * to encode strings. Therefore, the encoding scheme used to encode the 613 * <code>realm</code> is application dependent. 614 * @param realm a short description that describes what password to use; if 615 * <code>null</code> no realm will be sent in the authentication 616 * challenge header 617 * @param userID if <code>true</code>, a user ID is required in the reply; 618 * if <code>false</code>, no user ID is required 619 * @param access if <code>true</code> then full access will be granted if 620 * successful; if <code>false</code> then read-only access will be 621 * granted if successful 622 * @throws IOException 623 */ 624 public void createAuthenticationChallenge(String realm, boolean userID, boolean access) 625 throws IOException { 626 627 nonce = new byte[16]; 628 for (int i = 0; i < 16; i++) { 629 nonce[i] = (byte)mRandom.nextInt(); 630 } 631 632 mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID); 633 } 634 635 /** 636 * Returns the response code received from the server. Response codes are 637 * defined in the <code>ResponseCodes</code> class. 638 * @see ResponseCodes 639 * @return the response code retrieved from the server 640 * @throws IOException if an error occurred in the transport layer during 641 * the transaction; if this method is called on a 642 * <code>HeaderSet</code> object created by calling 643 * <code>createHeaderSet()</code> in a <code>ClientSession</code> 644 * object; if this object was created by an OBEX server 645 */ 646 public int getResponseCode() throws IOException { 647 if (responseCode == -1) { 648 throw new IOException("May not be called on a server"); 649 } else { 650 return responseCode; 651 } 652 } 653 } 654