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