19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/*
2238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde * Copyright (c) 2015 The Android Open Source Project
3238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde * Copyright (C) 2015 Samsung LSI
49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Copyright (c) 2008-2009, Motorola, Inc.
59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * All rights reserved.
79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Redistribution and use in source and binary forms, with or without
99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * modification, are permitted provided that the following conditions are met:
109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions of source code must retain the above copyright notice,
129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer.
139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions in binary form must reproduce the above copyright notice,
159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer in the documentation
169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * and/or other materials provided with the distribution.
179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Neither the name of the Motorola, Inc. nor the names of its contributors
199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * may be used to endorse or promote products derived from this software
209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * without specific prior written permission.
219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * POSSIBILITY OF SUCH DAMAGE.
339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */
349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellypackage javax.obex;
369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyimport java.io.ByteArrayOutputStream;
389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyimport java.io.IOException;
399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyimport java.io.InputStream;
409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyimport java.io.OutputStream;
419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
42238e0f934f1f47263b384bc745ae0678c777130dCasper Bondeimport android.util.Log;
43238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/**
453998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * This class in an implementation of the OBEX ClientSession.
462e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @hide
479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */
483998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejunpublic final class ClientSession extends ObexSession {
499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
50238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    private static final String TAG = "ClientSession";
51238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
5241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private boolean mOpen;
539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
542e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    // Determines if an OBEX layer connection has been established
5541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private boolean mObexConnected;
569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private byte[] mConnectionId = null;
589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /*
60238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * The max Packet size must be at least 255 according to the OBEX
6141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     * specification.
6241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     */
63238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private boolean mRequestActive;
669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private final InputStream mInput;
689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private final OutputStream mOutput;
709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
71238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    private final boolean mLocalSrmSupported;
72238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
73238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    private final ObexTransport mTransport;
74238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
753998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    public ClientSession(final ObexTransport trans) throws IOException {
7641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mInput = trans.openInputStream();
7741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mOutput = trans.openOutputStream();
7841557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mOpen = true;
7941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mRequestActive = false;
80238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mLocalSrmSupported = trans.isSrmSupported();
81238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mTransport = trans;
82238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    }
83238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
84238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    /**
85238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * Create a ClientSession
86238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * @param trans The transport to use for OBEX transactions
87238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * @param supportsSrm True if Single Response Mode should be used e.g. if the
88238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     *        supplied transport is a TCP or l2cap channel.
89238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * @throws IOException if it occurs while opening the transport streams.
90238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     */
91238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
92238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mInput = trans.openInputStream();
93238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mOutput = trans.openOutputStream();
94238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mOpen = true;
95238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mRequestActive = false;
96238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mLocalSrmSupported = supportsSrm;
97238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        mTransport = trans;
989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1003998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    public HeaderSet connect(final HeaderSet header) throws IOException {
1019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ensureOpen();
10241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mObexConnected) {
1039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Already connected to server");
1049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
10541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestActive();
10641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly
1079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int totalLength = 4;
1089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] head = null;
1099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Determine the header byte array
1119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (header != null) {
11241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            if (header.nonce != null) {
11341557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                mChallengeDigest = new byte[16];
11441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
1159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
1162e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly            head = ObexHelper.createHeader(header, false);
1179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            totalLength += head.length;
1189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
1199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
1209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Write the OBEX CONNECT packet to the server.
1219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 0: 0x80
1229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 1&2: Connect Packet Length
1239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 3: OBEX Version Number (Presently, 0x10)
1249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 4: Flags (For TCP 0x00)
1259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
1269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 7 to n: headers
1279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        */
1289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] requestPacket = new byte[totalLength];
129238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
1309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // We just need to start at  byte 3 since the sendRequest() method will
1319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // handle the length and 0x80.
1329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        requestPacket[0] = (byte)0x10;
1339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        requestPacket[1] = (byte)0x00;
134238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        requestPacket[2] = (byte)(maxRxPacketSize >> 8);
135238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
1369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (head != null) {
1379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            System.arraycopy(head, 0, requestPacket, 4, head.length);
1389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
1399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
140238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        // Since we are not yet connected, the peer max packet size is unknown,
141238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        // hence we are only guaranteed the server will use the first 7 bytes.
1422e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly        if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
143238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            throw new IOException("Packet size exceeds max packet size for connect");
1449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
1459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        HeaderSet returnHeaderSet = new HeaderSet();
147238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
1489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
1509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Read the response from the OBEX server.
1519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
1529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 1&2: Packet Length
1539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 3: OBEX Version Number
1549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 4: Flags3
1559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 5&6: Max OBEX packet Length
1569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        * Byte 7 to n: Optional HeaderSet
1579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        */
1589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
15941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            mObexConnected = true;
1609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
16141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestInactive();
1629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return returnHeaderSet;
1649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
1659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
16641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    public Operation get(HeaderSet header) throws IOException {
1679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
16841557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (!mObexConnected) {
1699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Not connected to the server");
1709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
17141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestActive();
17241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly
1739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ensureOpen();
1749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1753998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        HeaderSet head;
1769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (header == null) {
1773998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head = new HeaderSet();
1789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
1793998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head = header;
1803998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            if (head.nonce != null) {
18141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                mChallengeDigest = new byte[16];
1823998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
1839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
1849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
1859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Add the connection ID if one exists
18641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mConnectionId != null) {
1873998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head.mConnectionID = new byte[4];
1883998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
1899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
1909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
191238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if(mLocalSrmSupported) {
192238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
193238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            /* TODO: Consider creating an interface to get the wait state.
194238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde             * On an android system, I cannot see when this is to be used.
195238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde             * except perhaps if we are to wait for user accept on a push message.
196238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if(getLocalWaitState()) {
197238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
198238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            }
199238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            */
200238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        }
201238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
202238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        return new ClientOperation(mMaxTxPacketSize, this, head, true);
2039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
20605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * 0xCB Connection Id an identifier used for OBEX connection multiplexing
20741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     */
2089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public void setConnectionID(long id) {
2099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if ((id < 0) || (id > 0xFFFFFFFFL)) {
2109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IllegalArgumentException("Connection ID is not in a valid range");
2119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
21241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mConnectionId = ObexHelper.convertToByteArray(id);
2139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
21541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    public HeaderSet delete(HeaderSet header) throws IOException {
2169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
21741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        Operation op = put(header);
2189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        op.getResponseCode();
2193998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        HeaderSet returnValue = op.getReceivedHeader();
2209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        op.close();
2219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return returnValue;
2239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
22541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    public HeaderSet disconnect(HeaderSet header) throws IOException {
22641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (!mObexConnected) {
2279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Not connected to the server");
2289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
22941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestActive();
23041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly
2319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ensureOpen();
2329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Determine the header byte array
2339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] head = null;
2349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (header != null) {
23541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            if (header.nonce != null) {
23641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                mChallengeDigest = new byte[16];
23741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
2389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
2399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Add the connection ID if one exists
24041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            if (mConnectionId != null) {
2413998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                header.mConnectionID = new byte[4];
2423998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                System.arraycopy(mConnectionId, 0, header.mConnectionID, 0, 4);
2439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
2442e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly            head = ObexHelper.createHeader(header, false);
2459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
246238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if ((head.length + 3) > mMaxTxPacketSize) {
2479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                throw new IOException("Packet size exceeds max packet size");
2489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
2499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
2509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Add the connection ID if one exists
25141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            if (mConnectionId != null) {
2529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                head = new byte[5];
2533998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                head[0] = (byte)HeaderSet.CONNECTION_ID;
25441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                System.arraycopy(mConnectionId, 0, head, 1, 4);
2559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
2569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
2579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        HeaderSet returnHeaderSet = new HeaderSet();
259238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
2609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
26241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly         * An OBEX DISCONNECT reply from the server:
26341557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly         * Byte 1: Response code
26441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly         * Bytes 2 & 3: packet size
26541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly         * Bytes 4 & up: headers
26641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly         */
2679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /* response code , and header are ignored
2699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * */
2709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        synchronized (this) {
27241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            mObexConnected = false;
27341557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            setRequestInactive();
2749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
2759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return returnHeaderSet;
2779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public long getConnectionID() {
2809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
28141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mConnectionId == null) {
2829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return -1;
2839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
28441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        return ObexHelper.convertToLong(mConnectionId);
2859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
28741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    public Operation put(HeaderSet header) throws IOException {
28841557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (!mObexConnected) {
2899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Not connected to the server");
2909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
29141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestActive();
2929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ensureOpen();
2943998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        HeaderSet head;
2959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (header == null) {
2963998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head = new HeaderSet();
2979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
2983998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head = header;
2993998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            // when auth is initiated by client ,save the digest
3003998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            if (head.nonce != null) {
30141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                mChallengeDigest = new byte[16];
3023998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
3039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Add the connection ID if one exists
30741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mConnectionId != null) {
3089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3093998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            head.mConnectionID = new byte[4];
3103998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
3119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
313238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if(mLocalSrmSupported) {
314238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
315238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            /* TODO: Consider creating an interface to get the wait state.
316238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde             * On an android system, I cannot see when this is to be used.
317238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if(getLocalWaitState()) {
318238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
319238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            }
320238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde             */
321238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        }
322238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        return new ClientOperation(mMaxTxPacketSize, this, head, false);
3239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
3249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3253998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    public void setAuthenticator(Authenticator auth) throws IOException {
3269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (auth == null) {
3273998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            throw new IOException("Authenticator may not be null");
3289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
32941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mAuthenticator = auth;
3309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
3319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3323998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    public HeaderSet setPath(HeaderSet header, boolean backup, boolean create) throws IOException {
33341557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (!mObexConnected) {
3349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Not connected to the server");
3359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
33641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestActive();
3379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ensureOpen();
3389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int totalLength = 2;
3409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] head = null;
3413998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        HeaderSet headset;
3429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (header == null) {
3433998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            headset = new HeaderSet();
3449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
3453998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            headset = header;
3463998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            if (headset.nonce != null) {
34741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly                mChallengeDigest = new byte[16];
3483998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun                System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
3499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // when auth is initiated by client ,save the digest
3533998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        if (headset.nonce != null) {
35441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            mChallengeDigest = new byte[16];
3553998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
3569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Add the connection ID if one exists
35941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mConnectionId != null) {
3603998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            headset.mConnectionID = new byte[4];
3613998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun            System.arraycopy(mConnectionId, 0, headset.mConnectionID, 0, 4);
3629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3643998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        head = ObexHelper.createHeader(headset, false);
3659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        totalLength += head.length;
3669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
367238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if (totalLength > mMaxTxPacketSize) {
3689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Packet size exceeds max packet size");
3699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int flags = 0;
3729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
3739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * The backup flag bit is bit 0 so if we add 1, this will set that bit
3749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
3759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (backup) {
3769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            flags++;
3779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
3799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * The create bit is bit 1 so if we or with 2 the bit will be set.
3809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
3819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (!create) {
3829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            flags |= 2;
3839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
3869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * An OBEX SETPATH packet to the server:
3879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 1: 0x85
3889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 2 & 3: packet size
3899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 4: flags
3909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 5: constants
3919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 6 & up: headers
3929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
3939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] packet = new byte[totalLength];
3949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        packet[0] = (byte)flags;
3959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        packet[1] = (byte)0x00;
3963998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        if (headset != null) {
3979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            System.arraycopy(head, 0, packet, 2, head.length);
3989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        HeaderSet returnHeaderSet = new HeaderSet();
401238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
4029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
4049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * An OBEX SETPATH reply from the server:
4059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Byte 1: Response code
4069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Bytes 2 & 3: packet size
4079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Bytes 4 & up: headers
4089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
4099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
41041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        setRequestInactive();
4119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return returnHeaderSet;
4139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
4149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
4169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Verifies that the connection is open.
4172e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * @throws IOException if the connection is closed
4189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
4199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public synchronized void ensureOpen() throws IOException {
42041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (!mOpen) {
4219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Connection closed");
4229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
4239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
4249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
42605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * Set request inactive. Allows Put and get operation objects to tell this
42705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * object when they are done.
4289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
4293998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    /*package*/synchronized void setRequestInactive() {
43041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mRequestActive = false;
43141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    }
43241557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly
43341557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    /**
43441557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     * Set request to active.
43541557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     * @throws IOException if already active
43641557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     */
43741557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly    private synchronized void setRequestActive() throws IOException {
43841557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        if (mRequestActive) {
43941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly            throw new IOException("OBEX request is already being performed");
4409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
44141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mRequestActive = true;
4429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
4439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
44505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * Sends a standard request to the client. It will then wait for the reply
44605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * and update the header set object provided. If any authentication headers
44705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * (i.e. authentication challenge or authentication response) are received,
44805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * they will be processed.
4493998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun     * @param opCode the type of request to send to the client
45005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * @param head the headers to send to the client
45141557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly     * @param header the header object to update with the response
45205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     * @param privateInput the input stream used by the Operation object; null
453238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     *        if this is called on a CONNECT, SETPATH or DISCONNECT
454238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde     * @return
45505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     *        <code>true</code> if the operation completed successfully;
45605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun     *        <code>false</code> if an authentication response failed to pass
4572e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * @throws IOException if an IO error occurs
4589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
4593998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun    public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
460238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            PrivateInputStream privateInput, boolean srmActive) throws IOException {
4619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        //check header length with local max size
4629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (head != null) {
4632e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly            if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
464238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // TODO: This is an implementation limit - not a specification requirement.
4659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                throw new IOException("header too large ");
4669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
4679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
4683998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun
469238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        boolean skipSend = false;
470238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        boolean skipReceive = false;
471238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if (srmActive == true) {
472238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
473238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // we are in the middle of a SRM PUT operation, don't expect a continue.
474238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                skipReceive = true;
475238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            } else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
476238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // We are still sending the get request, send, but don't expect continue
477238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // until the request is transfered (the final bit is set)
478238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                skipReceive = true;
479238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            } else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
480238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // All done sending the request, expect data from the server, without
481238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                // sending continue.
482238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                skipSend = true;
483238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            }
484238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
485238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        }
486238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
4879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int bytesReceived;
4889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ByteArrayOutputStream out = new ByteArrayOutputStream();
4893998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun        out.write((byte)opCode);
4909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Determine if there are any headers to send
4929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (head == null) {
4939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            out.write(0x00);
4949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            out.write(0x03);
4959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
4969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            out.write((byte)((head.length + 3) >> 8));
4979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            out.write((byte)(head.length + 3));
4989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            out.write(head);
4999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
5009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
501238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if (!skipSend) {
502238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            // Write the request to the output stream and flush the stream
503238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            mOutput.write(out.toByteArray());
504238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            // TODO: is this really needed? if this flush is implemented
505238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            //       correctly, we will get a gap between each obex packet.
506238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            //       which is kind of the idea behind SRM to avoid.
507238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            //  Consider offloading to another thread (async action)
508238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            mOutput.flush();
509238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        }
5109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
511238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        if (!skipReceive) {
512238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            header.responseCode = mInput.read();
5139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
514238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            int length = ((mInput.read() << 8) | (mInput.read()));
5159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
516238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
517238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                throw new IOException("Packet received exceeds packet size limit");
518238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            }
519238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde            if (length > ObexHelper.BASE_PACKET_LENGTH) {
520238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                byte[] data = null;
521238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
522238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    @SuppressWarnings("unused")
523238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    int version = mInput.read();
524238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    @SuppressWarnings("unused")
525238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    int flags = mInput.read();
526238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
527238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
528238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    //check with local max size
529238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
530238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
531238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
5329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
533238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    // check with transport maximum size
534238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
535238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        // To increase this size, increase the buffer size in L2CAP layer
536238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        // in Bluedroid.
537238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
538238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                                + " requested. Transport only allows: "
539238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                                + ObexHelper.getMaxTxPacketSize(mTransport)
540238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                                + " Lowering limit to this value.");
541238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
542238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
5439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
544238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if (length > 7) {
545238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        data = new byte[length - 7];
5469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
547238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        bytesReceived = mInput.read(data);
548238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        while (bytesReceived != (length - 7)) {
549238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                            bytesReceived += mInput.read(data, bytesReceived, data.length
550238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                                    - bytesReceived);
551238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        }
552238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    } else {
553238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        return true;
5549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    }
5559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                } else {
556238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    data = new byte[length - 3];
557238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    bytesReceived = mInput.read(data);
5589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
559238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    while (bytesReceived != (length - 3)) {
560238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
561238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
562238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
563238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        return true;
564238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
5659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
567238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                byte[] body = ObexHelper.updateHeaderSet(header, data);
568238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                if ((privateInput != null) && (body != null)) {
569238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    privateInput.writeBytes(body, 1);
570238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                }
5719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
572238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                if (header.mConnectionID != null) {
573238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    mConnectionId = new byte[4];
574238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
575238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                }
5769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
577238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                if (header.mAuthResp != null) {
578238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if (!handleAuthResp(header.mAuthResp)) {
579238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        setRequestInactive();
580238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        throw new IOException("Authentication Failed");
581238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
5829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
584238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
585238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        && (header.mAuthChall != null)) {
5869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
587238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    if (handleAuthChall(header)) {
588238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        out.write((byte)HeaderSet.AUTH_RESPONSE);
589238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        out.write((byte)((header.mAuthResp.length + 3) >> 8));
590238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        out.write((byte)(header.mAuthResp.length + 3));
591238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        out.write(header.mAuthResp);
592238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        header.mAuthChall = null;
593238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        header.mAuthResp = null;
5949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
595238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        byte[] sendHeaders = new byte[out.size() - 3];
596238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
5979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
598238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                        return sendRequest(opCode, sendHeaders, header, privateInput, false);
599238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde                    }
6009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
6019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
6029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
6039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return true;
6059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
6069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public void close() throws IOException {
60841557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mOpen = false;
60941557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mInput.close();
61041557e18fa3fc5791a5a1c7f0051ae3385608bc4Nick Pelly        mOutput.close();
6119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
612238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde
613238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    public boolean isSrmSupported() {
614238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde        return mLocalSrmSupported;
615238e0f934f1f47263b384bc745ae0678c777130dCasper Bonde    }
6169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
617