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                        break;
108
109                    case ObexHelper.OBEX_OPCODE_GET:
110                    case ObexHelper.OBEX_OPCODE_GET_FINAL:
111                        handleGetRequest(requestType);
112                        break;
113
114                    case ObexHelper.OBEX_OPCODE_PUT:
115                    case ObexHelper.OBEX_OPCODE_PUT_FINAL:
116                        handlePutRequest(requestType);
117                        break;
118
119                    case ObexHelper.OBEX_OPCODE_SETPATH:
120                        handleSetPathRequest();
121                        break;
122                    case ObexHelper.OBEX_OPCODE_ABORT:
123                        handleAbortRequest();
124                        break;
125
126                    case -1:
127                        done = true;
128                        break;
129
130                    default:
131
132                        /*
133                         * Received a request type that is not recognized so I am
134                         * just going to read the packet and send a not implemented
135                         * to the client
136                         */
137                        int length = mInput.read();
138                        length = (length << 8) + mInput.read();
139                        for (int i = 3; i < length; i++) {
140                            mInput.read();
141                        }
142                        sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
143                }
144            }
145
146        } catch (NullPointerException e) {
147            Log.d(TAG, "Exception occured - ignoring", e);
148        } catch (Exception e) {
149            Log.d(TAG, "Exception occured - ignoring", e);
150        }
151        close();
152    }
153
154    /**
155     * Handles a ABORT request from a client. This method will read the rest of
156     * the request from the client. Assuming the request is valid, it will
157     * create a <code>HeaderSet</code> object to pass to the
158     * <code>ServerRequestHandler</code> object. After the handler processes the
159     * request, this method will create a reply message to send to the server.
160     *
161     * @throws IOException if an error occurred at the transport layer
162     */
163    private void handleAbortRequest() throws IOException {
164        int code = ResponseCodes.OBEX_HTTP_OK;
165        HeaderSet request = new HeaderSet();
166        HeaderSet reply = new HeaderSet();
167
168        int length = mInput.read();
169        length = (length << 8) + mInput.read();
170        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
171            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
172        } else {
173            for (int i = 3; i < length; i++) {
174                mInput.read();
175            }
176            code = mListener.onAbort(request, reply);
177            Log.v(TAG, "onAbort request handler return value- " + code);
178            code = validateResponseCode(code);
179        }
180        sendResponse(code, null);
181    }
182
183    /**
184     * Handles a PUT request from a client. This method will provide a
185     * <code>ServerOperation</code> object to the request handler. The
186     * <code>ServerOperation</code> object will handle the rest of the request.
187     * It will also send replies and receive requests until the final reply
188     * should be sent. When the final reply should be sent, this method will get
189     * the response code to use and send the reply. The
190     * <code>ServerOperation</code> object will always reply with a
191     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
192     * needed.
193     * @param type the type of request received; either 0x02 or 0x82
194     * @throws IOException if an error occurred at the transport layer
195     */
196    private void handlePutRequest(int type) throws IOException {
197        ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
198        try {
199            int response = -1;
200
201            if ((op.finalBitSet) && !op.isValidBody()) {
202                response = validateResponseCode(mListener
203                        .onDelete(op.requestHeader, op.replyHeader));
204            } else {
205                response = validateResponseCode(mListener.onPut(op));
206            }
207            if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
208                op.sendReply(response);
209            } else if (!op.isAborted) {
210                // wait for the final bit
211                while (!op.finalBitSet) {
212                    op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
213                }
214                op.sendReply(response);
215            }
216        } catch (Exception e) {
217            /*To fix bugs in aborted cases,
218             *(client abort file transfer prior to the last packet which has the end of body header,
219             *internal error should not be sent because server has already replied with
220             *OK response in "sendReply")
221             */
222            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
223            if (!op.isAborted) {
224                sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
225            }
226        }
227    }
228
229    /**
230     * Handles a GET request from a client. This method will provide a
231     * <code>ServerOperation</code> object to the request handler. The
232     * <code>ServerOperation</code> object will handle the rest of the request.
233     * It will also send replies and receive requests until the final reply
234     * should be sent. When the final reply should be sent, this method will get
235     * the response code to use and send the reply. The
236     * <code>ServerOperation</code> object will always reply with a
237     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
238     * needed.
239     * @param type the type of request received; either 0x03 or 0x83
240     * @throws IOException if an error occurred at the transport layer
241     */
242    private void handleGetRequest(int type) throws IOException {
243        ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
244        try {
245            int response = validateResponseCode(mListener.onGet(op));
246
247            if (!op.isAborted) {
248                op.sendReply(response);
249            }
250        } catch (Exception e) {
251            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
252            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
253        }
254    }
255
256    /**
257     * Send standard response.
258     * @param code the response code to send
259     * @param header the headers to include in the response
260     * @throws IOException if an IO error occurs
261     */
262    public void sendResponse(int code, byte[] header) throws IOException {
263        int totalLength = 3;
264        byte[] data = null;
265        OutputStream op = mOutput;
266        if (op == null) {
267            return;
268        }
269
270        if (header != null) {
271            totalLength += header.length;
272            data = new byte[totalLength];
273            data[0] = (byte)code;
274            data[1] = (byte)(totalLength >> 8);
275            data[2] = (byte)totalLength;
276            System.arraycopy(header, 0, data, 3, header.length);
277        } else {
278            data = new byte[totalLength];
279            data[0] = (byte)code;
280            data[1] = (byte)0x00;
281            data[2] = (byte)totalLength;
282        }
283        op.write(data);
284        op.flush(); // TODO: Do we need to flush?
285    }
286
287    /**
288     * Handles a SETPATH request from a client. This method will read the rest
289     * of the request from the client. Assuming the request is valid, it will
290     * create a <code>HeaderSet</code> object to pass to the
291     * <code>ServerRequestHandler</code> object. After the handler processes the
292     * request, this method will create a reply message to send to the server
293     * with the response code provided.
294     * @throws IOException if an error occurred at the transport layer
295     */
296    private void handleSetPathRequest() throws IOException {
297        int length;
298        int flags;
299        @SuppressWarnings("unused")
300        int constants;
301        int totalLength = 3;
302        byte[] head = null;
303        int code = -1;
304        int bytesReceived;
305        HeaderSet request = new HeaderSet();
306        HeaderSet reply = new HeaderSet();
307
308        length = mInput.read();
309        length = (length << 8) + mInput.read();
310        flags = mInput.read();
311        constants = mInput.read();
312
313        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
314            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
315            totalLength = 3;
316        } else {
317            if (length > 5) {
318                byte[] headers = new byte[length - 5];
319                bytesReceived = mInput.read(headers);
320
321                while (bytesReceived != headers.length) {
322                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
323                            - bytesReceived);
324                }
325
326                ObexHelper.updateHeaderSet(request, headers);
327
328                if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
329                    mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
330                } else {
331                    mListener.setConnectionId(1);
332                }
333                // the Auth chan is initiated by the server, client sent back the authResp .
334                if (request.mAuthResp != null) {
335                    if (!handleAuthResp(request.mAuthResp)) {
336                        code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
337                        mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
338                                request.mAuthResp));
339                    }
340                    request.mAuthResp = null;
341                }
342            }
343
344            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
345                // the Auth challenge is initiated by the client
346                // the server will send back the authResp to the client
347                if (request.mAuthChall != null) {
348                    handleAuthChall(request);
349                    reply.mAuthResp = new byte[request.mAuthResp.length];
350                    System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
351                            reply.mAuthResp.length);
352                    request.mAuthChall = null;
353                    request.mAuthResp = null;
354                }
355                boolean backup = false;
356                boolean create = true;
357                if (!((flags & 1) == 0)) {
358                    backup = true;
359                }
360                if (!((flags & 2) == 0)) {
361                    create = false;
362                }
363
364                try {
365                    code = mListener.onSetPath(request, reply, backup, create);
366                } catch (Exception e) {
367                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
368                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
369                    return;
370                }
371
372                code = validateResponseCode(code);
373
374                if (reply.nonce != null) {
375                    mChallengeDigest = new byte[16];
376                    System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
377                } else {
378                    mChallengeDigest = null;
379                }
380
381                long id = mListener.getConnectionId();
382                if (id == -1) {
383                    reply.mConnectionID = null;
384                } else {
385                    reply.mConnectionID = ObexHelper.convertToByteArray(id);
386                }
387
388                head = ObexHelper.createHeader(reply, false);
389                totalLength += head.length;
390
391                if (totalLength > mMaxPacketLength) {
392                    totalLength = 3;
393                    head = null;
394                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
395                }
396            }
397        }
398
399        // Compute Length of OBEX SETPATH packet
400        byte[] replyData = new byte[totalLength];
401        replyData[0] = (byte)code;
402        replyData[1] = (byte)(totalLength >> 8);
403        replyData[2] = (byte)totalLength;
404        if (head != null) {
405            System.arraycopy(head, 0, replyData, 3, head.length);
406        }
407        /*
408         * Write the OBEX SETPATH packet to the server. Byte 0: response code
409         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
410         */
411        mOutput.write(replyData);
412        mOutput.flush();
413    }
414
415    /**
416     * Handles a disconnect request from a client. This method will read the
417     * rest of the request from the client. Assuming the request is valid, it
418     * will create a <code>HeaderSet</code> object to pass to the
419     * <code>ServerRequestHandler</code> object. After the handler processes the
420     * request, this method will create a reply message to send to the server.
421     * @throws IOException if an error occurred at the transport layer
422     */
423    private void handleDisconnectRequest() throws IOException {
424        int length;
425        int code = ResponseCodes.OBEX_HTTP_OK;
426        int totalLength = 3;
427        byte[] head = null;
428        int bytesReceived;
429        HeaderSet request = new HeaderSet();
430        HeaderSet reply = new HeaderSet();
431
432        length = mInput.read();
433        length = (length << 8) + mInput.read();
434
435        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
436            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
437            totalLength = 3;
438        } else {
439            if (length > 3) {
440                byte[] headers = new byte[length - 3];
441                bytesReceived = mInput.read(headers);
442
443                while (bytesReceived != headers.length) {
444                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
445                            - bytesReceived);
446                }
447
448                ObexHelper.updateHeaderSet(request, headers);
449            }
450
451            if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
452                mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
453            } else {
454                mListener.setConnectionId(1);
455            }
456
457            if (request.mAuthResp != null) {
458                if (!handleAuthResp(request.mAuthResp)) {
459                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
460                    mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
461                            request.mAuthResp));
462                }
463                request.mAuthResp = null;
464            }
465
466            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
467
468                if (request.mAuthChall != null) {
469                    handleAuthChall(request);
470                    request.mAuthChall = null;
471                }
472
473                try {
474                    mListener.onDisconnect(request, reply);
475                } catch (Exception e) {
476                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
477                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
478                    return;
479                }
480
481                long id = mListener.getConnectionId();
482                if (id == -1) {
483                    reply.mConnectionID = null;
484                } else {
485                    reply.mConnectionID = ObexHelper.convertToByteArray(id);
486                }
487
488                head = ObexHelper.createHeader(reply, false);
489                totalLength += head.length;
490
491                if (totalLength > mMaxPacketLength) {
492                    totalLength = 3;
493                    head = null;
494                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
495                }
496            }
497        }
498
499        // Compute Length of OBEX CONNECT packet
500        byte[] replyData;
501        if (head != null) {
502            replyData = new byte[3 + head.length];
503        } else {
504            replyData = new byte[3];
505        }
506        replyData[0] = (byte)code;
507        replyData[1] = (byte)(totalLength >> 8);
508        replyData[2] = (byte)totalLength;
509        if (head != null) {
510            System.arraycopy(head, 0, replyData, 3, head.length);
511        }
512        /*
513         * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
514         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
515         */
516        mOutput.write(replyData);
517        mOutput.flush();
518    }
519
520    /**
521     * Handles a connect request from a client. This method will read the rest
522     * of the request from the client. Assuming the request is valid, it will
523     * create a <code>HeaderSet</code> object to pass to the
524     * <code>ServerRequestHandler</code> object. After the handler processes the
525     * request, this method will create a reply message to send to the server
526     * with the response code provided.
527     * @throws IOException if an error occurred at the transport layer
528     */
529    private void handleConnectRequest() throws IOException {
530        int packetLength;
531        @SuppressWarnings("unused")
532        int version;
533        @SuppressWarnings("unused")
534        int flags;
535        int totalLength = 7;
536        byte[] head = null;
537        int code = -1;
538        HeaderSet request = new HeaderSet();
539        HeaderSet reply = new HeaderSet();
540        int bytesReceived;
541
542        if(V) Log.v(TAG,"handleConnectRequest()");
543
544        /*
545         * Read in the length of the OBEX packet, OBEX version, flags, and max
546         * packet length
547         */
548        packetLength = mInput.read();
549        packetLength = (packetLength << 8) + mInput.read();
550        if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
551
552        version = mInput.read();
553        flags = mInput.read();
554        mMaxPacketLength = mInput.read();
555        mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
556
557        if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
558                + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
559
560        // should we check it?
561        if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
562            mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
563        }
564
565        if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
566            Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
567                    + " is larger than the max size supported by the transport: "
568                    + ObexHelper.getMaxTxPacketSize(mTransport)
569                    + " Reducing to this size.");
570            mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
571        }
572
573        if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
574            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
575            totalLength = 7;
576        } else {
577            if (packetLength > 7) {
578                byte[] headers = new byte[packetLength - 7];
579                bytesReceived = mInput.read(headers);
580
581                while (bytesReceived != headers.length) {
582                    bytesReceived += mInput.read(headers, bytesReceived, headers.length
583                            - bytesReceived);
584                }
585
586                ObexHelper.updateHeaderSet(request, headers);
587            }
588
589            if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
590                mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
591            } else {
592                mListener.setConnectionId(1);
593            }
594
595            if (request.mAuthResp != null) {
596                if (!handleAuthResp(request.mAuthResp)) {
597                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
598                    mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
599                            request.mAuthResp));
600                }
601                request.mAuthResp = null;
602            }
603
604            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
605                if (request.mAuthChall != null) {
606                    handleAuthChall(request);
607                    reply.mAuthResp = new byte[request.mAuthResp.length];
608                    System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
609                            reply.mAuthResp.length);
610                    request.mAuthChall = null;
611                    request.mAuthResp = null;
612                }
613
614                try {
615                    code = mListener.onConnect(request, reply);
616                    code = validateResponseCode(code);
617
618                    if (reply.nonce != null) {
619                        mChallengeDigest = new byte[16];
620                        System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
621                    } else {
622                        mChallengeDigest = null;
623                    }
624                    long id = mListener.getConnectionId();
625                    if (id == -1) {
626                        reply.mConnectionID = null;
627                    } else {
628                        reply.mConnectionID = ObexHelper.convertToByteArray(id);
629                    }
630
631                    head = ObexHelper.createHeader(reply, false);
632                    totalLength += head.length;
633
634                    if (totalLength > mMaxPacketLength) {
635                        totalLength = 7;
636                        head = null;
637                        code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
638                    }
639                } catch (Exception e) {
640                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
641                    totalLength = 7;
642                    head = null;
643                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
644                }
645
646            }
647        }
648
649        // Compute Length of OBEX CONNECT packet
650        byte[] length = ObexHelper.convertToByteArray(totalLength);
651
652        /*
653         * Write the OBEX CONNECT packet to the server. Byte 0: response code
654         * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
655         * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
656         * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
657         */
658        byte[] sendData = new byte[totalLength];
659        int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
660        if (maxRxLength > mMaxPacketLength) {
661            if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength +
662                    " and MaxNegotiated from Client: " + mMaxPacketLength);
663            maxRxLength = mMaxPacketLength;
664        }
665        sendData[0] = (byte)code;
666        sendData[1] = length[2];
667        sendData[2] = length[3];
668        sendData[3] = (byte)0x10;
669        sendData[4] = (byte)0x00;
670        sendData[5] = (byte)(maxRxLength >> 8);
671        sendData[6] = (byte)(maxRxLength & 0xFF);
672
673        if (head != null) {
674            System.arraycopy(head, 0, sendData, 7, head.length);
675        }
676
677        mOutput.write(sendData);
678        mOutput.flush();
679    }
680
681    /**
682     * Closes the server session - in detail close I/O streams and the
683     * underlying transport layer. Internal flag is also set so that later
684     * attempt to read/write will throw an exception.
685     */
686    public synchronized void close() {
687        if (mListener != null) {
688            mListener.onClose();
689        }
690        try {
691            /* Set state to closed before interrupting the thread by closing the streams */
692            mClosed = true;
693            if(mInput != null)
694                mInput.close();
695            if(mOutput != null)
696                mOutput.close();
697            if(mTransport != null)
698                mTransport.close();
699        } catch (Exception e) {
700            if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
701        }
702        mTransport = null;
703        mInput = null;
704        mOutput = null;
705        mListener = null;
706    }
707
708    /**
709     * Verifies that the response code is valid. If it is not valid, it will
710     * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
711     * @param code the response code to check
712     * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
713     *         if <code>code</code> is not valid
714     */
715    private int validateResponseCode(int code) {
716
717        if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
718            return code;
719        }
720        if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
721                && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
722            return code;
723        }
724        if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
725                && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
726            return code;
727        }
728        if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
729                && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
730            return code;
731        }
732        if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
733                && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
734            return code;
735        }
736        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
737    }
738
739    public ObexTransport getTransport() {
740        return mTransport;
741    }
742}
743