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 */ ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)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 */ setGetFinalFlag(boolean flag)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 */ abort()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 */ getResponseCode()209 public synchronized int getResponseCode() throws IOException { 210 //avoid dup validateConnection 211 if ((mReplyHeader.responseCode == -1) 212 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 213 validateConnection(); 214 } 215 216 return mReplyHeader.responseCode; 217 } 218 219 /** 220 * This method will always return <code>null</code> 221 * @return <code>null</code> 222 */ getEncoding()223 public String getEncoding() { 224 return null; 225 } 226 227 /** 228 * Returns the type of content that the resource connected to is providing. 229 * E.g. if the connection is via HTTP, then the value of the content-type 230 * header field is returned. 231 * @return the content type of the resource that the URL references, or 232 * <code>null</code> if not known 233 */ getType()234 public String getType() { 235 try { 236 return (String)mReplyHeader.getHeader(HeaderSet.TYPE); 237 } catch (IOException e) { 238 if(V) Log.d(TAG, "Exception occured - returning null",e); 239 return null; 240 } 241 } 242 243 /** 244 * Returns the length of the content which is being provided. E.g. if the 245 * connection is via HTTP, then the value of the content-length header field 246 * is returned. 247 * @return the content length of the resource that this connection's URL 248 * references, or -1 if the content length is not known 249 */ getLength()250 public long getLength() { 251 try { 252 Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH); 253 254 if (temp == null) { 255 return -1; 256 } else { 257 return temp.longValue(); 258 } 259 } catch (IOException e) { 260 if(V) Log.d(TAG,"Exception occured - returning -1",e); 261 return -1; 262 } 263 } 264 265 /** 266 * Open and return an input stream for a connection. 267 * @return an input stream 268 * @throws IOException if an I/O error occurs 269 */ openInputStream()270 public InputStream openInputStream() throws IOException { 271 272 ensureOpen(); 273 274 if (mPrivateInputOpen) 275 throw new IOException("no more input streams available"); 276 if (mGetOperation) { 277 // send the GET request here 278 validateConnection(); 279 } else { 280 if (mPrivateInput == null) { 281 mPrivateInput = new PrivateInputStream(this); 282 } 283 } 284 285 mPrivateInputOpen = true; 286 287 return mPrivateInput; 288 } 289 290 /** 291 * Open and return a data input stream for a connection. 292 * @return an input stream 293 * @throws IOException if an I/O error occurs 294 */ openDataInputStream()295 public DataInputStream openDataInputStream() throws IOException { 296 return new DataInputStream(openInputStream()); 297 } 298 299 /** 300 * Open and return an output stream for a connection. 301 * @return an output stream 302 * @throws IOException if an I/O error occurs 303 */ openOutputStream()304 public OutputStream openOutputStream() throws IOException { 305 306 ensureOpen(); 307 ensureNotDone(); 308 309 if (mPrivateOutputOpen) 310 throw new IOException("no more output streams available"); 311 312 if (mPrivateOutput == null) { 313 // there are 3 bytes operation headers and 3 bytes body headers // 314 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize()); 315 } 316 317 mPrivateOutputOpen = true; 318 319 return mPrivateOutput; 320 } 321 getMaxPacketSize()322 public int getMaxPacketSize() { 323 return mMaxPacketSize - 6 - getHeaderLength(); 324 } 325 getHeaderLength()326 public int getHeaderLength() { 327 // OPP may need it 328 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 329 return headerArray.length; 330 } 331 332 /** 333 * Open and return a data output stream for a connection. 334 * @return an output stream 335 * @throws IOException if an I/O error occurs 336 */ openDataOutputStream()337 public DataOutputStream openDataOutputStream() throws IOException { 338 return new DataOutputStream(openOutputStream()); 339 } 340 341 /** 342 * Closes the connection and ends the transaction 343 * @throws IOException if the operation has already ended or is closed 344 */ close()345 public void close() throws IOException { 346 mInputOpen = false; 347 mPrivateInputOpen = false; 348 mPrivateOutputOpen = false; 349 mParent.setRequestInactive(); 350 } 351 352 /** 353 * Returns the headers that have been received during the operation. 354 * Modifying the object returned has no effect on the headers that are sent 355 * or retrieved. 356 * @return the headers received during this <code>Operation</code> 357 * @throws IOException if this <code>Operation</code> has been closed 358 */ getReceivedHeader()359 public HeaderSet getReceivedHeader() throws IOException { 360 ensureOpen(); 361 362 return mReplyHeader; 363 } 364 365 /** 366 * Specifies the headers that should be sent in the next OBEX message that 367 * is sent. 368 * @param headers the headers to send in the next message 369 * @throws IOException if this <code>Operation</code> has been closed or the 370 * transaction has ended and no further messages will be exchanged 371 * @throws IllegalArgumentException if <code>headers</code> was not created 372 * by a call to <code>ServerRequestHandler.createHeaderSet()</code> 373 * @throws NullPointerException if <code>headers</code> is <code>null</code> 374 */ sendHeaders(HeaderSet headers)375 public void sendHeaders(HeaderSet headers) throws IOException { 376 ensureOpen(); 377 if (mOperationDone) { 378 throw new IOException("Operation has already exchanged all data"); 379 } 380 381 if (headers == null) { 382 throw new IOException("Headers may not be null"); 383 } 384 385 int[] headerList = headers.getHeaderList(); 386 if (headerList != null) { 387 for (int i = 0; i < headerList.length; i++) { 388 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i])); 389 } 390 } 391 } 392 393 /** 394 * Verifies that additional information may be sent. In other words, the 395 * operation is not done. 396 * @throws IOException if the operation is completed 397 */ ensureNotDone()398 public void ensureNotDone() throws IOException { 399 if (mOperationDone) { 400 throw new IOException("Operation has completed"); 401 } 402 } 403 404 /** 405 * Verifies that the connection is open and no exceptions should be thrown. 406 * @throws IOException if an exception needs to be thrown 407 */ ensureOpen()408 public void ensureOpen() throws IOException { 409 mParent.ensureOpen(); 410 411 if (mExceptionMessage != null) { 412 throw new IOException(mExceptionMessage); 413 } 414 if (!mInputOpen) { 415 throw new IOException("Operation has already ended"); 416 } 417 } 418 419 /** 420 * Verifies that the connection is open and the proper data has been read. 421 * @throws IOException if an IO error occurs 422 */ validateConnection()423 private void validateConnection() throws IOException { 424 ensureOpen(); 425 426 // to sure only one privateInput object exist. 427 if (mPrivateInput == null) { 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 */ sendRequest(int opCode)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 checkForSrm()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 */ startProcessing()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 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 636 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 637 more = sendRequest(ObexHelper.OBEX_OPCODE_GET); 638 } 639 // For GET we need to loop until all headers have been sent, 640 // And then we wait for the first continue package with the 641 // reply. 642 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 643 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, 644 null, mReplyHeader, mPrivateInput, mSrmActive); 645 } 646 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 647 mOperationDone = true; 648 } else { 649 checkForSrm(); 650 } 651 } 652 } else { 653 // PUT operation 654 if (!mOperationDone) { 655 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 656 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 657 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT); 658 } 659 } 660 661 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 662 mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL, 663 null, mReplyHeader, mPrivateInput, mSrmActive); 664 } 665 666 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 667 mOperationDone = true; 668 } 669 } 670 } 671 672 /** 673 * Continues the operation since there is no data to read. 674 * @param sendEmpty <code>true</code> if the operation should send an empty 675 * packet or not send anything if there is no data to send 676 * @param inStream <code>true</code> if the stream is input stream or is 677 * output stream 678 * @throws IOException if an IO error occurs 679 */ continueOperation(boolean sendEmpty, boolean inStream)680 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream) 681 throws IOException { 682 683 // One path to the first put operation - the other one does not need to 684 // handle SRM, as all will fit into one packet. 685 686 if (mGetOperation) { 687 if ((inStream) && (!mOperationDone)) { 688 // to deal with inputstream in get operation 689 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, 690 null, mReplyHeader, mPrivateInput, mSrmActive); 691 /* 692 * Determine if that was not the last packet in the operation 693 */ 694 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 695 mOperationDone = true; 696 } else { 697 checkForSrm(); 698 } 699 700 return true; 701 702 } else if ((!inStream) && (!mOperationDone)) { 703 // to deal with outputstream in get operation 704 705 if (mPrivateInput == null) { 706 mPrivateInput = new PrivateInputStream(this); 707 } 708 sendRequest(ObexHelper.OBEX_OPCODE_GET); 709 return true; 710 711 } else if (mOperationDone) { 712 return false; 713 } 714 715 } else { 716 // PUT operation 717 if ((!inStream) && (!mOperationDone)) { 718 // to deal with outputstream in put operation 719 if (mReplyHeader.responseCode == -1) { 720 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 721 } 722 sendRequest(ObexHelper.OBEX_OPCODE_PUT); 723 return true; 724 } else if ((inStream) && (!mOperationDone)) { 725 // How to deal with inputstream in put operation ? 726 return false; 727 728 } else if (mOperationDone) { 729 return false; 730 } 731 732 } 733 return false; 734 } 735 736 /** 737 * Called when the output or input stream is closed. 738 * @param inStream <code>true</code> if the input stream is closed; 739 * <code>false</code> if the output stream is closed 740 * @throws IOException if an IO error occurs 741 */ streamClosed(boolean inStream)742 public void streamClosed(boolean inStream) throws IOException { 743 if (!mGetOperation) { 744 if ((!inStream) && (!mOperationDone)) { 745 // to deal with outputstream in put operation 746 747 boolean more = true; 748 749 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 750 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 751 if (headerArray.length <= 0) 752 more = false; 753 } 754 // If have not sent any data so send all now 755 if (mReplyHeader.responseCode == -1) { 756 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 757 } 758 759 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 760 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT); 761 } 762 763 /* 764 * According to the IrOBEX specification, after the final put, you 765 * only have a single reply to send. so we don't need the while 766 * loop. 767 */ 768 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 769 770 sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL); 771 } 772 mOperationDone = true; 773 } else if ((inStream) && (mOperationDone)) { 774 // how to deal with input stream in put stream ? 775 mOperationDone = true; 776 } 777 } else { 778 if ((inStream) && (!mOperationDone)) { 779 780 // to deal with inputstream in get operation 781 // Have not sent any data so send it all now 782 783 if (mReplyHeader.responseCode == -1) { 784 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 785 } 786 787 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { 788 if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) { 789 break; 790 } 791 } 792 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { 793 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null, 794 mReplyHeader, mPrivateInput, false); 795 // Regardless of the SRM state, wait for the response. 796 } 797 mOperationDone = true; 798 } else if ((!inStream) && (!mOperationDone)) { 799 // to deal with outputstream in get operation 800 // part of the data may have been sent in continueOperation. 801 802 boolean more = true; 803 804 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 805 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 806 if (headerArray.length <= 0) 807 more = false; 808 } 809 810 if (mPrivateInput == null) { 811 mPrivateInput = new PrivateInputStream(this); 812 } 813 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) 814 more = false; 815 816 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 817 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 818 more = sendRequest(ObexHelper.OBEX_OPCODE_GET); 819 } 820 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL); 821 // parent.sendRequest(0x83, null, replyHeaders, privateInput); 822 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 823 mOperationDone = true; 824 } 825 } 826 } 827 } 828 noBodyHeader()829 public void noBodyHeader(){ 830 mSendBodyHeader = false; 831 } 832 } 833