blob: c627dfb8abacd78f95a21fcb0cfd74b5693b63d1 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
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 java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.io.DataInputStream;
41import java.io.DataOutputStream;
42import java.io.ByteArrayOutputStream;
43
44import android.util.Log;
45
46/**
47 * This class implements the <code>Operation</code> interface. It will read and
48 * write data via puts and gets.
49 * @hide
50 */
51public final class ClientOperation implements Operation, BaseStream {
52
53 private static final String TAG = "ClientOperation";
54
55 private static final boolean V = ObexHelper.VDBG;
56
57 private ClientSession mParent;
58
59 private boolean mInputOpen;
60
61 private PrivateInputStream mPrivateInput;
62
63 private boolean mPrivateInputOpen;
64
65 private PrivateOutputStream mPrivateOutput;
66
67 private boolean mPrivateOutputOpen;
68
69 private String mExceptionMessage;
70
71 private int mMaxPacketSize;
72
73 private boolean mOperationDone;
74
75 private boolean mGetOperation;
76
77 private boolean mGetFinalFlag;
78
79 private HeaderSet mRequestHeader;
80
81 private HeaderSet mReplyHeader;
82
83 private boolean mEndOfBodySent;
84
85 private boolean mSendBodyHeader = true;
86 // A latch - when triggered, there is not way back ;-)
87 private boolean mSrmActive = false;
88
89 // Assume SRM disabled - until support is confirmed
90 // by the server
91 private boolean mSrmEnabled = false;
92 // keep waiting until final-bit is received in request
93 // to handle the case where the SRM enable header is in
94 // a different OBEX packet than the SRMP header.
95 private boolean mSrmWaitingForRemote = true;
96
97
98 /**
99 * Creates new OperationImpl to read and write data to a server
100 * @param maxSize the maximum packet size
101 * @param p the parent to this object
102 * @param type <code>true</code> if this is a get request;
103 * <code>false</code. if this is a put request
104 * @param header the header to set in the initial request
105 * @throws IOException if the an IO error occurred
106 */
107 public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
108 throws IOException {
109
110 mParent = p;
111 mEndOfBodySent = false;
112 mInputOpen = true;
113 mOperationDone = false;
114 mMaxPacketSize = maxSize;
115 mGetOperation = type;
116 mGetFinalFlag = false;
117
118 mPrivateInputOpen = false;
119 mPrivateOutputOpen = false;
120 mPrivateInput = null;
121 mPrivateOutput = null;
122
123 mReplyHeader = new HeaderSet();
124
125 mRequestHeader = new HeaderSet();
126
127 int[] headerList = header.getHeaderList();
128
129 if (headerList != null) {
130
131 for (int i = 0; i < headerList.length; i++) {
132 mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
133 }
134 }
135
136 if ((header).mAuthChall != null) {
137 mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
138 System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
139 (header).mAuthChall.length);
140 }
141
142 if ((header).mAuthResp != null) {
143 mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
144 System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
145 (header).mAuthResp.length);
146
147 }
148
149 if ((header).mConnectionID != null) {
150 mRequestHeader.mConnectionID = new byte[4];
151 System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
152 4);
153
154 }
155 }
156
157 /**
158 * Allows to set flag which will force GET to be always sent as single packet request with
159 * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
160 * require requests to be sent this way.
161 */
162 public void setGetFinalFlag(boolean flag) {
163 mGetFinalFlag = flag;
164 }
165
166 /**
167 * Sends an ABORT message to the server. By calling this method, the
168 * corresponding input and output streams will be closed along with this
169 * object.
170 * @throws IOException if the transaction has already ended or if an OBEX
171 * server called this method
172 */
173 public synchronized void abort() throws IOException {
174 ensureOpen();
175 //no compatible with sun-ri
176 if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
177 throw new IOException("Operation has already ended");
178 }
179
180 mExceptionMessage = "Operation aborted";
181 if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
182 mOperationDone = true;
183 /*
184 * Since we are not sending any headers or returning any headers then
185 * we just need to write and read the same bytes
186 */
187 mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
188
189 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
190 throw new IOException("Invalid response code from server");
191 }
192
193 mExceptionMessage = null;
194 }
195
196 close();
197 }
198
199 /**
200 * Retrieves the response code retrieved from the server. Response codes are
201 * defined in the <code>ResponseCodes</code> interface.
202 * @return the response code retrieved from the server
203 * @throws IOException if an error occurred in the transport layer during
204 * the transaction; if this method is called on a
205 * <code>HeaderSet</code> object created by calling
206 * <code>createHeaderSet</code> in a <code>ClientSession</code>
207 * object
208 */
209 public synchronized int getResponseCode() throws IOException {
210 if ((mReplyHeader.responseCode == -1)
211 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
212 validateConnection();
213 }
214
215 return mReplyHeader.responseCode;
216 }
217
218 /**
219 * This method will always return <code>null</code>
220 * @return <code>null</code>
221 */
222 public String getEncoding() {
223 return null;
224 }
225
226 /**
227 * Returns the type of content that the resource connected to is providing.
228 * E.g. if the connection is via HTTP, then the value of the content-type
229 * header field is returned.
230 * @return the content type of the resource that the URL references, or
231 * <code>null</code> if not known
232 */
233 public String getType() {
234 try {
235 return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
236 } catch (IOException e) {
237 if(V) Log.d(TAG, "Exception occured - returning null",e);
238 return null;
239 }
240 }
241
242 /**
243 * Returns the length of the content which is being provided. E.g. if the
244 * connection is via HTTP, then the value of the content-length header field
245 * is returned.
246 * @return the content length of the resource that this connection's URL
247 * references, or -1 if the content length is not known
248 */
249 public long getLength() {
250 try {
251 Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
252
253 if (temp == null) {
254 return -1;
255 } else {
256 return temp.longValue();
257 }
258 } catch (IOException e) {
259 if(V) Log.d(TAG,"Exception occured - returning -1",e);
260 return -1;
261 }
262 }
263
264 /**
265 * Open and return an input stream for a connection.
266 * @return an input stream
267 * @throws IOException if an I/O error occurs
268 */
269 public InputStream openInputStream() throws IOException {
270
271 ensureOpen();
272
273 if (mPrivateInputOpen)
274 throw new IOException("no more input streams available");
275 if (mGetOperation) {
276 // send the GET request here
277 validateConnection();
278 } else {
279 if (mPrivateInput == null) {
280 mPrivateInput = new PrivateInputStream(this);
281 }
282 }
283
284 mPrivateInputOpen = true;
285
286 return mPrivateInput;
287 }
288
289 /**
290 * Open and return a data input stream for a connection.
291 * @return an input stream
292 * @throws IOException if an I/O error occurs
293 */
294 public DataInputStream openDataInputStream() throws IOException {
295 return new DataInputStream(openInputStream());
296 }
297
298 /**
299 * Open and return an output stream for a connection.
300 * @return an output stream
301 * @throws IOException if an I/O error occurs
302 */
303 public OutputStream openOutputStream() throws IOException {
304
305 ensureOpen();
306 ensureNotDone();
307
308 if (mPrivateOutputOpen)
309 throw new IOException("no more output streams available");
310
311 if (mPrivateOutput == null) {
312 // there are 3 bytes operation headers and 3 bytes body headers //
313 mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
314 }
315
316 mPrivateOutputOpen = true;
317
318 return mPrivateOutput;
319 }
320
321 public int getMaxPacketSize() {
322 return mMaxPacketSize - 6 - getHeaderLength();
323 }
324
325 public int getHeaderLength() {
326 // OPP may need it
327 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
328 return headerArray.length;
329 }
330
331 /**
332 * Open and return a data output stream for a connection.
333 * @return an output stream
334 * @throws IOException if an I/O error occurs
335 */
336 public DataOutputStream openDataOutputStream() throws IOException {
337 return new DataOutputStream(openOutputStream());
338 }
339
340 /**
341 * Closes the connection and ends the transaction
342 * @throws IOException if the operation has already ended or is closed
343 */
344 public void close() throws IOException {
345 mInputOpen = false;
346 mPrivateInputOpen = false;
347 mPrivateOutputOpen = false;
348 mParent.setRequestInactive();
349 }
350
351 /**
352 * Returns the headers that have been received during the operation.
353 * Modifying the object returned has no effect on the headers that are sent
354 * or retrieved.
355 * @return the headers received during this <code>Operation</code>
356 * @throws IOException if this <code>Operation</code> has been closed
357 */
358 public HeaderSet getReceivedHeader() throws IOException {
359 ensureOpen();
360
361 return mReplyHeader;
362 }
363
364 /**
365 * Specifies the headers that should be sent in the next OBEX message that
366 * is sent.
367 * @param headers the headers to send in the next message
368 * @throws IOException if this <code>Operation</code> has been closed or the
369 * transaction has ended and no further messages will be exchanged
370 * @throws IllegalArgumentException if <code>headers</code> was not created
371 * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
372 * @throws NullPointerException if <code>headers</code> is <code>null</code>
373 */
374 public void sendHeaders(HeaderSet headers) throws IOException {
375 ensureOpen();
376 if (mOperationDone) {
377 throw new IOException("Operation has already exchanged all data");
378 }
379
380 if (headers == null) {
381 throw new IOException("Headers may not be null");
382 }
383
384 int[] headerList = headers.getHeaderList();
385 if (headerList != null) {
386 for (int i = 0; i < headerList.length; i++) {
387 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
388 }
389 }
390 }
391
392 /**
393 * Verifies that additional information may be sent. In other words, the
394 * operation is not done.
395 * @throws IOException if the operation is completed
396 */
397 public void ensureNotDone() throws IOException {
398 if (mOperationDone) {
399 throw new IOException("Operation has completed");
400 }
401 }
402
403 /**
404 * Verifies that the connection is open and no exceptions should be thrown.
405 * @throws IOException if an exception needs to be thrown
406 */
407 public void ensureOpen() throws IOException {
408 mParent.ensureOpen();
409
410 if (mExceptionMessage != null) {
411 throw new IOException(mExceptionMessage);
412 }
413 if (!mInputOpen) {
414 throw new IOException("Operation has already ended");
415 }
416 }
417
418 /**
419 * Verifies that the connection is open and the proper data has been read.
420 * @throws IOException if an IO error occurs
421 */
422 private void validateConnection() throws IOException {
423 ensureOpen();
424
425 // Make sure that a response has been recieved from remote
426 // before continuing
427 if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
428 startProcessing();
429 }
430 }
431
432 /**
433 * Sends a request to the client of the specified type.
434 * This function will enable SRM and set SRM active if the server
435 * response allows this.
436 * @param opCode the request code to send to the client
437 * @return <code>true</code> if there is more data to send;
438 * <code>false</code> if there is no more data to send
439 * @throws IOException if an IO error occurs
440 */
441 private boolean sendRequest(int opCode) throws IOException {
442 boolean returnValue = false;
443 ByteArrayOutputStream out = new ByteArrayOutputStream();
444 int bodyLength = -1;
445 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
446 if (mPrivateOutput != null) {
447 bodyLength = mPrivateOutput.size();
448 }
449
450 /*
451 * Determine if there is space to add a body request. At present
452 * this method checks to see if there is room for at least a 17
453 * byte body header. This number needs to be at least 6 so that
454 * there is room for the header ID and length and the reply ID and
455 * length, but it is a waste of resources if we can't send much of
456 * the body.
457 */
458 final int MINIMUM_BODY_LENGTH = 3;
459 if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
460 > mMaxPacketSize) {
461 int end = 0;
462 int start = 0;
463 // split & send the headerArray in multiple packets.
464
465 while (end != headerArray.length) {
466 //split the headerArray
467
468 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
469 - ObexHelper.BASE_PACKET_LENGTH);
470 // can not split
471 if (end == -1) {
472 mOperationDone = true;
473 abort();
474 mExceptionMessage = "Header larger then can be sent in a packet";
475 mInputOpen = false;
476
477 if (mPrivateInput != null) {
478 mPrivateInput.close();
479 }
480
481 if (mPrivateOutput != null) {
482 mPrivateOutput.close();
483 }
484 throw new IOException("OBEX Packet exceeds max packet size");
485 }
486
487 byte[] sendHeader = new byte[end - start];
488 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
489 if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
490 return false;
491 }
492
493 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
494 return false;
495 }
496
497 start = end;
498 }
499
500 // Enable SRM if it should be enabled
501 checkForSrm();
502
503 if (bodyLength > 0) {
504 return true;
505 } else {
506 return false;
507 }
508 } else {
509 /* All headers will fit into a single package */
510 if(mSendBodyHeader == false) {
511 /* As we are not to send any body data, set the FINAL_BIT */
512 opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
513 }
514 out.write(headerArray);
515 }
516
517 if (bodyLength > 0) {
518 /*
519 * Determine if we can send the whole body or just part of
520 * the body. Remember that there is the 3 bytes for the
521 * response message and 3 bytes for the header ID and length
522 */
523 if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
524 returnValue = true;
525
526 bodyLength = mMaxPacketSize - headerArray.length - 6;
527 }
528
529 byte[] body = mPrivateOutput.readBytes(bodyLength);
530
531 /*
532 * Since this is a put request if the final bit is set or
533 * the output stream is closed we need to send the 0x49
534 * (End of Body) otherwise, we need to send 0x48 (Body)
535 */
536 if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
537 && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
538 out.write(HeaderSet.END_OF_BODY);
539 mEndOfBodySent = true;
540 } else {
541 out.write(HeaderSet.BODY);
542 }
543
544 bodyLength += 3;
545 out.write((byte)(bodyLength >> 8));
546 out.write((byte)bodyLength);
547
548 if (body != null) {
549 out.write(body);
550 }
551 }
552
553 if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
554 // only 0x82 or 0x83 can send 0x49
555 if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
556 out.write(HeaderSet.BODY);
557 } else {
558 out.write(HeaderSet.END_OF_BODY);
559 mEndOfBodySent = true;
560 }
561
562 bodyLength = 3;
563 out.write((byte)(bodyLength >> 8));
564 out.write((byte)bodyLength);
565 }
566
567 if (out.size() == 0) {
568 if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
569 return false;
570 }
571 // Enable SRM if it should be enabled
572 checkForSrm();
573 return returnValue;
574 }
575 if ((out.size() > 0)
576 && (!mParent.sendRequest(opCode, out.toByteArray(),
577 mReplyHeader, mPrivateInput, mSrmActive))) {
578 return false;
579 }
580 // Enable SRM if it should be enabled
581 checkForSrm();
582
583 // send all of the output data in 0x48,
584 // send 0x49 with empty body
585 if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
586 returnValue = true;
587
588 return returnValue;
589 }
590
591 private void checkForSrm() throws IOException {
592 Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
593 if(mParent.isSrmSupported() == true && srmMode != null
594 && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
595 mSrmEnabled = true;
596 }
597 /**
598 * Call this only when a complete obex packet have been received.
599 * (This is not optimal, but the current design is not really suited to
600 * the way SRM is specified.)
601 * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
602 * into every OBEX packet, hence if another header occupies the entire packet,
603 * the scheme will not work - unlikely though.
604 */
605 if(mSrmEnabled) {
606 mSrmWaitingForRemote = false;
607 Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
608 if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
609 mSrmWaitingForRemote = true;
610 // Clear the wait header, as the absence of the header in the next packet
611 // indicates don't wait anymore.
612 mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
613 }
614 }
615 if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
616 mSrmActive = true;
617 }
618 }
619
620 /**
621 * This method starts the processing thread results. It will send the
622 * initial request. If the response takes more then one packet, a thread
623 * will be started to handle additional requests
624 * @throws IOException if an IO error occurs
625 */
626 private synchronized void startProcessing() throws IOException {
627
628 if (mPrivateInput == null) {
629 mPrivateInput = new PrivateInputStream(this);
630 }
631 boolean more = true;
632
633 if (mGetOperation) {
634 if (!mOperationDone) {
635 if (!mGetFinalFlag) {
636 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
637 while ((more) && (mReplyHeader.responseCode ==
638 ResponseCodes.OBEX_HTTP_CONTINUE)) {
639 more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
640 }
641 // For GET we need to loop until all headers have been sent,
642 // And then we wait for the first continue package with the
643 // reply.
644 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
645 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
646 null, mReplyHeader, mPrivateInput, mSrmActive);
647 }
648 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
649 mOperationDone = true;
650 } else {
651 checkForSrm();
652 }
653 } else {
654 more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
655
656 if (more) {
657 throw new IOException("FINAL_GET forced, data didn't fit into one packet");
658 }
659
660 mOperationDone = true;
661 }
662 }
663 } else {
664 // PUT operation
665 if (!mOperationDone) {
666 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
667 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
668 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
669 }
670 }
671
672 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
673 mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
674 null, mReplyHeader, mPrivateInput, mSrmActive);
675 }
676
677 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
678 mOperationDone = true;
679 }
680 }
681 }
682
683 /**
684 * Continues the operation since there is no data to read.
685 * @param sendEmpty <code>true</code> if the operation should send an empty
686 * packet or not send anything if there is no data to send
687 * @param inStream <code>true</code> if the stream is input stream or is
688 * output stream
689 * @throws IOException if an IO error occurs
690 */
691 public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
692 throws IOException {
693
694 // One path to the first put operation - the other one does not need to
695 // handle SRM, as all will fit into one packet.
696
697 if (mGetOperation) {
698 if ((inStream) && (!mOperationDone)) {
699 // to deal with inputstream in get operation
700 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
701 null, mReplyHeader, mPrivateInput, mSrmActive);
702 /*
703 * Determine if that was not the last packet in the operation
704 */
705 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
706 mOperationDone = true;
707 } else {
708 checkForSrm();
709 }
710
711 return true;
712
713 } else if ((!inStream) && (!mOperationDone)) {
714 // to deal with outputstream in get operation
715
716 if (mPrivateInput == null) {
717 mPrivateInput = new PrivateInputStream(this);
718 }
719
720 if (!mGetFinalFlag) {
721 sendRequest(ObexHelper.OBEX_OPCODE_GET);
722 } else {
723 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
724 }
725 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
726 mOperationDone = true;
727 }
728 return true;
729
730 } else if (mOperationDone) {
731 return false;
732 }
733
734 } else {
735 // PUT operation
736 if ((!inStream) && (!mOperationDone)) {
737 // to deal with outputstream in put operation
738 if (mReplyHeader.responseCode == -1) {
739 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
740 }
741 sendRequest(ObexHelper.OBEX_OPCODE_PUT);
742 return true;
743 } else if ((inStream) && (!mOperationDone)) {
744 // How to deal with inputstream in put operation ?
745 return false;
746
747 } else if (mOperationDone) {
748 return false;
749 }
750
751 }
752 return false;
753 }
754
755 /**
756 * Called when the output or input stream is closed.
757 * @param inStream <code>true</code> if the input stream is closed;
758 * <code>false</code> if the output stream is closed
759 * @throws IOException if an IO error occurs
760 */
761 public void streamClosed(boolean inStream) throws IOException {
762 if (!mGetOperation) {
763 if ((!inStream) && (!mOperationDone)) {
764 // to deal with outputstream in put operation
765
766 boolean more = true;
767
768 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
769 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
770 if (headerArray.length <= 0)
771 more = false;
772 }
773 // If have not sent any data so send all now
774 if (mReplyHeader.responseCode == -1) {
775 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
776 }
777
778 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
779 more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
780 }
781
782 /*
783 * According to the IrOBEX specification, after the final put, you
784 * only have a single reply to send. so we don't need the while
785 * loop.
786 */
787 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
788
789 sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
790 }
791 mOperationDone = true;
792 } else if ((inStream) && (mOperationDone)) {
793 // how to deal with input stream in put stream ?
794 mOperationDone = true;
795 }
796 } else {
797 if ((inStream) && (!mOperationDone)) {
798
799 // to deal with inputstream in get operation
800 // Have not sent any data so send it all now
801
802 if (mReplyHeader.responseCode == -1) {
803 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
804 }
805
806 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
807 if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
808 break;
809 }
810 }
811 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
812 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
813 mReplyHeader, mPrivateInput, false);
814 // Regardless of the SRM state, wait for the response.
815 }
816 mOperationDone = true;
817 } else if ((!inStream) && (!mOperationDone)) {
818 // to deal with outputstream in get operation
819 // part of the data may have been sent in continueOperation.
820
821 boolean more = true;
822
823 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
824 byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
825 if (headerArray.length <= 0)
826 more = false;
827 }
828
829 if (mPrivateInput == null) {
830 mPrivateInput = new PrivateInputStream(this);
831 }
832 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
833 more = false;
834
835 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
836 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
837 more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
838 }
839 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
840 // parent.sendRequest(0x83, null, replyHeaders, privateInput);
841 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
842 mOperationDone = true;
843 }
844 }
845 }
846 }
847
848 public void noBodyHeader(){
849 mSendBodyHeader = false;
850 }
851}