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 
35 package javax.obex;
36 
37 import android.util.Log;
38 
39 import java.io.InputStream;
40 import java.io.IOException;
41 import java.io.OutputStream;
42 
43 /**
44  * This class in an implementation of the OBEX ServerSession.
45  * @hide
46  */
47 public 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      */
ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)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      */
run()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      */
handleAbortRequest()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      */
handlePutRequest(int type)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      */
handleGetRequest(int type)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      */
sendResponse(int code, byte[] header)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      */
handleSetPathRequest()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      */
handleDisconnectRequest()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      */
handleConnectRequest()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      */
close()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      */
validateResponseCode(int code)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 
getTransport()735     public ObexTransport getTransport() {
736         return mTransport;
737     }
738 }
739