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