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