1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Copyright (c) 2015 Samsung LSI
4 * Copyright (c) 2008-2009, Motorola, Inc.
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * - Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * - Neither the name of the Motorola, Inc. nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35package javax.obex;
36
37import android.util.Log;
38
39import java.io.InputStream;
40import java.io.IOException;
41import java.io.OutputStream;
42
43/**
44 * This class in an implementation of the OBEX ServerSession.
45 * @hide
46 */
47public final class ServerSession extends ObexSession implements Runnable {
48
49    private static final String TAG = "Obex ServerSession";
50    private static final boolean V = ObexHelper.VDBG;
51
52    private ObexTransport mTransport;
53
54    private InputStream mInput;
55
56    private OutputStream mOutput;
57
58    private ServerRequestHandler mListener;
59
60    private Thread mProcessThread;
61
62    private int mMaxPacketLength;
63
64    private boolean mClosed;
65
66    /**
67     * Creates new ServerSession.
68     * @param trans the connection to the client
69     * @param handler the event listener that will process requests
70     * @param auth the authenticator to use with this connection
71     * @throws IOException if an error occurred while opening the input and
72     *         output streams
73     */
74    public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
75            throws IOException {
76        mAuthenticator = auth;
77        mTransport = trans;
78        mInput = mTransport.openInputStream();
79        mOutput = mTransport.openOutputStream();
80        mListener = handler;
81        mMaxPacketLength = 256;
82
83        mClosed = false;
84        mProcessThread = new Thread(this);
85        mProcessThread.start();
86    }
87
88    /**
89     * Processes requests made to the server and forwards them to the
90     * appropriate event listener.
91     */
92    public void run() {
93        try {
94
95            boolean done = false;
96            while (!done && !mClosed) {
97                if(V) Log.v(TAG, "Waiting for incoming request...");
98                int requestType = mInput.read();
99                if(V) Log.v(TAG, "Read request: " + requestType);
100                switch (requestType) {
101                    case ObexHelper.OBEX_OPCODE_CONNECT:
102                        handleConnectRequest();
103                        break;
104
105                    case ObexHelper.OBEX_OPCODE_DISCONNECT:
106                        handleDisconnectRequest();
107                        done = true;
108                        break;
109
110                    case ObexHelper.OBEX_OPCODE_GET:
111                    case ObexHelper.OBEX_OPCODE_GET_FINAL:
112                        handleGetRequest(requestType);
113                        break;
114
115                    case ObexHelper.OBEX_OPCODE_PUT:
116                    case ObexHelper.OBEX_OPCODE_PUT_FINAL:
117                        handlePutRequest(requestType);
118                        break;
119
120                    case ObexHelper.OBEX_OPCODE_SETPATH:
121                        handleSetPathRequest();
122                        break;
123                    case ObexHelper.OBEX_OPCODE_ABORT:
124                        handleAbortRequest();
125                        break;
126
127                    case -1:
128                        done = true;
129                        break;
130
131                    default:
132
133                        /*
134                         * Received a request type that is not recognized so I am
135                         * just going to read the packet and send a not implemented
136                         * to the client
137                         */
138                        int length = mInput.read();
139                        length = (length << 8) + mInput.read();
140                        for (int i = 3; i < length; i++) {
141                            mInput.read();
142                        }
143                        sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
144                }
145            }
146
147        } catch (NullPointerException e) {
148            Log.d(TAG, "Exception occured - ignoring", e);
149        } catch (Exception e) {
150            Log.d(TAG, "Exception occured - ignoring", e);
151        }
152        close();
153    }
154
155    /**
156     * Handles a ABORT request from a client. This method will read the rest of
157     * the request from the client. Assuming the request is valid, it will
158     * create a <code>HeaderSet</code> object to pass to the
159     * <code>ServerRequestHandler</code> object. After the handler processes the
160     * request, this method will create a reply message to send to the server.
161     *
162     * @throws IOException if an error occurred at the transport layer
163     */
164    private void handleAbortRequest() throws IOException {
165        int code = ResponseCodes.OBEX_HTTP_OK;
166        HeaderSet request = new HeaderSet();
167        HeaderSet reply = new HeaderSet();
168
169        int length = mInput.read();
170        length = (length << 8) + mInput.read();
171        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
172            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
173        } else {
174            for (int i = 3; i < length; i++) {
175                mInput.read();
176            }
177            code = mListener.onAbort(request, reply);
178            Log.v(TAG, "onAbort request handler return value- " + code);
179            code = validateResponseCode(code);
180        }
181        sendResponse(code, null);
182    }
183
184    /**
185     * Handles a PUT request from a client. This method will provide a
186     * <code>ServerOperation</code> object to the request handler. The
187     * <code>ServerOperation</code> object will handle the rest of the request.
188     * It will also send replies and receive requests until the final reply
189     * should be sent. When the final reply should be sent, this method will get
190     * the response code to use and send the reply. The
191     * <code>ServerOperation</code> object will always reply with a
192     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
193     * needed.
194     * @param type the type of request received; either 0x02 or 0x82
195     * @throws IOException if an error occurred at the transport layer
196     */
197    private void handlePutRequest(int type) throws IOException {
198        ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
199        try {
200            int response = -1;
201
202            if ((op.finalBitSet) && !op.isValidBody()) {
203                response = validateResponseCode(mListener
204                        .onDelete(op.requestHeader, op.replyHeader));
205            } else {
206                response = validateResponseCode(mListener.onPut(op));
207            }
208            if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
209                op.sendReply(response);
210            } else if (!op.isAborted) {
211                // wait for the final bit
212                while (!op.finalBitSet) {
213                    op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
214                }
215                op.sendReply(response);
216            }
217        } catch (Exception e) {
218            /*To fix bugs in aborted cases,
219             *(client abort file transfer prior to the last packet which has the end of body header,
220             *internal error should not be sent because server has already replied with
221             *OK response in "sendReply")
222             */
223            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
224            if (!op.isAborted) {
225                sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
226            }
227        }
228    }
229
230    /**
231     * Handles a GET request from a client. This method will provide a
232     * <code>ServerOperation</code> object to the request handler. The
233     * <code>ServerOperation</code> object will handle the rest of the request.
234     * It will also send replies and receive requests until the final reply
235     * should be sent. When the final reply should be sent, this method will get
236     * the response code to use and send the reply. The
237     * <code>ServerOperation</code> object will always reply with a
238     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
239     * needed.
240     * @param type the type of request received; either 0x03 or 0x83
241     * @throws IOException if an error occurred at the transport layer
242     */
243    private void handleGetRequest(int type) throws IOException {
244        ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
245        try {
246            int response = validateResponseCode(mListener.onGet(op));
247
248            if (!op.isAborted) {
249                op.sendReply(response);
250            }
251        } catch (Exception e) {
252            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
253            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
254        }
255    }
256
257    /**
258     * Send standard response.
259     * @param code the response code to send
260     * @param header the headers to include in the response
261     * @throws IOException if an IO error occurs
262     */
263    public void sendResponse(int code, byte[] header) throws IOException {
264        int totalLength = 3;
265        byte[] data = null;
266        OutputStream op = mOutput;
267        if (op == null) {
268            return;
269        }
270
271        if (header != null) {
272            totalLength += header.length;
273            data = new byte[totalLength];
274            data[0] = (byte)code;
275            data[1] = (byte)(totalLength >> 8);
276            data[2] = (byte)totalLength;
277            System.arraycopy(header, 0, data, 3, header.length);
278        } else {
279            data = new byte[totalLength];
280            data[0] = (byte)code;
281            data[1] = (byte)0x00;
282            data[2] = (byte)totalLength;
283        }
284        op.write(data);
285        op.flush(); // TODO: Do we need to flush?
286    }
287
288    /**
289     * Handles a SETPATH request from a client. This method will read the rest
290     * of the request from the client. Assuming the request is valid, it will
291     * create a <code>HeaderSet</code> object to pass to the
292     * <code>ServerRequestHandler</code> object. After the handler processes the
293     * request, this method will create a reply message to send to the server
294     * with the response code provided.
295     * @throws IOException if an error occurred at the transport layer
296     */
297    private void handleSetPathRequest() throws IOException {
298        int length;
299        int flags;
300        @SuppressWarnings("unused")
301        int constants;
302        int totalLength = 3;
303        byte[] head = null;
304        int code = -1;
305        int bytesReceived;
306        HeaderSet request = new HeaderSet();
307        HeaderSet reply = new HeaderSet();
308
309        length = mInput.read();
310        length = (length << 8) + mInput.read();
311        flags = mInput.read();
312        constants = mInput.read();
313
314        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
315            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
316            totalLength = 3;
317        } else {
318            if (length > 5) {
319                byte[] headers = new byte[length - 5];
320                bytesReceived = mInput.read(headers);
321
322                while (bytesReceived != headers.length) {
323                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
324                            - bytesReceived);
325                }
326
327                ObexHelper.updateHeaderSet(request, headers);
328
329                if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
330                    mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
331                } else {
332                    mListener.setConnectionId(1);
333                }
334                // the Auth chan is initiated by the server, client sent back the authResp .
335                if (request.mAuthResp != null) {
336                    if (!handleAuthResp(request.mAuthResp)) {
337                        code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
338                        mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
339                                request.mAuthResp));
340                    }
341                    request.mAuthResp = null;
342                }
343            }
344
345            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
346                // the Auth challenge is initiated by the client
347                // the server will send back the authResp to the client
348                if (request.mAuthChall != null) {
349                    handleAuthChall(request);
350                    reply.mAuthResp = new byte[request.mAuthResp.length];
351                    System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
352                            reply.mAuthResp.length);
353                    request.mAuthChall = null;
354                    request.mAuthResp = null;
355                }
356                boolean backup = false;
357                boolean create = true;
358                if (!((flags & 1) == 0)) {
359                    backup = true;
360                }
361                if (!((flags & 2) == 0)) {
362                    create = false;
363                }
364
365                try {
366                    code = mListener.onSetPath(request, reply, backup, create);
367                } catch (Exception e) {
368                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
369                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
370                    return;
371                }
372
373                code = validateResponseCode(code);
374
375                if (reply.nonce != null) {
376                    mChallengeDigest = new byte[16];
377                    System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
378                } else {
379                    mChallengeDigest = null;
380                }
381
382                long id = mListener.getConnectionId();
383                if (id == -1) {
384                    reply.mConnectionID = null;
385                } else {
386                    reply.mConnectionID = ObexHelper.convertToByteArray(id);
387                }
388
389                head = ObexHelper.createHeader(reply, false);
390                totalLength += head.length;
391
392                if (totalLength > mMaxPacketLength) {
393                    totalLength = 3;
394                    head = null;
395                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
396                }
397            }
398        }
399
400        // Compute Length of OBEX SETPATH packet
401        byte[] replyData = new byte[totalLength];
402        replyData[0] = (byte)code;
403        replyData[1] = (byte)(totalLength >> 8);
404        replyData[2] = (byte)totalLength;
405        if (head != null) {
406            System.arraycopy(head, 0, replyData, 3, head.length);
407        }
408        /*
409         * Write the OBEX SETPATH packet to the server. Byte 0: response code
410         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
411         */
412        mOutput.write(replyData);
413        mOutput.flush();
414    }
415
416    /**
417     * Handles a disconnect request from a client. This method will read the
418     * rest of the request from the client. Assuming the request is valid, it
419     * will create a <code>HeaderSet</code> object to pass to the
420     * <code>ServerRequestHandler</code> object. After the handler processes the
421     * request, this method will create a reply message to send to the server.
422     * @throws IOException if an error occurred at the transport layer
423     */
424    private void handleDisconnectRequest() throws IOException {
425        int length;
426        int code = ResponseCodes.OBEX_HTTP_OK;
427        int totalLength = 3;
428        byte[] head = null;
429        int bytesReceived;
430        HeaderSet request = new HeaderSet();
431        HeaderSet reply = new HeaderSet();
432
433        length = mInput.read();
434        length = (length << 8) + mInput.read();
435
436        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
437            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
438            totalLength = 3;
439        } else {
440            if (length > 3) {
441                byte[] headers = new byte[length - 3];
442                bytesReceived = mInput.read(headers);
443
444                while (bytesReceived != headers.length) {
445                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
446                            - bytesReceived);
447                }
448
449                ObexHelper.updateHeaderSet(request, headers);
450            }
451
452            if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
453                mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
454            } else {
455                mListener.setConnectionId(1);
456            }
457
458            if (request.mAuthResp != null) {
459                if (!handleAuthResp(request.mAuthResp)) {
460                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
461                    mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
462                            request.mAuthResp));
463                }
464                request.mAuthResp = null;
465            }
466
467            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
468
469                if (request.mAuthChall != null) {
470                    handleAuthChall(request);
471                    request.mAuthChall = null;
472                }
473
474                try {
475                    mListener.onDisconnect(request, reply);
476                } catch (Exception e) {
477                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
478                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
479                    return;
480                }
481
482                long id = mListener.getConnectionId();
483                if (id == -1) {
484                    reply.mConnectionID = null;
485                } else {
486                    reply.mConnectionID = ObexHelper.convertToByteArray(id);
487                }
488
489                head = ObexHelper.createHeader(reply, false);
490                totalLength += head.length;
491
492                if (totalLength > mMaxPacketLength) {
493                    totalLength = 3;
494                    head = null;
495                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
496                }
497            }
498        }
499
500        // Compute Length of OBEX CONNECT packet
501        byte[] replyData;
502        if (head != null) {
503            replyData = new byte[3 + head.length];
504        } else {
505            replyData = new byte[3];
506        }
507        replyData[0] = (byte)code;
508        replyData[1] = (byte)(totalLength >> 8);
509        replyData[2] = (byte)totalLength;
510        if (head != null) {
511            System.arraycopy(head, 0, replyData, 3, head.length);
512        }
513        /*
514         * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
515         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
516         */
517        mOutput.write(replyData);
518        mOutput.flush();
519    }
520
521    /**
522     * Handles a connect request from a client. This method will read the rest
523     * of the request from the client. Assuming the request is valid, it will
524     * create a <code>HeaderSet</code> object to pass to the
525     * <code>ServerRequestHandler</code> object. After the handler processes the
526     * request, this method will create a reply message to send to the server
527     * with the response code provided.
528     * @throws IOException if an error occurred at the transport layer
529     */
530    private void handleConnectRequest() throws IOException {
531        int packetLength;
532        @SuppressWarnings("unused")
533        int version;
534        @SuppressWarnings("unused")
535        int flags;
536        int totalLength = 7;
537        byte[] head = null;
538        int code = -1;
539        HeaderSet request = new HeaderSet();
540        HeaderSet reply = new HeaderSet();
541        int bytesReceived;
542
543        if(V) Log.v(TAG,"handleConnectRequest()");
544
545        /*
546         * Read in the length of the OBEX packet, OBEX version, flags, and max
547         * packet length
548         */
549        packetLength = mInput.read();
550        packetLength = (packetLength << 8) + mInput.read();
551        if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
552
553        version = mInput.read();
554        flags = mInput.read();
555        mMaxPacketLength = mInput.read();
556        mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
557
558        if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
559                + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
560
561        // should we check it?
562        if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
563            mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
564        }
565
566        if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
567            Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
568                    + " is larger than the max size supported by the transport: "
569                    + ObexHelper.getMaxTxPacketSize(mTransport)
570                    + " Reducing to this size.");
571            mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
572        }
573
574        if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
575            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
576            totalLength = 7;
577        } else {
578            if (packetLength > 7) {
579                byte[] headers = new byte[packetLength - 7];
580                bytesReceived = mInput.read(headers);
581
582                while (bytesReceived != headers.length) {
583                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
584                            - bytesReceived);
585                }
586
587                ObexHelper.updateHeaderSet(request, headers);
588            }
589
590            if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
591                mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
592            } else {
593                mListener.setConnectionId(1);
594            }
595
596            if (request.mAuthResp != null) {
597                if (!handleAuthResp(request.mAuthResp)) {
598                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
599                    mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
600                            request.mAuthResp));
601                }
602                request.mAuthResp = null;
603            }
604
605            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
606                if (request.mAuthChall != null) {
607                    handleAuthChall(request);
608                    reply.mAuthResp = new byte[request.mAuthResp.length];
609                    System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
610                            reply.mAuthResp.length);
611                    request.mAuthChall = null;
612                    request.mAuthResp = null;
613                }
614
615                try {
616                    code = mListener.onConnect(request, reply);
617                    code = validateResponseCode(code);
618
619                    if (reply.nonce != null) {
620                        mChallengeDigest = new byte[16];
621                        System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
622                    } else {
623                        mChallengeDigest = null;
624                    }
625                    long id = mListener.getConnectionId();
626                    if (id == -1) {
627                        reply.mConnectionID = null;
628                    } else {
629                        reply.mConnectionID = ObexHelper.convertToByteArray(id);
630                    }
631
632                    head = ObexHelper.createHeader(reply, false);
633                    totalLength += head.length;
634
635                    if (totalLength > mMaxPacketLength) {
636                        totalLength = 7;
637                        head = null;
638                        code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
639                    }
640                } catch (Exception e) {
641                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
642                    totalLength = 7;
643                    head = null;
644                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
645                }
646
647            }
648        }
649
650        // Compute Length of OBEX CONNECT packet
651        byte[] length = ObexHelper.convertToByteArray(totalLength);
652
653        /*
654         * Write the OBEX CONNECT packet to the server. Byte 0: response code
655         * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
656         * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
657         * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
658         */
659        byte[] sendData = new byte[totalLength];
660        int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
661        sendData[0] = (byte)code;
662        sendData[1] = length[2];
663        sendData[2] = length[3];
664        sendData[3] = (byte)0x10;
665        sendData[4] = (byte)0x00;
666        sendData[5] = (byte)(maxRxLength >> 8);
667        sendData[6] = (byte)(maxRxLength & 0xFF);
668
669        if (head != null) {
670            System.arraycopy(head, 0, sendData, 7, head.length);
671        }
672
673        mOutput.write(sendData);
674        mOutput.flush();
675    }
676
677    /**
678     * Closes the server session - in detail close I/O streams and the
679     * underlying transport layer. Internal flag is also set so that later
680     * attempt to read/write will throw an exception.
681     */
682    public synchronized void close() {
683        if (mListener != null) {
684            mListener.onClose();
685        }
686        try {
687            /* Set state to closed before interrupting the thread by closing the streams */
688            mClosed = true;
689            if(mInput != null)
690                mInput.close();
691            if(mOutput != null)
692                mOutput.close();
693            if(mTransport != null)
694                mTransport.close();
695        } catch (Exception e) {
696            if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
697        }
698        mTransport = null;
699        mInput = null;
700        mOutput = null;
701        mListener = null;
702    }
703
704    /**
705     * Verifies that the response code is valid. If it is not valid, it will
706     * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
707     * @param code the response code to check
708     * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
709     *         if <code>code</code> is not valid
710     */
711    private int validateResponseCode(int code) {
712
713        if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
714            return code;
715        }
716        if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
717                && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
718            return code;
719        }
720        if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
721                && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
722            return code;
723        }
724        if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
725                && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
726            return code;
727        }
728        if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
729                && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
730            return code;
731        }
732        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
733    }
734
735    public ObexTransport getTransport() {
736        return mTransport;
737    }
738}
739