1 /* 2 * Copyright (c) 2015 The Android Open Source Project 3 * Copyright (C) 2015 Samsung LSI 4 * Copyright (c) 2008-2009, Motorola, Inc. 5 * 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * - Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * - Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * - Neither the name of the Motorola, Inc. nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 package javax.obex; 36 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.io.DataInputStream; 41 import java.io.DataOutputStream; 42 import java.io.ByteArrayOutputStream; 43 44 import android.util.Log; 45 46 /** 47 * This class implements the <code>Operation</code> interface. It will read and 48 * write data via puts and gets. 49 * @hide 50 */ 51 public final class ClientOperation implements Operation, BaseStream { 52 53 private static final String TAG = "ClientOperation"; 54 55 private static final boolean V = ObexHelper.VDBG; 56 57 private ClientSession mParent; 58 59 private boolean mInputOpen; 60 61 private PrivateInputStream mPrivateInput; 62 63 private boolean mPrivateInputOpen; 64 65 private PrivateOutputStream mPrivateOutput; 66 67 private boolean mPrivateOutputOpen; 68 69 private String mExceptionMessage; 70 71 private int mMaxPacketSize; 72 73 private boolean mOperationDone; 74 75 private boolean mGetOperation; 76 77 private boolean mGetFinalFlag; 78 79 private HeaderSet mRequestHeader; 80 81 private HeaderSet mReplyHeader; 82 83 private boolean mEndOfBodySent; 84 85 private boolean mSendBodyHeader = true; 86 // A latch - when triggered, there is not way back ;-) 87 private boolean mSrmActive = false; 88 89 // Assume SRM disabled - until support is confirmed 90 // by the server 91 private boolean mSrmEnabled = false; 92 // keep waiting until final-bit is received in request 93 // to handle the case where the SRM enable header is in 94 // a different OBEX packet than the SRMP header. 95 private boolean mSrmWaitingForRemote = true; 96 97 98 /** 99 * Creates new OperationImpl to read and write data to a server 100 * @param maxSize the maximum packet size 101 * @param p the parent to this object 102 * @param type <code>true</code> if this is a get request; 103 * <code>false</code. if this is a put request 104 * @param header the header to set in the initial request 105 * @throws IOException if the an IO error occurred 106 */ 107 public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type) 108 throws IOException { 109 110 mParent = p; 111 mEndOfBodySent = false; 112 mInputOpen = true; 113 mOperationDone = false; 114 mMaxPacketSize = maxSize; 115 mGetOperation = type; 116 mGetFinalFlag = false; 117 118 mPrivateInputOpen = false; 119 mPrivateOutputOpen = false; 120 mPrivateInput = null; 121 mPrivateOutput = null; 122 123 mReplyHeader = new HeaderSet(); 124 125 mRequestHeader = new HeaderSet(); 126 127 int[] headerList = header.getHeaderList(); 128 129 if (headerList != null) { 130 131 for (int i = 0; i < headerList.length; i++) { 132 mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i])); 133 } 134 } 135 136 if ((header).mAuthChall != null) { 137 mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length]; 138 System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0, 139 (header).mAuthChall.length); 140 } 141 142 if ((header).mAuthResp != null) { 143 mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length]; 144 System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0, 145 (header).mAuthResp.length); 146 147 } 148 149 if ((header).mConnectionID != null) { 150 mRequestHeader.mConnectionID = new byte[4]; 151 System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0, 152 4); 153 154 } 155 } 156 157 /** 158 * Allows to set flag which will force GET to be always sent as single packet request with 159 * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which 160 * require requests to be sent this way. 161 */ 162 public void setGetFinalFlag(boolean flag) { 163 mGetFinalFlag = flag; 164 } 165 166 /** 167 * Sends an ABORT message to the server. By calling this method, the 168 * corresponding input and output streams will be closed along with this 169 * object. 170 * @throws IOException if the transaction has already ended or if an OBEX 171 * server called this method 172 */ 173 public synchronized void abort() throws IOException { 174 ensureOpen(); 175 //no compatible with sun-ri 176 if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) { 177 throw new IOException("Operation has already ended"); 178 } 179 180 mExceptionMessage = "Operation aborted"; 181 if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 182 mOperationDone = true; 183 /* 184 * Since we are not sending any headers or returning any headers then 185 * we just need to write and read the same bytes 186 */ 187 mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false); 188 189 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) { 190 throw new IOException("Invalid response code from server"); 191 } 192 193 mExceptionMessage = null; 194 } 195 196 close(); 197 } 198 199 /** 200 * Retrieves the response code retrieved from the server. Response codes are 201 * defined in the <code>ResponseCodes</code> interface. 202 * @return the response code retrieved from the server 203 * @throws IOException if an error occurred in the transport layer during 204 * the transaction; if this method is called on a 205 * <code>HeaderSet</code> object created by calling 206 * <code>createHeaderSet</code> in a <code>ClientSession</code> 207 * object 208 */ 209 public synchronized int getResponseCode() throws IOException { 210 if ((mReplyHeader.responseCode == -1) 211 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 212 validateConnection(); 213 } 214 215 return mReplyHeader.responseCode; 216 } 217 218 /** 219 * This method will always return <code>null</code> 220 * @return <code>null</code> 221 */ 222 public String getEncoding() { 223 return null; 224 } 225 226 /** 227 * Returns the type of content that the resource connected to is providing. 228 * E.g. if the connection is via HTTP, then the value of the content-type 229 * header field is returned. 230 * @return the content type of the resource that the URL references, or 231 * <code>null</code> if not known 232 */ 233 public String getType() { 234 try { 235 return (String)mReplyHeader.getHeader(HeaderSet.TYPE); 236 } catch (IOException e) { 237 if(V) Log.d(TAG, "Exception occured - returning null",e); 238 return null; 239 } 240 } 241 242 /** 243 * Returns the length of the content which is being provided. E.g. if the 244 * connection is via HTTP, then the value of the content-length header field 245 * is returned. 246 * @return the content length of the resource that this connection's URL 247 * references, or -1 if the content length is not known 248 */ 249 public long getLength() { 250 try { 251 Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH); 252 253 if (temp == null) { 254 return -1; 255 } else { 256 return temp.longValue(); 257 } 258 } catch (IOException e) { 259 if(V) Log.d(TAG,"Exception occured - returning -1",e); 260 return -1; 261 } 262 } 263 264 /** 265 * Open and return an input stream for a connection. 266 * @return an input stream 267 * @throws IOException if an I/O error occurs 268 */ 269 public InputStream openInputStream() throws IOException { 270 271 ensureOpen(); 272 273 if (mPrivateInputOpen) 274 throw new IOException("no more input streams available"); 275 if (mGetOperation) { 276 // send the GET request here 277 validateConnection(); 278 } else { 279 if (mPrivateInput == null) { 280 mPrivateInput = new PrivateInputStream(this); 281 } 282 } 283 284 mPrivateInputOpen = true; 285 286 return mPrivateInput; 287 } 288 289 /** 290 * Open and return a data input stream for a connection. 291 * @return an input stream 292 * @throws IOException if an I/O error occurs 293 */ 294 public DataInputStream openDataInputStream() throws IOException { 295 return new DataInputStream(openInputStream()); 296 } 297 298 /** 299 * Open and return an output stream for a connection. 300 * @return an output stream 301 * @throws IOException if an I/O error occurs 302 */ 303 public OutputStream openOutputStream() throws IOException { 304 305 ensureOpen(); 306 ensureNotDone(); 307 308 if (mPrivateOutputOpen) 309 throw new IOException("no more output streams available"); 310 311 if (mPrivateOutput == null) { 312 // there are 3 bytes operation headers and 3 bytes body headers // 313 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize()); 314 } 315 316 mPrivateOutputOpen = true; 317 318 return mPrivateOutput; 319 } 320 321 public int getMaxPacketSize() { 322 return mMaxPacketSize - 6 - getHeaderLength(); 323 } 324 325 public int getHeaderLength() { 326 // OPP may need it 327 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 328 return headerArray.length; 329 } 330 331 /** 332 * Open and return a data output stream for a connection. 333 * @return an output stream 334 * @throws IOException if an I/O error occurs 335 */ 336 public DataOutputStream openDataOutputStream() throws IOException { 337 return new DataOutputStream(openOutputStream()); 338 } 339 340 /** 341 * Closes the connection and ends the transaction 342 * @throws IOException if the operation has already ended or is closed 343 */ 344 public void close() throws IOException { 345 mInputOpen = false; 346 mPrivateInputOpen = false; 347 mPrivateOutputOpen = false; 348 mParent.setRequestInactive(); 349 } 350 351 /** 352 * Returns the headers that have been received during the operation. 353 * Modifying the object returned has no effect on the headers that are sent 354 * or retrieved. 355 * @return the headers received during this <code>Operation</code> 356 * @throws IOException if this <code>Operation</code> has been closed 357 */ 358 public HeaderSet getReceivedHeader() throws IOException { 359 ensureOpen(); 360 361 return mReplyHeader; 362 } 363 364 /** 365 * Specifies the headers that should be sent in the next OBEX message that 366 * is sent. 367 * @param headers the headers to send in the next message 368 * @throws IOException if this <code>Operation</code> has been closed or the 369 * transaction has ended and no further messages will be exchanged 370 * @throws IllegalArgumentException if <code>headers</code> was not created 371 * by a call to <code>ServerRequestHandler.createHeaderSet()</code> 372 * @throws NullPointerException if <code>headers</code> is <code>null</code> 373 */ 374 public void sendHeaders(HeaderSet headers) throws IOException { 375 ensureOpen(); 376 if (mOperationDone) { 377 throw new IOException("Operation has already exchanged all data"); 378 } 379 380 if (headers == null) { 381 throw new IOException("Headers may not be null"); 382 } 383 384 int[] headerList = headers.getHeaderList(); 385 if (headerList != null) { 386 for (int i = 0; i < headerList.length; i++) { 387 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i])); 388 } 389 } 390 } 391 392 /** 393 * Verifies that additional information may be sent. In other words, the 394 * operation is not done. 395 * @throws IOException if the operation is completed 396 */ 397 public void ensureNotDone() throws IOException { 398 if (mOperationDone) { 399 throw new IOException("Operation has completed"); 400 } 401 } 402 403 /** 404 * Verifies that the connection is open and no exceptions should be thrown. 405 * @throws IOException if an exception needs to be thrown 406 */ 407 public void ensureOpen() throws IOException { 408 mParent.ensureOpen(); 409 410 if (mExceptionMessage != null) { 411 throw new IOException(mExceptionMessage); 412 } 413 if (!mInputOpen) { 414 throw new IOException("Operation has already ended"); 415 } 416 } 417 418 /** 419 * Verifies that the connection is open and the proper data has been read. 420 * @throws IOException if an IO error occurs 421 */ 422 private void validateConnection() throws IOException { 423 ensureOpen(); 424 425 // Make sure that a response has been recieved from remote 426 // before continuing 427 if (mPrivateInput == null || mReplyHeader.responseCode == -1) { 428 startProcessing(); 429 } 430 } 431 432 /** 433 * Sends a request to the client of the specified type. 434 * This function will enable SRM and set SRM active if the server 435 * response allows this. 436 * @param opCode the request code to send to the client 437 * @return <code>true</code> if there is more data to send; 438 * <code>false</code> if there is no more data to send 439 * @throws IOException if an IO error occurs 440 */ 441 private boolean sendRequest(int opCode) throws IOException { 442 boolean returnValue = false; 443 ByteArrayOutputStream out = new ByteArrayOutputStream(); 444 int bodyLength = -1; 445 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true); 446 if (mPrivateOutput != null) { 447 bodyLength = mPrivateOutput.size(); 448 } 449 450 /* 451 * Determine if there is space to add a body request. At present 452 * this method checks to see if there is room for at least a 17 453 * byte body header. This number needs to be at least 6 so that 454 * there is room for the header ID and length and the reply ID and 455 * length, but it is a waste of resources if we can't send much of 456 * the body. 457 */ 458 final int MINIMUM_BODY_LENGTH = 3; 459 if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH) 460 > mMaxPacketSize) { 461 int end = 0; 462 int start = 0; 463 // split & send the headerArray in multiple packets. 464 465 while (end != headerArray.length) { 466 //split the headerArray 467 468 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize 469 - ObexHelper.BASE_PACKET_LENGTH); 470 // can not split 471 if (end == -1) { 472 mOperationDone = true; 473 abort(); 474 mExceptionMessage = "Header larger then can be sent in a packet"; 475 mInputOpen = false; 476 477 if (mPrivateInput != null) { 478 mPrivateInput.close(); 479 } 480 481 if (mPrivateOutput != null) { 482 mPrivateOutput.close(); 483 } 484 throw new IOException("OBEX Packet exceeds max packet size"); 485 } 486 487 byte[] sendHeader = new byte[end - start]; 488 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length); 489 if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) { 490 return false; 491 } 492 493 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 494 return false; 495 } 496 497 start = end; 498 } 499 500 // Enable SRM if it should be enabled 501 checkForSrm(); 502 503 if (bodyLength > 0) { 504 return true; 505 } else { 506 return false; 507 } 508 } else { 509 /* All headers will fit into a single package */ 510 if(mSendBodyHeader == false) { 511 /* As we are not to send any body data, set the FINAL_BIT */ 512 opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK; 513 } 514 out.write(headerArray); 515 } 516 517 if (bodyLength > 0) { 518 /* 519 * Determine if we can send the whole body or just part of 520 * the body. Remember that there is the 3 bytes for the 521 * response message and 3 bytes for the header ID and length 522 */ 523 if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) { 524 returnValue = true; 525 526 bodyLength = mMaxPacketSize - headerArray.length - 6; 527 } 528 529 byte[] body = mPrivateOutput.readBytes(bodyLength); 530 531 /* 532 * Since this is a put request if the final bit is set or 533 * the output stream is closed we need to send the 0x49 534 * (End of Body) otherwise, we need to send 0x48 (Body) 535 */ 536 if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent) 537 && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) { 538 out.write(HeaderSet.END_OF_BODY); 539 mEndOfBodySent = true; 540 } else { 541 out.write(HeaderSet.BODY); 542 } 543 544 bodyLength += 3; 545 out.write((byte)(bodyLength >> 8)); 546 out.write((byte)bodyLength); 547 548 if (body != null) { 549 out.write(body); 550 } 551 } 552 553 if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) { 554 // only 0x82 or 0x83 can send 0x49 555 if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) { 556 out.write(HeaderSet.BODY); 557 } else { 558 out.write(HeaderSet.END_OF_BODY); 559 mEndOfBodySent = true; 560 } 561 562 bodyLength = 3; 563 out.write((byte)(bodyLength >> 8)); 564 out.write((byte)bodyLength); 565 } 566 567 if (out.size() == 0) { 568 if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) { 569 return false; 570 } 571 // Enable SRM if it should be enabled 572 checkForSrm(); 573 return returnValue; 574 } 575 if ((out.size() > 0) 576 && (!mParent.sendRequest(opCode, out.toByteArray(), 577 mReplyHeader, mPrivateInput, mSrmActive))) { 578 return false; 579 } 580 // Enable SRM if it should be enabled 581 checkForSrm(); 582 583 // send all of the output data in 0x48, 584 // send 0x49 with empty body 585 if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0)) 586 returnValue = true; 587 588 return returnValue; 589 } 590 591 private void checkForSrm() throws IOException { 592 Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE); 593 if(mParent.isSrmSupported() == true && srmMode != null 594 && srmMode == ObexHelper.OBEX_SRM_ENABLE) { 595 mSrmEnabled = true; 596 } 597 /** 598 * Call this only when a complete obex packet have been received. 599 * (This is not optimal, but the current design is not really suited to 600 * the way SRM is specified.) 601 * The BT usage of SRM is not really safe - it assumes that the SRMP will fit 602 * into every OBEX packet, hence if another header occupies the entire packet, 603 * the scheme will not work - unlikely though. 604 */ 605 if(mSrmEnabled) { 606 mSrmWaitingForRemote = false; 607 Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER); 608 if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) { 609 mSrmWaitingForRemote = true; 610 // Clear the wait header, as the absence of the header in the next packet 611 // indicates don't wait anymore. 612 mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null); 613 } 614 } 615 if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) { 616 mSrmActive = true; 617 } 618 } 619 620 /** 621 * This method starts the processing thread results. It will send the 622 * initial request. If the response takes more then one packet, a thread 623 * will be started to handle additional requests 624 * @throws IOException if an IO error occurs 625 */ 626 private synchronized void startProcessing() throws IOException { 627 628 if (mPrivateInput == null) { 629 mPrivateInput = new PrivateInputStream(this); 630 } 631 boolean more = true; 632 633 if (mGetOperation) { 634 if (!mOperationDone) { 635 if (!mGetFinalFlag) { 636 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 637 while ((more) && (mReplyHeader.responseCode == 638 ResponseCodes.OBEX_HTTP_CONTINUE)) { 639 more = sendRequest(ObexHelper.OBEX_OPCODE_GET); 640 } 641 // For GET we need to loop until all headers have been sent, 642 // And then we wait for the first continue package with the 643 // reply. 644 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 645 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, 646 null, mReplyHeader, mPrivateInput, mSrmActive); 647 } 648 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 649 mOperationDone = true; 650 } else { 651 checkForSrm(); 652 } 653 } else { 654 more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL); 655 656 if (more) { 657 throw new IOException("FINAL_GET forced, data didn't fit into one packet"); 658 } 659 660 mOperationDone = true; 661 } 662 } 663 } else { 664 // PUT operation 665 if (!mOperationDone) { 666 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 667 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 668 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT); 669 } 670 } 671 672 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 673 mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL, 674 null, mReplyHeader, mPrivateInput, mSrmActive); 675 } 676 677 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 678 mOperationDone = true; 679 } 680 } 681 } 682 683 /** 684 * Continues the operation since there is no data to read. 685 * @param sendEmpty <code>true</code> if the operation should send an empty 686 * packet or not send anything if there is no data to send 687 * @param inStream <code>true</code> if the stream is input stream or is 688 * output stream 689 * @throws IOException if an IO error occurs 690 */ 691 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream) 692 throws IOException { 693 694 // One path to the first put operation - the other one does not need to 695 // handle SRM, as all will fit into one packet. 696 697 if (mGetOperation) { 698 if ((inStream) && (!mOperationDone)) { 699 // to deal with inputstream in get operation 700 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, 701 null, mReplyHeader, mPrivateInput, mSrmActive); 702 /* 703 * Determine if that was not the last packet in the operation 704 */ 705 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 706 mOperationDone = true; 707 } else { 708 checkForSrm(); 709 } 710 711 return true; 712 713 } else if ((!inStream) && (!mOperationDone)) { 714 // to deal with outputstream in get operation 715 716 if (mPrivateInput == null) { 717 mPrivateInput = new PrivateInputStream(this); 718 } 719 720 if (!mGetFinalFlag) { 721 sendRequest(ObexHelper.OBEX_OPCODE_GET); 722 } else { 723 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL); 724 } 725 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 726 mOperationDone = true; 727 } 728 return true; 729 730 } else if (mOperationDone) { 731 return false; 732 } 733 734 } else { 735 // PUT operation 736 if ((!inStream) && (!mOperationDone)) { 737 // to deal with outputstream in put operation 738 if (mReplyHeader.responseCode == -1) { 739 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 740 } 741 sendRequest(ObexHelper.OBEX_OPCODE_PUT); 742 return true; 743 } else if ((inStream) && (!mOperationDone)) { 744 // How to deal with inputstream in put operation ? 745 return false; 746 747 } else if (mOperationDone) { 748 return false; 749 } 750 751 } 752 return false; 753 } 754 755 /** 756 * Called when the output or input stream is closed. 757 * @param inStream <code>true</code> if the input stream is closed; 758 * <code>false</code> if the output stream is closed 759 * @throws IOException if an IO error occurs 760 */ 761 public void streamClosed(boolean inStream) throws IOException { 762 if (!mGetOperation) { 763 if ((!inStream) && (!mOperationDone)) { 764 // to deal with outputstream in put operation 765 766 boolean more = true; 767 768 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 769 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 770 if (headerArray.length <= 0) 771 more = false; 772 } 773 // If have not sent any data so send all now 774 if (mReplyHeader.responseCode == -1) { 775 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 776 } 777 778 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 779 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT); 780 } 781 782 /* 783 * According to the IrOBEX specification, after the final put, you 784 * only have a single reply to send. so we don't need the while 785 * loop. 786 */ 787 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 788 789 sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL); 790 } 791 mOperationDone = true; 792 } else if ((inStream) && (mOperationDone)) { 793 // how to deal with input stream in put stream ? 794 mOperationDone = true; 795 } 796 } else { 797 if ((inStream) && (!mOperationDone)) { 798 799 // to deal with inputstream in get operation 800 // Have not sent any data so send it all now 801 802 if (mReplyHeader.responseCode == -1) { 803 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 804 } 805 806 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { 807 if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) { 808 break; 809 } 810 } 811 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { 812 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null, 813 mReplyHeader, mPrivateInput, false); 814 // Regardless of the SRM state, wait for the response. 815 } 816 mOperationDone = true; 817 } else if ((!inStream) && (!mOperationDone)) { 818 // to deal with outputstream in get operation 819 // part of the data may have been sent in continueOperation. 820 821 boolean more = true; 822 823 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 824 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 825 if (headerArray.length <= 0) 826 more = false; 827 } 828 829 if (mPrivateInput == null) { 830 mPrivateInput = new PrivateInputStream(this); 831 } 832 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) 833 more = false; 834 835 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 836 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 837 more = sendRequest(ObexHelper.OBEX_OPCODE_GET); 838 } 839 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL); 840 // parent.sendRequest(0x83, null, replyHeaders, privateInput); 841 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 842 mOperationDone = true; 843 } 844 } 845 } 846 } 847 848 public void noBodyHeader(){ 849 mSendBodyHeader = false; 850 } 851 } 852