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             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      */
handleObexPacket(ObexPacket packet)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      */
updateRequestHeaders(ObexPacket packet)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      */
checkForSrmWait(int headerId)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 
isValidBody()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      */
continueOperation(boolean sendEmpty, boolean inStream)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      */
sendReply(int type)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      */
checkSrmRemoteAbort()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 
handleRemoteAbort()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      */
abort()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      */
getReceivedHeader()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      */
sendHeaders(HeaderSet headers)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      */
getResponseCode()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      */
getEncoding()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      */
getType()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      */
getLength()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 
getMaxPacketSize()734     public int getMaxPacketSize() {
735         return mMaxPacketLength - 6 - getHeaderLength();
736     }
737 
getHeaderLength()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      */
openInputStream()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      */
openDataInputStream()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      */
openOutputStream()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      */
openDataOutputStream()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      */
close()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      */
ensureOpen()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      */
ensureNotDone()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      */
streamClosed(boolean inStream)844     public void streamClosed(boolean inStream) throws IOException {
845 
846     }
847 
noBodyHeader()848     public void noBodyHeader(){
849         mSendBodyHeader = false;
850     }
851 }
852