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