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