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 34package javax.obex; 35 36import java.io.IOException; 37import java.io.InputStream; 38import java.io.OutputStream; 39import java.io.DataInputStream; 40import java.io.DataOutputStream; 41import java.io.ByteArrayOutputStream; 42 43/** 44 * This class implements the <code>Operation</code> interface. It will read and 45 * write data via puts and gets. 46 * @hide 47 */ 48public final class ClientOperation implements Operation, BaseStream { 49 50 private ClientSession mParent; 51 52 private boolean mInputOpen; 53 54 private PrivateInputStream mPrivateInput; 55 56 private boolean mPrivateInputOpen; 57 58 private PrivateOutputStream mPrivateOutput; 59 60 private boolean mPrivateOutputOpen; 61 62 private String mExceptionMessage; 63 64 private int mMaxPacketSize; 65 66 private boolean mOperationDone; 67 68 private boolean mGetOperation; 69 70 private boolean mGetFinalFlag; 71 72 private HeaderSet mRequestHeader; 73 74 private HeaderSet mReplyHeader; 75 76 private boolean mEndOfBodySent; 77 78 /** 79 * Creates new OperationImpl to read and write data to a server 80 * @param maxSize the maximum packet size 81 * @param p the parent to this object 82 * @param type <code>true</code> if this is a get request; 83 * <code>false</code. if this is a put request 84 * @param header the header to set in the initial request 85 * @throws IOException if the an IO error occurred 86 */ 87 public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type) 88 throws IOException { 89 90 mParent = p; 91 mEndOfBodySent = false; 92 mInputOpen = true; 93 mOperationDone = false; 94 mMaxPacketSize = maxSize; 95 mGetOperation = type; 96 mGetFinalFlag = false; 97 98 mPrivateInputOpen = false; 99 mPrivateOutputOpen = false; 100 mPrivateInput = null; 101 mPrivateOutput = null; 102 103 mReplyHeader = new HeaderSet(); 104 105 mRequestHeader = new HeaderSet(); 106 107 int[] headerList = header.getHeaderList(); 108 109 if (headerList != null) { 110 111 for (int i = 0; i < headerList.length; i++) { 112 mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i])); 113 } 114 } 115 116 if ((header).mAuthChall != null) { 117 mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length]; 118 System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0, 119 (header).mAuthChall.length); 120 } 121 122 if ((header).mAuthResp != null) { 123 mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length]; 124 System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0, 125 (header).mAuthResp.length); 126 127 } 128 129 if ((header).mConnectionID != null) { 130 mRequestHeader.mConnectionID = new byte[4]; 131 System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0, 132 4); 133 134 } 135 } 136 137 /** 138 * Allows to set flag which will force GET to be always sent as single packet request with 139 * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which 140 * require requests to be sent this way. 141 */ 142 public void setGetFinalFlag(boolean flag) { 143 mGetFinalFlag = flag; 144 } 145 146 /** 147 * Sends an ABORT message to the server. By calling this method, the 148 * corresponding input and output streams will be closed along with this 149 * object. 150 * @throws IOException if the transaction has already ended or if an OBEX 151 * server called this method 152 */ 153 public synchronized void abort() throws IOException { 154 ensureOpen(); 155 //no compatible with sun-ri 156 if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) { 157 throw new IOException("Operation has already ended"); 158 } 159 160 mExceptionMessage = "Operation aborted"; 161 if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 162 mOperationDone = true; 163 /* 164 * Since we are not sending any headers or returning any headers then 165 * we just need to write and read the same bytes 166 */ 167 mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null); 168 169 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) { 170 throw new IOException("Invalid response code from server"); 171 } 172 173 mExceptionMessage = null; 174 } 175 176 close(); 177 } 178 179 /** 180 * Retrieves the response code retrieved from the server. Response codes are 181 * defined in the <code>ResponseCodes</code> interface. 182 * @return the response code retrieved from the server 183 * @throws IOException if an error occurred in the transport layer during 184 * the transaction; if this method is called on a 185 * <code>HeaderSet</code> object created by calling 186 * <code>createHeaderSet</code> in a <code>ClientSession</code> 187 * object 188 */ 189 public synchronized int getResponseCode() throws IOException { 190 //avoid dup validateConnection 191 if ((mReplyHeader.responseCode == -1) 192 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 193 validateConnection(); 194 } 195 196 return mReplyHeader.responseCode; 197 } 198 199 /** 200 * This method will always return <code>null</code> 201 * @return <code>null</code> 202 */ 203 public String getEncoding() { 204 return null; 205 } 206 207 /** 208 * Returns the type of content that the resource connected to is providing. 209 * E.g. if the connection is via HTTP, then the value of the content-type 210 * header field is returned. 211 * @return the content type of the resource that the URL references, or 212 * <code>null</code> if not known 213 */ 214 public String getType() { 215 try { 216 return (String)mReplyHeader.getHeader(HeaderSet.TYPE); 217 } catch (IOException e) { 218 return null; 219 } 220 } 221 222 /** 223 * Returns the length of the content which is being provided. E.g. if the 224 * connection is via HTTP, then the value of the content-length header field 225 * is returned. 226 * @return the content length of the resource that this connection's URL 227 * references, or -1 if the content length is not known 228 */ 229 public long getLength() { 230 try { 231 Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH); 232 233 if (temp == null) { 234 return -1; 235 } else { 236 return temp.longValue(); 237 } 238 } catch (IOException e) { 239 return -1; 240 } 241 } 242 243 /** 244 * Open and return an input stream for a connection. 245 * @return an input stream 246 * @throws IOException if an I/O error occurs 247 */ 248 public InputStream openInputStream() throws IOException { 249 250 ensureOpen(); 251 252 if (mPrivateInputOpen) 253 throw new IOException("no more input streams available"); 254 if (mGetOperation) { 255 // send the GET request here 256 validateConnection(); 257 } else { 258 if (mPrivateInput == null) { 259 mPrivateInput = new PrivateInputStream(this); 260 } 261 } 262 263 mPrivateInputOpen = true; 264 265 return mPrivateInput; 266 } 267 268 /** 269 * Open and return a data input stream for a connection. 270 * @return an input stream 271 * @throws IOException if an I/O error occurs 272 */ 273 public DataInputStream openDataInputStream() throws IOException { 274 return new DataInputStream(openInputStream()); 275 } 276 277 /** 278 * Open and return an output stream for a connection. 279 * @return an output stream 280 * @throws IOException if an I/O error occurs 281 */ 282 public OutputStream openOutputStream() throws IOException { 283 284 ensureOpen(); 285 ensureNotDone(); 286 287 if (mPrivateOutputOpen) 288 throw new IOException("no more output streams available"); 289 290 if (mPrivateOutput == null) { 291 // there are 3 bytes operation headers and 3 bytes body headers // 292 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize()); 293 } 294 295 mPrivateOutputOpen = true; 296 297 return mPrivateOutput; 298 } 299 300 public int getMaxPacketSize() { 301 return mMaxPacketSize - 6 - getHeaderLength(); 302 } 303 304 public int getHeaderLength() { 305 // OPP may need it 306 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 307 return headerArray.length; 308 } 309 310 /** 311 * Open and return a data output stream for a connection. 312 * @return an output stream 313 * @throws IOException if an I/O error occurs 314 */ 315 public DataOutputStream openDataOutputStream() throws IOException { 316 return new DataOutputStream(openOutputStream()); 317 } 318 319 /** 320 * Closes the connection and ends the transaction 321 * @throws IOException if the operation has already ended or is closed 322 */ 323 public void close() throws IOException { 324 mInputOpen = false; 325 mPrivateInputOpen = false; 326 mPrivateOutputOpen = false; 327 mParent.setRequestInactive(); 328 } 329 330 /** 331 * Returns the headers that have been received during the operation. 332 * Modifying the object returned has no effect on the headers that are sent 333 * or retrieved. 334 * @return the headers received during this <code>Operation</code> 335 * @throws IOException if this <code>Operation</code> has been closed 336 */ 337 public HeaderSet getReceivedHeader() throws IOException { 338 ensureOpen(); 339 340 return mReplyHeader; 341 } 342 343 /** 344 * Specifies the headers that should be sent in the next OBEX message that 345 * is sent. 346 * @param headers the headers to send in the next message 347 * @throws IOException if this <code>Operation</code> has been closed or the 348 * transaction has ended and no further messages will be exchanged 349 * @throws IllegalArgumentException if <code>headers</code> was not created 350 * by a call to <code>ServerRequestHandler.createHeaderSet()</code> 351 * @throws NullPointerException if <code>headers</code> is <code>null</code> 352 */ 353 public void sendHeaders(HeaderSet headers) throws IOException { 354 ensureOpen(); 355 if (mOperationDone) { 356 throw new IOException("Operation has already exchanged all data"); 357 } 358 359 if (headers == null) { 360 throw new IOException("Headers may not be null"); 361 } 362 363 int[] headerList = headers.getHeaderList(); 364 if (headerList != null) { 365 for (int i = 0; i < headerList.length; i++) { 366 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i])); 367 } 368 } 369 } 370 371 /** 372 * Verifies that additional information may be sent. In other words, the 373 * operation is not done. 374 * @throws IOException if the operation is completed 375 */ 376 public void ensureNotDone() throws IOException { 377 if (mOperationDone) { 378 throw new IOException("Operation has completed"); 379 } 380 } 381 382 /** 383 * Verifies that the connection is open and no exceptions should be thrown. 384 * @throws IOException if an exception needs to be thrown 385 */ 386 public void ensureOpen() throws IOException { 387 mParent.ensureOpen(); 388 389 if (mExceptionMessage != null) { 390 throw new IOException(mExceptionMessage); 391 } 392 if (!mInputOpen) { 393 throw new IOException("Operation has already ended"); 394 } 395 } 396 397 /** 398 * Verifies that the connection is open and the proper data has been read. 399 * @throws IOException if an IO error occurs 400 */ 401 private void validateConnection() throws IOException { 402 ensureOpen(); 403 404 // to sure only one privateInput object exist. 405 if (mPrivateInput == null) { 406 startProcessing(); 407 } 408 } 409 410 /** 411 * Sends a request to the client of the specified type 412 * @param opCode the request code to send to the client 413 * @return <code>true</code> if there is more data to send; 414 * <code>false</code> if there is no more data to send 415 * @throws IOException if an IO error occurs 416 */ 417 private boolean sendRequest(int opCode) throws IOException { 418 boolean returnValue = false; 419 ByteArrayOutputStream out = new ByteArrayOutputStream(); 420 int bodyLength = -1; 421 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true); 422 if (mPrivateOutput != null) { 423 bodyLength = mPrivateOutput.size(); 424 } 425 426 /* 427 * Determine if there is space to add a body request. At present 428 * this method checks to see if there is room for at least a 17 429 * byte body header. This number needs to be at least 6 so that 430 * there is room for the header ID and length and the reply ID and 431 * length, but it is a waste of resources if we can't send much of 432 * the body. 433 */ 434 if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketSize) { 435 int end = 0; 436 int start = 0; 437 // split & send the headerArray in multiple packets. 438 439 while (end != headerArray.length) { 440 //split the headerArray 441 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize 442 - ObexHelper.BASE_PACKET_LENGTH); 443 // can not split 444 if (end == -1) { 445 mOperationDone = true; 446 abort(); 447 mExceptionMessage = "Header larger then can be sent in a packet"; 448 mInputOpen = false; 449 450 if (mPrivateInput != null) { 451 mPrivateInput.close(); 452 } 453 454 if (mPrivateOutput != null) { 455 mPrivateOutput.close(); 456 } 457 throw new IOException("OBEX Packet exceeds max packet size"); 458 } 459 460 byte[] sendHeader = new byte[end - start]; 461 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length); 462 if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) { 463 return false; 464 } 465 466 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 467 return false; 468 } 469 470 start = end; 471 } 472 473 if (bodyLength > 0) { 474 return true; 475 } else { 476 return false; 477 } 478 } else { 479 out.write(headerArray); 480 } 481 482 if (bodyLength > 0) { 483 /* 484 * Determine if we can send the whole body or just part of 485 * the body. Remember that there is the 3 bytes for the 486 * response message and 3 bytes for the header ID and length 487 */ 488 if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) { 489 returnValue = true; 490 491 bodyLength = mMaxPacketSize - headerArray.length - 6; 492 } 493 494 byte[] body = mPrivateOutput.readBytes(bodyLength); 495 496 /* 497 * Since this is a put request if the final bit is set or 498 * the output stream is closed we need to send the 0x49 499 * (End of Body) otherwise, we need to send 0x48 (Body) 500 */ 501 if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent) 502 && ((opCode & 0x80) != 0)) { 503 out.write(0x49); 504 mEndOfBodySent = true; 505 } else { 506 out.write(0x48); 507 } 508 509 bodyLength += 3; 510 out.write((byte)(bodyLength >> 8)); 511 out.write((byte)bodyLength); 512 513 if (body != null) { 514 out.write(body); 515 } 516 } 517 518 if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) { 519 // only 0x82 or 0x83 can send 0x49 520 if ((opCode & 0x80) == 0) { 521 out.write(0x48); 522 } else { 523 out.write(0x49); 524 mEndOfBodySent = true; 525 526 } 527 528 bodyLength = 3; 529 out.write((byte)(bodyLength >> 8)); 530 out.write((byte)bodyLength); 531 } 532 533 if (out.size() == 0) { 534 if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) { 535 return false; 536 } 537 return returnValue; 538 } 539 if ((out.size() > 0) 540 && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) { 541 return false; 542 } 543 544 // send all of the output data in 0x48, 545 // send 0x49 with empty body 546 if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0)) 547 returnValue = true; 548 549 return returnValue; 550 } 551 552 /** 553 * This method starts the processing thread results. It will send the 554 * initial request. If the response takes more then one packet, a thread 555 * will be started to handle additional requests 556 * @throws IOException if an IO error occurs 557 */ 558 private synchronized void startProcessing() throws IOException { 559 560 if (mPrivateInput == null) { 561 mPrivateInput = new PrivateInputStream(this); 562 } 563 boolean more = true; 564 565 if (mGetOperation) { 566 if (!mOperationDone) { 567 if (!mGetFinalFlag) { 568 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 569 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 570 more = sendRequest(0x03); 571 } 572 573 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 574 mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput); 575 } 576 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 577 mOperationDone = true; 578 } 579 } else { 580 more = sendRequest(0x83); 581 582 if (more) { 583 throw new IOException("FINAL_GET forced but data did not fit into single packet!"); 584 } 585 586 mOperationDone = true; 587 } 588 } 589 } else { 590 591 if (!mOperationDone) { 592 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 593 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 594 more = sendRequest(0x02); 595 596 } 597 } 598 599 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 600 mParent.sendRequest(0x82, null, mReplyHeader, mPrivateInput); 601 } 602 603 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 604 mOperationDone = true; 605 } 606 } 607 } 608 609 /** 610 * Continues the operation since there is no data to read. 611 * @param sendEmpty <code>true</code> if the operation should send an empty 612 * packet or not send anything if there is no data to send 613 * @param inStream <code>true</code> if the stream is input stream or is 614 * output stream 615 * @throws IOException if an IO error occurs 616 */ 617 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream) 618 throws IOException { 619 620 if (mGetOperation) { 621 if ((inStream) && (!mOperationDone)) { 622 // to deal with inputstream in get operation 623 mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput); 624 /* 625 * Determine if that was not the last packet in the operation 626 */ 627 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 628 mOperationDone = true; 629 } 630 631 return true; 632 633 } else if ((!inStream) && (!mOperationDone)) { 634 // to deal with outputstream in get operation 635 636 if (mPrivateInput == null) { 637 mPrivateInput = new PrivateInputStream(this); 638 } 639 640 if (!mGetFinalFlag) { 641 sendRequest(0x03); 642 } else { 643 sendRequest(0x83); 644 645 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 646 mOperationDone = true; 647 } 648 } 649 return true; 650 651 } else if (mOperationDone) { 652 return false; 653 } 654 655 } else { 656 if ((!inStream) && (!mOperationDone)) { 657 // to deal with outputstream in put operation 658 if (mReplyHeader.responseCode == -1) { 659 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 660 } 661 sendRequest(0x02); 662 return true; 663 } else if ((inStream) && (!mOperationDone)) { 664 // How to deal with inputstream in put operation ? 665 return false; 666 667 } else if (mOperationDone) { 668 return false; 669 } 670 671 } 672 return false; 673 } 674 675 /** 676 * Called when the output or input stream is closed. 677 * @param inStream <code>true</code> if the input stream is closed; 678 * <code>false</code> if the output stream is closed 679 * @throws IOException if an IO error occurs 680 */ 681 public void streamClosed(boolean inStream) throws IOException { 682 if (!mGetOperation) { 683 if ((!inStream) && (!mOperationDone)) { 684 // to deal with outputstream in put operation 685 686 boolean more = true; 687 688 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 689 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 690 if (headerArray.length <= 0) 691 more = false; 692 } 693 // If have not sent any data so send all now 694 if (mReplyHeader.responseCode == -1) { 695 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 696 } 697 698 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 699 more = sendRequest(0x02); 700 } 701 702 /* 703 * According to the IrOBEX specification, after the final put, you 704 * only have a single reply to send. so we don't need the while 705 * loop. 706 */ 707 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 708 709 sendRequest(0x82); 710 } 711 mOperationDone = true; 712 } else if ((inStream) && (mOperationDone)) { 713 // how to deal with input stream in put stream ? 714 mOperationDone = true; 715 } 716 } else { 717 if ((inStream) && (!mOperationDone)) { 718 719 // to deal with inputstream in get operation 720 // Have not sent any data so send it all now 721 722 if (mReplyHeader.responseCode == -1) { 723 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 724 } 725 726 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 727 if (!sendRequest(0x83)) { 728 break; 729 } 730 } 731 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { 732 mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput); 733 } 734 mOperationDone = true; 735 } else if ((!inStream) && (!mOperationDone)) { 736 // to deal with outputstream in get operation 737 // part of the data may have been sent in continueOperation. 738 739 boolean more = true; 740 741 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) { 742 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); 743 if (headerArray.length <= 0) 744 more = false; 745 } 746 747 if (mPrivateInput == null) { 748 mPrivateInput = new PrivateInputStream(this); 749 } 750 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) 751 more = false; 752 753 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; 754 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) { 755 more = sendRequest(0x03); 756 } 757 sendRequest(0x83); 758 // parent.sendRequest(0x83, null, replyHeaders, privateInput); 759 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) { 760 mOperationDone = true; 761 } 762 } 763 } 764 } 765 766 public void noBodyHeader(){ 767 } 768} 769