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