1 /* Copyright (c) 2015 The Android Open Source Project 2 * Copyright (C) 2015 Samsung LSI 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 com.android.obex; 35 36 import android.util.Log; 37 38 import java.io.ByteArrayOutputStream; 39 import java.io.DataInputStream; 40 import java.io.DataOutputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.OutputStream; 44 45 /** 46 * This class implements the Operation interface for server side connections. 47 * 48 * <p><STRONG>Request Codes</STRONG> There are four different request codes that are in this class. 49 * 0x02 is a PUT request that signals that the request is not complete and requires an additional 50 * OBEX packet. 0x82 is a PUT request that says that request is complete. In this case, the server 51 * can begin sending the response. The 0x03 is a GET request that signals that the request is not 52 * finished. When the server receives a 0x83, the client is signaling the server that it is done 53 * with its request. TODO: Extend the ClientOperation and reuse the methods defined TODO: in that 54 * class. 55 */ 56 public final class ServerOperation implements Operation, BaseStream { 57 58 private static final String TAG = "ServerOperation"; 59 60 private static final boolean V = ObexHelper.VDBG; // Verbose debugging 61 62 private boolean mAborted; 63 64 /** @hide */ 65 public HeaderSet requestHeader; 66 67 /** @hide */ 68 public HeaderSet replyHeader; 69 70 /** @hide */ 71 public boolean finalBitSet; 72 73 private InputStream mInput; 74 75 private ServerSession mParent; 76 77 private int mMaxPacketLength; 78 79 private int mResponseSize; 80 81 private boolean mClosed; 82 83 private boolean mGetOperation; 84 85 private PrivateInputStream mPrivateInput; 86 87 private PrivateOutputStream mPrivateOutput; 88 89 private ObexTransport mTransport; 90 91 private boolean mPrivateOutputOpen; 92 93 private String mExceptionString; 94 95 private ServerRequestHandler mListener; 96 97 private boolean mRequestFinished; 98 99 private boolean mHasBody; 100 101 private boolean mSendBodyHeader = true; 102 // Assume SRM disabled - needs to be explicit 103 // enabled by client 104 private boolean mSrmEnabled = false; 105 // A latch - when triggered, there is not way back ;-) 106 private boolean mSrmActive = false; 107 // Set to true when a SRM enable response have been send 108 private boolean mSrmResponseSent = false; 109 // keep waiting until final-bit is received in request 110 // to handle the case where the SRM enable header is in 111 // a different OBEX packet than the SRMP header. 112 private boolean mSrmWaitingForRemote = true; 113 // Why should we wait? - currently not exposed to apps. 114 private boolean mSrmLocalWait = false; 115 116 /** 117 * Creates new ServerOperation 118 * 119 * @param p the parent that created this object 120 * @param in the input stream to read from 121 * @param request the initial request that was received from the client 122 * @param maxSize the max packet size that the client will accept 123 * @param listen the listener that is responding to the request 124 * @throws IOException if an IO error occurs 125 * @hide 126 */ ServerOperation( ServerSession p, InputStream in, int request, int maxSize, ServerRequestHandler listen)127 public ServerOperation( 128 ServerSession p, InputStream in, int request, int maxSize, ServerRequestHandler listen) 129 throws IOException { 130 131 mAborted = false; 132 mParent = p; 133 mInput = in; 134 mMaxPacketLength = maxSize; 135 mClosed = false; 136 requestHeader = new HeaderSet(); 137 replyHeader = new HeaderSet(); 138 mPrivateInput = new PrivateInputStream(this); 139 mResponseSize = 3; 140 mListener = listen; 141 mRequestFinished = false; 142 mPrivateOutputOpen = false; 143 mHasBody = false; 144 ObexPacket packet; 145 mTransport = p.getTransport(); 146 147 /* 148 * Determine if this is a PUT request 149 */ 150 if ((request == ObexHelper.OBEX_OPCODE_PUT) 151 || (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) { 152 /* 153 * It is a PUT request. 154 */ 155 mGetOperation = false; 156 157 /* 158 * Determine if the final bit is set 159 */ 160 if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) { 161 finalBitSet = false; 162 } else { 163 finalBitSet = true; 164 mRequestFinished = true; 165 } 166 } else if ((request == ObexHelper.OBEX_OPCODE_GET) 167 || (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) { 168 /* 169 * It is a GET request. 170 */ 171 mGetOperation = true; 172 173 // For Get request, final bit set is decided by server side logic 174 finalBitSet = false; 175 176 if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) { 177 mRequestFinished = true; 178 } 179 } else { 180 throw new IOException("ServerOperation can not handle such request"); 181 } 182 183 packet = ObexPacket.read(request, mInput); 184 185 /* 186 * Determine if the packet length is larger than this device can receive 187 */ 188 if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) { 189 mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null); 190 throw new IOException( 191 "Packet received was too large. Length: " 192 + packet.mLength 193 + " maxLength: " 194 + ObexHelper.getMaxRxPacketSize(mTransport)); 195 } 196 197 /* 198 * Determine if any headers were sent in the initial request 199 */ 200 if (packet.mLength > 3) { 201 if (!handleObexPacket(packet)) { 202 return; 203 } 204 /* Don't Pre-Send continue when Remote requested for SRM 205 * Let the Application confirm. 206 */ 207 if (V) 208 Log.v( 209 TAG, 210 "Get App confirmation if SRM ENABLED case: " 211 + mSrmEnabled 212 + " not hasBody case: " 213 + mHasBody); 214 if (!mHasBody && !mSrmEnabled) { 215 while ((!mGetOperation) && (!finalBitSet)) { 216 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 217 if (mPrivateInput.available() > 0) { 218 break; 219 } 220 } 221 } 222 } 223 /* Don't Pre-Send continue when Remote requested for SRM 224 * Let the Application confirm. 225 */ 226 if (V) 227 Log.v( 228 TAG, 229 "Get App confirmation if SRM ENABLED case: " 230 + mSrmEnabled 231 + " not finalPacket: " 232 + finalBitSet 233 + " not GETOp Case: " 234 + mGetOperation); 235 while ((!mSrmEnabled) 236 && (!mGetOperation) 237 && (!finalBitSet) 238 && (mPrivateInput.available() == 0)) { 239 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 240 if (mPrivateInput.available() > 0) { 241 break; 242 } 243 } 244 245 // wait for get request finished !!!! 246 while (mGetOperation && !mRequestFinished) { 247 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 248 } 249 } 250 251 /** 252 * Parse headers and update member variables 253 * 254 * @param packet the received obex packet 255 * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED response have been 256 * send. Else true. 257 */ handleObexPacket(ObexPacket packet)258 private boolean handleObexPacket(ObexPacket packet) throws IOException { 259 byte[] body = updateRequestHeaders(packet); 260 261 if (body != null) { 262 mHasBody = true; 263 } 264 if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) { 265 mListener.setConnectionId(ObexHelper.convertToLong(requestHeader.mConnectionID)); 266 } else { 267 mListener.setConnectionId(1); 268 } 269 270 if (requestHeader.mAuthResp != null) { 271 if (!mParent.handleAuthResp(requestHeader.mAuthResp)) { 272 mExceptionString = "Authentication Failed"; 273 mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null); 274 mClosed = true; 275 requestHeader.mAuthResp = null; 276 return false; 277 } 278 requestHeader.mAuthResp = null; 279 } 280 281 if (requestHeader.mAuthChall != null) { 282 mParent.handleAuthChall(requestHeader); 283 // send the auhtResp to the client 284 replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length]; 285 System.arraycopy( 286 requestHeader.mAuthResp, 287 0, 288 replyHeader.mAuthResp, 289 0, 290 replyHeader.mAuthResp.length); 291 requestHeader.mAuthResp = null; 292 requestHeader.mAuthChall = null; 293 } 294 295 if (body != null) { 296 mPrivateInput.writeBytes(body, 1); 297 } 298 return true; 299 } 300 301 /** 302 * Update the request header set, and sniff on SRM headers to update local state. 303 * 304 * @param packet the OBEX packet data 305 * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet} 306 */ updateRequestHeaders(ObexPacket packet)307 private byte[] updateRequestHeaders(ObexPacket packet) throws IOException { 308 byte[] body = null; 309 if (packet.mPayload != null) { 310 body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload); 311 } 312 Byte srmMode = (Byte) requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE); 313 if (mTransport.isSrmSupported() 314 && srmMode != null 315 && srmMode == ObexHelper.OBEX_SRM_ENABLE) { 316 mSrmEnabled = true; 317 if (V) Log.d(TAG, "SRM is now ENABLED (but not active) for this operation"); 318 } 319 checkForSrmWait(packet.mHeaderId); 320 if ((!mSrmWaitingForRemote) && (mSrmEnabled)) { 321 if (V) Log.d(TAG, "SRM is now ACTIVE for this operation"); 322 mSrmActive = true; 323 } 324 return body; 325 } 326 327 /** 328 * Call this only when a complete request have been received. (This is not optimal, but the 329 * current design is not really suited to the way SRM is specified.) 330 */ checkForSrmWait(int headerId)331 private void checkForSrmWait(int headerId) { 332 if (mSrmEnabled 333 && (headerId == ObexHelper.OBEX_OPCODE_GET 334 || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL 335 || headerId == ObexHelper.OBEX_OPCODE_PUT)) { 336 mSrmWaitingForRemote = false; 337 Byte srmp = (Byte) requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER); 338 if (srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) { 339 mSrmWaitingForRemote = true; 340 // Clear the wait header, as the absents of the header when the final bit is set 341 // indicates don't wait. 342 requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null); 343 } 344 } 345 } 346 347 /** @hide */ isValidBody()348 public boolean isValidBody() { 349 return mHasBody; 350 } 351 352 /** 353 * Determines if the operation should continue or should wait. If it should continue, this 354 * method will continue the operation. 355 * 356 * @param sendEmpty if <code>true</code> then this will continue the operation even if no 357 * headers will be sent; if <code>false</code> then this method will only continue the 358 * operation if there are headers to send 359 * @param inStream if<code>true</code> the stream is input stream, otherwise output stream 360 * @return <code>true</code> if the operation was completed; <code>false</code> if no operation 361 * took place 362 * @hide 363 */ continueOperation(boolean sendEmpty, boolean inStream)364 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream) 365 throws IOException { 366 if (!mGetOperation) { 367 if (!finalBitSet) { 368 if (sendEmpty) { 369 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 370 return true; 371 } else { 372 if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) { 373 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 374 return true; 375 } else { 376 return false; 377 } 378 } 379 } else { 380 return false; 381 } 382 } else { 383 sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); 384 return true; 385 } 386 } 387 388 /** 389 * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it will wait for a 390 * response from the client before ending unless SRM is active. 391 * 392 * @param type the response code to send back to the client 393 * @return <code>true</code> if the final bit was not set on the reply; <code>false</code> if no 394 * reply was received because the operation ended, an abort was received, the final bit was 395 * set in the reply or SRM is active. 396 * @throws IOException if an IO error occurs 397 * @hide 398 */ sendReply(int type)399 public synchronized boolean sendReply(int type) throws IOException { 400 ByteArrayOutputStream out = new ByteArrayOutputStream(); 401 boolean skipSend = false; 402 boolean skipReceive = false; 403 boolean srmRespSendPending = false; 404 405 long id = mListener.getConnectionId(); 406 if (id == -1) { 407 replyHeader.mConnectionID = null; 408 } else { 409 replyHeader.mConnectionID = ObexHelper.convertToByteArray(id); 410 } 411 412 if (mSrmEnabled && !mSrmResponseSent) { 413 // As we are not ensured that the SRM enable is in the first OBEX packet 414 // We must check for each reply. 415 if (V) Log.v(TAG, "mSrmEnabled==true, sending SRM enable response."); 416 replyHeader.setHeader( 417 HeaderSet.SINGLE_RESPONSE_MODE, (byte) ObexHelper.OBEX_SRM_ENABLE); 418 srmRespSendPending = true; 419 } 420 421 if (mSrmEnabled && !mGetOperation && mSrmLocalWait) { 422 replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte) ObexHelper.OBEX_SRMP_WAIT); 423 } 424 425 byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers 426 int bodyLength = -1; 427 int originalBodyLength = -1; 428 429 if (mPrivateOutput != null) { 430 bodyLength = mPrivateOutput.size(); 431 originalBodyLength = bodyLength; 432 } 433 434 if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) { 435 436 int end = 0; 437 int start = 0; 438 439 while (end != headerArray.length) { 440 end = 441 ObexHelper.findHeaderEnd( 442 headerArray, 443 start, 444 mMaxPacketLength - ObexHelper.BASE_PACKET_LENGTH); 445 if (end == -1) { 446 447 mClosed = true; 448 449 if (mPrivateInput != null) { 450 mPrivateInput.close(); 451 } 452 453 if (mPrivateOutput != null) { 454 mPrivateOutput.close(); 455 } 456 mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 457 throw new IOException("OBEX Packet exceeds max packet size"); 458 } 459 byte[] sendHeader = new byte[end - start]; 460 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length); 461 462 mParent.sendResponse(type, sendHeader); 463 start = end; 464 } 465 466 if (bodyLength > 0) { 467 return true; 468 } else { 469 return false; 470 } 471 472 } else { 473 out.write(headerArray); 474 } 475 476 // For Get operation: if response code is OBEX_HTTP_OK, then this is the 477 // last packet; so set finalBitSet to true. 478 if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) { 479 finalBitSet = true; 480 } 481 482 if (mSrmActive) { 483 if (!mGetOperation 484 && type == ResponseCodes.OBEX_HTTP_CONTINUE 485 && mSrmResponseSent == true) { 486 // we are in the middle of a SRM PUT operation, don't send a continue. 487 skipSend = true; 488 } else if (mGetOperation && mRequestFinished == false && mSrmResponseSent == true) { 489 // We are still receiving the get request, receive, but don't send continue. 490 skipSend = true; 491 } else if (mGetOperation && mRequestFinished == true) { 492 // All done receiving the GET request, send data to the client, without 493 // expecting a continue. 494 skipReceive = true; 495 } 496 if (V) 497 Log.v( 498 TAG, 499 "type==" 500 + type 501 + " skipSend==" 502 + skipSend 503 + " skipReceive==" 504 + skipReceive); 505 } 506 if (srmRespSendPending) { 507 if (V) 508 Log.v(TAG, "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response"); 509 mSrmResponseSent = true; 510 } 511 512 if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) { 513 if (bodyLength > 0) { 514 /* 515 * Determine if I can send the whole body or just part of 516 * the body. Remember that there is the 3 bytes for the 517 * response message and 3 bytes for the header ID and length 518 */ 519 if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) { 520 bodyLength = mMaxPacketLength - headerArray.length - 6; 521 } 522 523 byte[] body = mPrivateOutput.readBytes(bodyLength); 524 525 /* 526 * Since this is a put request if the final bit is set or 527 * the output stream is closed we need to send the 0x49 528 * (End of Body) otherwise, we need to send 0x48 (Body) 529 */ 530 if ((finalBitSet) || (mPrivateOutput.isClosed())) { 531 if (mSendBodyHeader == true) { 532 out.write(0x49); 533 bodyLength += 3; 534 out.write((byte) (bodyLength >> 8)); 535 out.write((byte) bodyLength); 536 out.write(body); 537 } 538 } else { 539 if (mSendBodyHeader == true) { 540 out.write(0x48); 541 bodyLength += 3; 542 out.write((byte) (bodyLength >> 8)); 543 out.write((byte) bodyLength); 544 out.write(body); 545 } 546 } 547 } 548 } 549 550 if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (originalBodyLength <= 0)) { 551 if (mSendBodyHeader) { 552 out.write(0x49); 553 originalBodyLength = 3; 554 out.write((byte) (originalBodyLength >> 8)); 555 out.write((byte) originalBodyLength); 556 } 557 } 558 559 if (skipSend == false) { 560 mResponseSize = 3; 561 mParent.sendResponse(type, out.toByteArray()); 562 } 563 564 if (type == ResponseCodes.OBEX_HTTP_CONTINUE) { 565 566 if (mGetOperation && skipReceive) { 567 // Here we need to check for and handle abort (throw an exception). 568 // Any other signal received should be discarded silently (only on server side) 569 checkSrmRemoteAbort(); 570 } else { 571 // Receive and handle data (only send reply if !skipSend) 572 // Read a complete OBEX Packet 573 ObexPacket packet = ObexPacket.read(mInput); 574 575 int headerId = packet.mHeaderId; 576 if ((headerId != ObexHelper.OBEX_OPCODE_PUT) 577 && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL) 578 && (headerId != ObexHelper.OBEX_OPCODE_GET) 579 && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) { 580 581 /* 582 * Determine if an ABORT was sent as the reply 583 */ 584 if (headerId == ObexHelper.OBEX_OPCODE_ABORT) { 585 handleRemoteAbort(); 586 } else { 587 // TODO:shall we send this if it occurs during SRM? Errata on the subject 588 mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null); 589 mClosed = true; 590 mExceptionString = "Bad Request Received"; 591 throw new IOException("Bad Request Received"); 592 } 593 } else { 594 595 if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) { 596 finalBitSet = true; 597 } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) { 598 mRequestFinished = true; 599 } 600 601 /* 602 * Determine if the packet length is larger than the negotiated packet size 603 */ 604 if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) { 605 mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null); 606 throw new IOException("Packet received was too large"); 607 } 608 609 /* 610 * Determine if any headers were sent in the initial request 611 */ 612 if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) { 613 if (handleObexPacket(packet) == false) { 614 return false; 615 } 616 } 617 } 618 } 619 return true; 620 } else { 621 return false; 622 } 623 } 624 625 /** 626 * This method will look for an abort from the peer during a SRM transfer. The function will not 627 * block if no data has been received from the remote device. If data have been received, the 628 * function will block while reading the incoming OBEX package. An Abort request will be 629 * handled, and cause an IOException("Abort Received"). Other messages will be discarded 630 * silently as per GOEP specification. 631 * 632 * @throws IOException if an abort request have been received. TODO: I think this is an error in 633 * the specification. If we discard other messages, the peer device will most likely stall, 634 * as it will not receive the expected response for the message... I'm not sure how to 635 * understand "Receipt of invalid or unexpected SRM or SRMP header values shall be ignored 636 * by the receiving device." If any signal is received during an active SRM transfer it is 637 * unexpected regardless whether or not it contains SRM/SRMP headers... 638 */ checkSrmRemoteAbort()639 private void checkSrmRemoteAbort() throws IOException { 640 if (mInput.available() > 0) { 641 ObexPacket packet = ObexPacket.read(mInput); 642 /* 643 * Determine if an ABORT was sent as the reply 644 */ 645 if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) { 646 handleRemoteAbort(); 647 } else { 648 // TODO: should we throw an exception here anyway? - don't see how to 649 // ignore SRM/SRMP headers without ignoring the complete signal 650 // (in this particular case). 651 Log.w( 652 TAG, 653 "Received unexpected request from client - discarding...\n" 654 + " headerId: " 655 + packet.mHeaderId 656 + " length: " 657 + packet.mLength); 658 } 659 } 660 } 661 handleRemoteAbort()662 private void handleRemoteAbort() throws IOException { 663 /* TODO: To increase the speed of the abort operation in SRM, we need 664 * to be able to flush the L2CAP queue for the PSM in use. 665 * This could be implemented by introducing a control 666 * message to be send over the socket, that in the abort case 667 * could carry a flush command. */ 668 mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null); 669 mClosed = true; 670 mAborted = true; 671 mExceptionString = "Abort Received"; 672 throw new IOException("Abort Received"); 673 } 674 675 /** 676 * Sends an ABORT message to the server. By calling this method, the corresponding input and 677 * output streams will be closed along with this object. 678 * 679 * @throws IOException if the transaction has already ended or if an OBEX server called this 680 * method 681 * @hide 682 */ abort()683 public void abort() throws IOException { 684 throw new IOException("Called from a server"); 685 } 686 687 /** 688 * Returns the headers that have been received during the operation. Modifying the object 689 * returned has no effect on the headers that are sent or retrieved. 690 * 691 * @return the headers received during this <code>Operation</code> 692 * @throws IOException if this <code>Operation</code> has been closed 693 * @hide 694 */ getReceivedHeader()695 public HeaderSet getReceivedHeader() throws IOException { 696 ensureOpen(); 697 return requestHeader; 698 } 699 700 /** 701 * Specifies the headers that should be sent in the next OBEX message that is sent. 702 * 703 * @param headers the headers to send in the next message 704 * @throws IOException if this <code>Operation</code> has been closed or the transaction has 705 * ended and no further messages will be exchanged 706 * @throws IllegalArgumentException if <code>headers</code> was not created by a call to <code> 707 * ServerRequestHandler.createHeaderSet()</code> 708 * @hide 709 */ sendHeaders(HeaderSet headers)710 public void sendHeaders(HeaderSet headers) throws IOException { 711 ensureOpen(); 712 713 if (headers == null) { 714 throw new IOException("Headers may not be null"); 715 } 716 717 int[] headerList = headers.getHeaderList(); 718 if (headerList != null) { 719 for (int i = 0; i < headerList.length; i++) { 720 replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i])); 721 } 722 } 723 } 724 725 /** 726 * Retrieves the response code retrieved from the server. Response codes are defined in the 727 * <code>ResponseCodes</code> interface. 728 * 729 * @return the response code retrieved from the server 730 * @throws IOException if an error occurred in the transport layer during the transaction; if 731 * this method is called on a <code>HeaderSet</code> object created by calling <code> 732 * createHeaderSet</code> in a <code>ClientSession</code> object; if this is called from a 733 * server 734 * @hide 735 */ getResponseCode()736 public int getResponseCode() throws IOException { 737 throw new IOException("Called from a server"); 738 } 739 740 /** @hide */ getMaxPacketSize()741 public int getMaxPacketSize() { 742 return mMaxPacketLength - 6 - getHeaderLength(); 743 } 744 745 /** @hide */ getHeaderLength()746 public int getHeaderLength() { 747 long id = mListener.getConnectionId(); 748 if (id == -1) { 749 replyHeader.mConnectionID = null; 750 } else { 751 replyHeader.mConnectionID = ObexHelper.convertToByteArray(id); 752 } 753 754 byte[] headerArray = ObexHelper.createHeader(replyHeader, false); 755 756 return headerArray.length; 757 } 758 759 /** 760 * Open and return an input stream for a connection. 761 * 762 * @return an input stream 763 * @throws IOException if an I/O error occurs 764 * @hide 765 */ openInputStream()766 public InputStream openInputStream() throws IOException { 767 ensureOpen(); 768 return mPrivateInput; 769 } 770 771 /** 772 * Open and return a data input stream for a connection. 773 * 774 * @return an input stream 775 * @throws IOException if an I/O error occurs 776 * @hide 777 */ openDataInputStream()778 public DataInputStream openDataInputStream() throws IOException { 779 return new DataInputStream(openInputStream()); 780 } 781 782 /** 783 * Open and return an output stream for a connection. 784 * 785 * @return an output stream 786 * @throws IOException if an I/O error occurs 787 * @hide 788 */ openOutputStream()789 public OutputStream openOutputStream() throws IOException { 790 ensureOpen(); 791 792 if (mPrivateOutputOpen) { 793 throw new IOException("no more input streams available, stream already opened"); 794 } 795 796 if (!mRequestFinished) { 797 throw new IOException("no output streams available ,request not finished"); 798 } 799 800 if (mPrivateOutput == null) { 801 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize()); 802 } 803 mPrivateOutputOpen = true; 804 return mPrivateOutput; 805 } 806 807 /** 808 * Open and return a data output stream for a connection. 809 * 810 * @return an output stream 811 * @throws IOException if an I/O error occurs 812 * @hide 813 */ openDataOutputStream()814 public DataOutputStream openDataOutputStream() throws IOException { 815 return new DataOutputStream(openOutputStream()); 816 } 817 818 /** 819 * Closes the connection and ends the transaction 820 * 821 * @throws IOException if the operation has already ended or is closed 822 * @hide 823 */ close()824 public void close() throws IOException { 825 ensureOpen(); 826 mClosed = true; 827 } 828 829 /** 830 * Verifies that the connection is open and no exceptions should be thrown. 831 * 832 * @throws IOException if an exception needs to be thrown 833 * @hide 834 */ ensureOpen()835 public void ensureOpen() throws IOException { 836 if (mExceptionString != null) { 837 throw new IOException(mExceptionString); 838 } 839 if (mClosed) { 840 throw new IOException("Operation has already ended"); 841 } 842 } 843 844 /** 845 * Verifies that additional information may be sent. In other words, the operation is not done. 846 * 847 * <p>Included to implement the BaseStream interface only. It does not do anything on the server 848 * side since the operation of the Operation object is not done until after the handler returns 849 * from its method. 850 * 851 * @throws IOException if the operation is completed 852 * @hide 853 */ ensureNotDone()854 public void ensureNotDone() throws IOException {} 855 856 /** 857 * Called when the output or input stream is closed. It does not do anything on the server side 858 * since the operation of the Operation object is not done until after the handler returns from 859 * its method. 860 * 861 * @param inStream <code>true</code> if the input stream is closed; <code>false</code> if the 862 * output stream is closed 863 * @throws IOException if an IO error occurs 864 * @hide 865 */ streamClosed(boolean inStream)866 public void streamClosed(boolean inStream) throws IOException {} 867 868 /** @hide */ noBodyHeader()869 public void noBodyHeader() { 870 mSendBodyHeader = false; 871 } 872 873 /** Returns whether the operation is aborted. */ isAborted()874 public boolean isAborted() { 875 return mAborted; 876 } 877 878 /** 879 * Set whether the operation is aborted. 880 * 881 * @param aborted {@code true} if the operation is aborted, {@code false} otherwise 882 */ setAborted(boolean aborted)883 public void setAborted(boolean aborted) { 884 this.mAborted = aborted; 885 } 886 } 887