Remove obex library code from frameworks/base.
The obex library has been migrated to platform/external/obex as it uses the BSD license and should not be in any non-3p repo. Only the ObexPacket class is retained as it is a public API. Bug: 219056000 Bug: 220211554 Test: Manual Change-Id: If279fda41b7790abe0f25d9ebd8ea4c6fb58cbbb
This commit is contained in:
parent
deb79e3070
commit
af8baab515
@ -44,6 +44,8 @@ license {
|
||||
],
|
||||
}
|
||||
|
||||
// No longer used. Only kept because the ObexPacket class is a public API.
|
||||
// The library has been migrated to platform/external/obex.
|
||||
java_sdk_library {
|
||||
name: "javax.obex",
|
||||
srcs: ["javax/**/*.java"],
|
||||
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class ApplicationParameter {
|
||||
|
||||
private byte[] mArray;
|
||||
|
||||
private int mLength;
|
||||
|
||||
private int mMaxLength = 1000;
|
||||
|
||||
public static class TRIPLET_TAGID {
|
||||
public static final byte ORDER_TAGID = 0x01;
|
||||
|
||||
public static final byte SEARCH_VALUE_TAGID = 0x02;
|
||||
|
||||
public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
|
||||
|
||||
// if equals to "0", PSE only reply number of contacts
|
||||
public static final byte MAXLISTCOUNT_TAGID = 0x04;
|
||||
|
||||
public static final byte LISTSTARTOFFSET_TAGID = 0x05;
|
||||
|
||||
public static final byte PROPERTY_SELECTOR_TAGID = 0x06;
|
||||
|
||||
public static final byte FORMAT_TAGID = 0x07;
|
||||
|
||||
// only used if max list count = 0
|
||||
public static final byte PHONEBOOKSIZE_TAGID = 0x08;
|
||||
|
||||
// only used in "mch" in response
|
||||
public static final byte NEWMISSEDCALLS_TAGID = 0x09;
|
||||
|
||||
public static final byte SUPPORTEDFEATURE_TAGID = 0x10;
|
||||
|
||||
public static final byte PRIMARYVERSIONCOUNTER_TAGID = 0x0A;
|
||||
|
||||
public static final byte SECONDARYVERSIONCOUNTER_TAGID = 0x0B;
|
||||
|
||||
public static final byte VCARDSELECTOR_TAGID = 0x0C;
|
||||
|
||||
public static final byte DATABASEIDENTIFIER_TAGID = 0x0D;
|
||||
|
||||
public static final byte VCARDSELECTOROPERATOR_TAGID = 0x0E;
|
||||
|
||||
public static final byte RESET_NEW_MISSED_CALLS_TAGID = 0x0F;
|
||||
}
|
||||
|
||||
public static class TRIPLET_VALUE {
|
||||
public static class ORDER {
|
||||
public static final byte ORDER_BY_INDEX = 0x00;
|
||||
|
||||
public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
|
||||
|
||||
public static final byte ORDER_BY_PHONETIC = 0x02;
|
||||
}
|
||||
|
||||
public static class SEARCHATTRIBUTE {
|
||||
public static final byte SEARCH_BY_NAME = 0x00;
|
||||
|
||||
public static final byte SEARCH_BY_NUMBER = 0x01;
|
||||
|
||||
public static final byte SEARCH_BY_SOUND = 0x02;
|
||||
}
|
||||
|
||||
public static class FORMAT {
|
||||
public static final byte VCARD_VERSION_21 = 0x00;
|
||||
|
||||
public static final byte VCARD_VERSION_30 = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TRIPLET_LENGTH {
|
||||
public static final byte ORDER_LENGTH = 1;
|
||||
|
||||
public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
|
||||
|
||||
public static final byte MAXLISTCOUNT_LENGTH = 2;
|
||||
|
||||
public static final byte LISTSTARTOFFSET_LENGTH = 2;
|
||||
|
||||
public static final byte PROPERTY_SELECTOR_LENGTH = 8;
|
||||
|
||||
public static final byte FORMAT_LENGTH = 1;
|
||||
|
||||
public static final byte PHONEBOOKSIZE_LENGTH = 2;
|
||||
|
||||
public static final byte NEWMISSEDCALLS_LENGTH = 1;
|
||||
|
||||
public static final byte SUPPORTEDFEATURE_LENGTH = 4;
|
||||
|
||||
public static final byte PRIMARYVERSIONCOUNTER_LENGTH = 16;
|
||||
|
||||
public static final byte SECONDARYVERSIONCOUNTER_LENGTH = 16;
|
||||
|
||||
public static final byte VCARDSELECTOR_LENGTH = 8;
|
||||
|
||||
public static final byte DATABASEIDENTIFIER_LENGTH = 16;
|
||||
|
||||
public static final byte VCARDSELECTOROPERATOR_LENGTH = 1;
|
||||
|
||||
public static final byte RESETNEWMISSEDCALLS_LENGTH = 1;
|
||||
}
|
||||
|
||||
public ApplicationParameter() {
|
||||
mArray = new byte[mMaxLength];
|
||||
mLength = 0;
|
||||
}
|
||||
|
||||
public void addAPPHeader(byte tag, byte len, byte[] value) {
|
||||
if ((mLength + len + 2) > mMaxLength) {
|
||||
byte[] array_tmp = new byte[mLength + 4 * len];
|
||||
System.arraycopy(mArray, 0, array_tmp, 0, mLength);
|
||||
mArray = array_tmp;
|
||||
mMaxLength = mLength + 4 * len;
|
||||
}
|
||||
mArray[mLength++] = tag;
|
||||
mArray[mLength++] = len;
|
||||
System.arraycopy(value, 0, mArray, mLength, len);
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
public byte[] getAPPparam() {
|
||||
byte[] para = new byte[mLength];
|
||||
System.arraycopy(mArray, 0, para, 0, mLength);
|
||||
return para;
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
/**
|
||||
* This interface provides a way to respond to authentication challenge and
|
||||
* authentication response headers. When a client or server receives an
|
||||
* authentication challenge or authentication response header, the
|
||||
* <code>onAuthenticationChallenge()</code> or
|
||||
* <code>onAuthenticationResponse()</code> will be called, respectively, by the
|
||||
* implementation.
|
||||
* <P>
|
||||
* For more information on how the authentication procedure works in OBEX,
|
||||
* please review the IrOBEX specification at <A
|
||||
* HREF="http://www.irda.org">http://www.irda.org</A>.
|
||||
* <P>
|
||||
* <STRONG>Authentication Challenges</STRONG>
|
||||
* <P>
|
||||
* When a client or server receives an authentication challenge header, the
|
||||
* <code>onAuthenticationChallenge()</code> method will be invoked by the OBEX
|
||||
* API implementation. The application will then return the user name (if
|
||||
* needed) and password via a <code>PasswordAuthentication</code> object. The
|
||||
* password in this object is not sent in the authentication response. Instead,
|
||||
* the 16-byte challenge received in the authentication challenge is combined
|
||||
* with the password returned from the <code>onAuthenticationChallenge()</code>
|
||||
* method and passed through the MD5 hash algorithm. The resulting value is sent
|
||||
* in the authentication response along with the user name if it was provided.
|
||||
* <P>
|
||||
* <STRONG>Authentication Responses</STRONG>
|
||||
* <P>
|
||||
* When a client or server receives an authentication response header, the
|
||||
* <code>onAuthenticationResponse()</code> method is invoked by the API
|
||||
* implementation with the user name received in the authentication response
|
||||
* header. (The user name will be <code>null</code> if no user name was provided
|
||||
* in the authentication response header.) The application must determine the
|
||||
* correct password. This value should be returned from the
|
||||
* <code>onAuthenticationResponse()</code> method. If the authentication request
|
||||
* should fail without the implementation checking the password,
|
||||
* <code>null</code> should be returned by the application. (This is needed for
|
||||
* reasons like not recognizing the user name, etc.) If the returned value is
|
||||
* not <code>null</code>, the OBEX API implementation will combine the password
|
||||
* returned from the <code>onAuthenticationResponse()</code> method and
|
||||
* challenge sent via the authentication challenge, apply the MD5 hash
|
||||
* algorithm, and compare the result to the response hash received in the
|
||||
* authentication response header. If the values are not equal, an
|
||||
* <code>IOException</code> will be thrown if the client requested
|
||||
* authentication. If the server requested authentication, the
|
||||
* <code>onAuthenticationFailure()</code> method will be called on the
|
||||
* <code>ServerRequestHandler</code> that failed authentication. The connection
|
||||
* is <B>not</B> closed if authentication failed.
|
||||
* @hide
|
||||
*/
|
||||
public interface Authenticator {
|
||||
|
||||
/**
|
||||
* Called when a client or a server receives an authentication challenge
|
||||
* header. It should respond to the challenge with a
|
||||
* <code>PasswordAuthentication</code> that contains the correct user name
|
||||
* and password for the challenge.
|
||||
* @param description the description of which user name and password should
|
||||
* be used; if no description is provided in the authentication
|
||||
* challenge or the description is encoded in an encoding scheme that
|
||||
* is not supported, an empty string will be provided
|
||||
* @param isUserIdRequired <code>true</code> if the user ID is required;
|
||||
* <code>false</code> if the user ID is not required
|
||||
* @param isFullAccess <code>true</code> if full access to the server will
|
||||
* be granted; <code>false</code> if read only access will be granted
|
||||
* @return a <code>PasswordAuthentication</code> object containing the user
|
||||
* name and password used for authentication
|
||||
*/
|
||||
PasswordAuthentication onAuthenticationChallenge(String description, boolean isUserIdRequired,
|
||||
boolean isFullAccess);
|
||||
|
||||
/**
|
||||
* Called when a client or server receives an authentication response
|
||||
* header. This method will provide the user name and expect the correct
|
||||
* password to be returned.
|
||||
* @param userName the user name provided in the authentication response; may
|
||||
* be <code>null</code>
|
||||
* @return the correct password for the user name provided; if
|
||||
* <code>null</code> is returned then the authentication request
|
||||
* failed
|
||||
*/
|
||||
byte[] onAuthenticationResponse(byte[] userName);
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This interface defines the methods needed by a parent that uses the
|
||||
* PrivateInputStream and PrivateOutputStream objects defined in this package.
|
||||
* @hide
|
||||
*/
|
||||
public interface BaseStream {
|
||||
|
||||
/**
|
||||
* Verifies that this object is still open.
|
||||
* @throws IOException if the object is closed
|
||||
*/
|
||||
void ensureOpen() throws IOException;
|
||||
|
||||
/**
|
||||
* Verifies that additional information may be sent. In other words, the
|
||||
* operation is not done.
|
||||
* @throws IOException if the operation is completed
|
||||
*/
|
||||
void ensureNotDone() throws IOException;
|
||||
|
||||
/**
|
||||
* Continues the operation since there is no data to read.
|
||||
* @param sendEmpty <code>true</code> if the operation should send an empty
|
||||
* packet or not send anything if there is no data to send
|
||||
* @param inStream <code>true</code> if the stream is input stream or is
|
||||
* output stream
|
||||
* @return <code>true</code> if the operation was completed;
|
||||
* <code>false</code> if no operation took place
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Called when the output or input stream is closed.
|
||||
* @param inStream <code>true</code> if the input stream is closed;
|
||||
* <code>false</code> if the output stream is closed
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
void streamClosed(boolean inStream) throws IOException;
|
||||
}
|
@ -1,851 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2015 Samsung LSI
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class implements the <code>Operation</code> interface. It will read and
|
||||
* write data via puts and gets.
|
||||
* @hide
|
||||
*/
|
||||
public final class ClientOperation implements Operation, BaseStream {
|
||||
|
||||
private static final String TAG = "ClientOperation";
|
||||
|
||||
private static final boolean V = ObexHelper.VDBG;
|
||||
|
||||
private ClientSession mParent;
|
||||
|
||||
private boolean mInputOpen;
|
||||
|
||||
private PrivateInputStream mPrivateInput;
|
||||
|
||||
private boolean mPrivateInputOpen;
|
||||
|
||||
private PrivateOutputStream mPrivateOutput;
|
||||
|
||||
private boolean mPrivateOutputOpen;
|
||||
|
||||
private String mExceptionMessage;
|
||||
|
||||
private int mMaxPacketSize;
|
||||
|
||||
private boolean mOperationDone;
|
||||
|
||||
private boolean mGetOperation;
|
||||
|
||||
private boolean mGetFinalFlag;
|
||||
|
||||
private HeaderSet mRequestHeader;
|
||||
|
||||
private HeaderSet mReplyHeader;
|
||||
|
||||
private boolean mEndOfBodySent;
|
||||
|
||||
private boolean mSendBodyHeader = true;
|
||||
// A latch - when triggered, there is not way back ;-)
|
||||
private boolean mSrmActive = false;
|
||||
|
||||
// Assume SRM disabled - until support is confirmed
|
||||
// by the server
|
||||
private boolean mSrmEnabled = false;
|
||||
// keep waiting until final-bit is received in request
|
||||
// to handle the case where the SRM enable header is in
|
||||
// a different OBEX packet than the SRMP header.
|
||||
private boolean mSrmWaitingForRemote = true;
|
||||
|
||||
|
||||
/**
|
||||
* Creates new OperationImpl to read and write data to a server
|
||||
* @param maxSize the maximum packet size
|
||||
* @param p the parent to this object
|
||||
* @param type <code>true</code> if this is a get request;
|
||||
* <code>false</code. if this is a put request
|
||||
* @param header the header to set in the initial request
|
||||
* @throws IOException if the an IO error occurred
|
||||
*/
|
||||
public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
|
||||
throws IOException {
|
||||
|
||||
mParent = p;
|
||||
mEndOfBodySent = false;
|
||||
mInputOpen = true;
|
||||
mOperationDone = false;
|
||||
mMaxPacketSize = maxSize;
|
||||
mGetOperation = type;
|
||||
mGetFinalFlag = false;
|
||||
|
||||
mPrivateInputOpen = false;
|
||||
mPrivateOutputOpen = false;
|
||||
mPrivateInput = null;
|
||||
mPrivateOutput = null;
|
||||
|
||||
mReplyHeader = new HeaderSet();
|
||||
|
||||
mRequestHeader = new HeaderSet();
|
||||
|
||||
int[] headerList = header.getHeaderList();
|
||||
|
||||
if (headerList != null) {
|
||||
|
||||
for (int i = 0; i < headerList.length; i++) {
|
||||
mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if ((header).mAuthChall != null) {
|
||||
mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
|
||||
System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
|
||||
(header).mAuthChall.length);
|
||||
}
|
||||
|
||||
if ((header).mAuthResp != null) {
|
||||
mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
|
||||
System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
|
||||
(header).mAuthResp.length);
|
||||
|
||||
}
|
||||
|
||||
if ((header).mConnectionID != null) {
|
||||
mRequestHeader.mConnectionID = new byte[4];
|
||||
System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
|
||||
4);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set flag which will force GET to be always sent as single packet request with
|
||||
* final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
|
||||
* require requests to be sent this way.
|
||||
*/
|
||||
public void setGetFinalFlag(boolean flag) {
|
||||
mGetFinalFlag = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an ABORT message to the server. By calling this method, the
|
||||
* corresponding input and output streams will be closed along with this
|
||||
* object.
|
||||
* @throws IOException if the transaction has already ended or if an OBEX
|
||||
* server called this method
|
||||
*/
|
||||
public synchronized void abort() throws IOException {
|
||||
ensureOpen();
|
||||
//no compatible with sun-ri
|
||||
if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
throw new IOException("Operation has already ended");
|
||||
}
|
||||
|
||||
mExceptionMessage = "Operation aborted";
|
||||
if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
mOperationDone = true;
|
||||
/*
|
||||
* Since we are not sending any headers or returning any headers then
|
||||
* we just need to write and read the same bytes
|
||||
*/
|
||||
mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
|
||||
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
|
||||
throw new IOException("Invalid response code from server");
|
||||
}
|
||||
|
||||
mExceptionMessage = null;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the response code retrieved from the server. Response codes are
|
||||
* defined in the <code>ResponseCodes</code> interface.
|
||||
* @return the response code retrieved from the server
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the transaction; if this method is called on a
|
||||
* <code>HeaderSet</code> object created by calling
|
||||
* <code>createHeaderSet</code> in a <code>ClientSession</code>
|
||||
* object
|
||||
*/
|
||||
public synchronized int getResponseCode() throws IOException {
|
||||
if ((mReplyHeader.responseCode == -1)
|
||||
|| (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
validateConnection();
|
||||
}
|
||||
|
||||
return mReplyHeader.responseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will always return <code>null</code>
|
||||
* @return <code>null</code>
|
||||
*/
|
||||
public String getEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of content that the resource connected to is providing.
|
||||
* E.g. if the connection is via HTTP, then the value of the content-type
|
||||
* header field is returned.
|
||||
* @return the content type of the resource that the URL references, or
|
||||
* <code>null</code> if not known
|
||||
*/
|
||||
public String getType() {
|
||||
try {
|
||||
return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
|
||||
} catch (IOException e) {
|
||||
if(V) Log.d(TAG, "Exception occured - returning null",e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the content which is being provided. E.g. if the
|
||||
* connection is via HTTP, then the value of the content-length header field
|
||||
* is returned.
|
||||
* @return the content length of the resource that this connection's URL
|
||||
* references, or -1 if the content length is not known
|
||||
*/
|
||||
public long getLength() {
|
||||
try {
|
||||
Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
|
||||
|
||||
if (temp == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return temp.longValue();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if(V) Log.d(TAG,"Exception occured - returning -1",e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return an input stream for a connection.
|
||||
* @return an input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public InputStream openInputStream() throws IOException {
|
||||
|
||||
ensureOpen();
|
||||
|
||||
if (mPrivateInputOpen)
|
||||
throw new IOException("no more input streams available");
|
||||
if (mGetOperation) {
|
||||
// send the GET request here
|
||||
validateConnection();
|
||||
} else {
|
||||
if (mPrivateInput == null) {
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
}
|
||||
}
|
||||
|
||||
mPrivateInputOpen = true;
|
||||
|
||||
return mPrivateInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return a data input stream for a connection.
|
||||
* @return an input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public DataInputStream openDataInputStream() throws IOException {
|
||||
return new DataInputStream(openInputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return an output stream for a connection.
|
||||
* @return an output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
|
||||
ensureOpen();
|
||||
ensureNotDone();
|
||||
|
||||
if (mPrivateOutputOpen)
|
||||
throw new IOException("no more output streams available");
|
||||
|
||||
if (mPrivateOutput == null) {
|
||||
// there are 3 bytes operation headers and 3 bytes body headers //
|
||||
mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
|
||||
}
|
||||
|
||||
mPrivateOutputOpen = true;
|
||||
|
||||
return mPrivateOutput;
|
||||
}
|
||||
|
||||
public int getMaxPacketSize() {
|
||||
return mMaxPacketSize - 6 - getHeaderLength();
|
||||
}
|
||||
|
||||
public int getHeaderLength() {
|
||||
// OPP may need it
|
||||
byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
|
||||
return headerArray.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return a data output stream for a connection.
|
||||
* @return an output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public DataOutputStream openDataOutputStream() throws IOException {
|
||||
return new DataOutputStream(openOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection and ends the transaction
|
||||
* @throws IOException if the operation has already ended or is closed
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
mInputOpen = false;
|
||||
mPrivateInputOpen = false;
|
||||
mPrivateOutputOpen = false;
|
||||
mParent.setRequestInactive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the headers that have been received during the operation.
|
||||
* Modifying the object returned has no effect on the headers that are sent
|
||||
* or retrieved.
|
||||
* @return the headers received during this <code>Operation</code>
|
||||
* @throws IOException if this <code>Operation</code> has been closed
|
||||
*/
|
||||
public HeaderSet getReceivedHeader() throws IOException {
|
||||
ensureOpen();
|
||||
|
||||
return mReplyHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the headers that should be sent in the next OBEX message that
|
||||
* is sent.
|
||||
* @param headers the headers to send in the next message
|
||||
* @throws IOException if this <code>Operation</code> has been closed or the
|
||||
* transaction has ended and no further messages will be exchanged
|
||||
* @throws IllegalArgumentException if <code>headers</code> was not created
|
||||
* by a call to <code>ServerRequestHandler.createHeaderSet()</code>
|
||||
* @throws NullPointerException if <code>headers</code> is <code>null</code>
|
||||
*/
|
||||
public void sendHeaders(HeaderSet headers) throws IOException {
|
||||
ensureOpen();
|
||||
if (mOperationDone) {
|
||||
throw new IOException("Operation has already exchanged all data");
|
||||
}
|
||||
|
||||
if (headers == null) {
|
||||
throw new IOException("Headers may not be null");
|
||||
}
|
||||
|
||||
int[] headerList = headers.getHeaderList();
|
||||
if (headerList != null) {
|
||||
for (int i = 0; i < headerList.length; i++) {
|
||||
mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that additional information may be sent. In other words, the
|
||||
* operation is not done.
|
||||
* @throws IOException if the operation is completed
|
||||
*/
|
||||
public void ensureNotDone() throws IOException {
|
||||
if (mOperationDone) {
|
||||
throw new IOException("Operation has completed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the connection is open and no exceptions should be thrown.
|
||||
* @throws IOException if an exception needs to be thrown
|
||||
*/
|
||||
public void ensureOpen() throws IOException {
|
||||
mParent.ensureOpen();
|
||||
|
||||
if (mExceptionMessage != null) {
|
||||
throw new IOException(mExceptionMessage);
|
||||
}
|
||||
if (!mInputOpen) {
|
||||
throw new IOException("Operation has already ended");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the connection is open and the proper data has been read.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
private void validateConnection() throws IOException {
|
||||
ensureOpen();
|
||||
|
||||
// Make sure that a response has been recieved from remote
|
||||
// before continuing
|
||||
if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
|
||||
startProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the client of the specified type.
|
||||
* This function will enable SRM and set SRM active if the server
|
||||
* response allows this.
|
||||
* @param opCode the request code to send to the client
|
||||
* @return <code>true</code> if there is more data to send;
|
||||
* <code>false</code> if there is no more data to send
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
private boolean sendRequest(int opCode) throws IOException {
|
||||
boolean returnValue = false;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int bodyLength = -1;
|
||||
byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
|
||||
if (mPrivateOutput != null) {
|
||||
bodyLength = mPrivateOutput.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if there is space to add a body request. At present
|
||||
* this method checks to see if there is room for at least a 17
|
||||
* byte body header. This number needs to be at least 6 so that
|
||||
* there is room for the header ID and length and the reply ID and
|
||||
* length, but it is a waste of resources if we can't send much of
|
||||
* the body.
|
||||
*/
|
||||
final int MINIMUM_BODY_LENGTH = 3;
|
||||
if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
|
||||
> mMaxPacketSize) {
|
||||
int end = 0;
|
||||
int start = 0;
|
||||
// split & send the headerArray in multiple packets.
|
||||
|
||||
while (end != headerArray.length) {
|
||||
//split the headerArray
|
||||
|
||||
end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
|
||||
- ObexHelper.BASE_PACKET_LENGTH);
|
||||
// can not split
|
||||
if (end == -1) {
|
||||
mOperationDone = true;
|
||||
abort();
|
||||
mExceptionMessage = "Header larger then can be sent in a packet";
|
||||
mInputOpen = false;
|
||||
|
||||
if (mPrivateInput != null) {
|
||||
mPrivateInput.close();
|
||||
}
|
||||
|
||||
if (mPrivateOutput != null) {
|
||||
mPrivateOutput.close();
|
||||
}
|
||||
throw new IOException("OBEX Packet exceeds max packet size");
|
||||
}
|
||||
|
||||
byte[] sendHeader = new byte[end - start];
|
||||
System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
|
||||
if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start = end;
|
||||
}
|
||||
|
||||
// Enable SRM if it should be enabled
|
||||
checkForSrm();
|
||||
|
||||
if (bodyLength > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* All headers will fit into a single package */
|
||||
if(mSendBodyHeader == false) {
|
||||
/* As we are not to send any body data, set the FINAL_BIT */
|
||||
opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
|
||||
}
|
||||
out.write(headerArray);
|
||||
}
|
||||
|
||||
if (bodyLength > 0) {
|
||||
/*
|
||||
* Determine if we can send the whole body or just part of
|
||||
* the body. Remember that there is the 3 bytes for the
|
||||
* response message and 3 bytes for the header ID and length
|
||||
*/
|
||||
if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
|
||||
returnValue = true;
|
||||
|
||||
bodyLength = mMaxPacketSize - headerArray.length - 6;
|
||||
}
|
||||
|
||||
byte[] body = mPrivateOutput.readBytes(bodyLength);
|
||||
|
||||
/*
|
||||
* Since this is a put request if the final bit is set or
|
||||
* the output stream is closed we need to send the 0x49
|
||||
* (End of Body) otherwise, we need to send 0x48 (Body)
|
||||
*/
|
||||
if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
|
||||
&& ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
|
||||
out.write(HeaderSet.END_OF_BODY);
|
||||
mEndOfBodySent = true;
|
||||
} else {
|
||||
out.write(HeaderSet.BODY);
|
||||
}
|
||||
|
||||
bodyLength += 3;
|
||||
out.write((byte)(bodyLength >> 8));
|
||||
out.write((byte)bodyLength);
|
||||
|
||||
if (body != null) {
|
||||
out.write(body);
|
||||
}
|
||||
}
|
||||
|
||||
if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
|
||||
// only 0x82 or 0x83 can send 0x49
|
||||
if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
|
||||
out.write(HeaderSet.BODY);
|
||||
} else {
|
||||
out.write(HeaderSet.END_OF_BODY);
|
||||
mEndOfBodySent = true;
|
||||
}
|
||||
|
||||
bodyLength = 3;
|
||||
out.write((byte)(bodyLength >> 8));
|
||||
out.write((byte)bodyLength);
|
||||
}
|
||||
|
||||
if (out.size() == 0) {
|
||||
if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
|
||||
return false;
|
||||
}
|
||||
// Enable SRM if it should be enabled
|
||||
checkForSrm();
|
||||
return returnValue;
|
||||
}
|
||||
if ((out.size() > 0)
|
||||
&& (!mParent.sendRequest(opCode, out.toByteArray(),
|
||||
mReplyHeader, mPrivateInput, mSrmActive))) {
|
||||
return false;
|
||||
}
|
||||
// Enable SRM if it should be enabled
|
||||
checkForSrm();
|
||||
|
||||
// send all of the output data in 0x48,
|
||||
// send 0x49 with empty body
|
||||
if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
|
||||
returnValue = true;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private void checkForSrm() throws IOException {
|
||||
Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
|
||||
if(mParent.isSrmSupported() == true && srmMode != null
|
||||
&& srmMode == ObexHelper.OBEX_SRM_ENABLE) {
|
||||
mSrmEnabled = true;
|
||||
}
|
||||
/**
|
||||
* Call this only when a complete obex packet have been received.
|
||||
* (This is not optimal, but the current design is not really suited to
|
||||
* the way SRM is specified.)
|
||||
* The BT usage of SRM is not really safe - it assumes that the SRMP will fit
|
||||
* into every OBEX packet, hence if another header occupies the entire packet,
|
||||
* the scheme will not work - unlikely though.
|
||||
*/
|
||||
if(mSrmEnabled) {
|
||||
mSrmWaitingForRemote = false;
|
||||
Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
|
||||
if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
|
||||
mSrmWaitingForRemote = true;
|
||||
// Clear the wait header, as the absence of the header in the next packet
|
||||
// indicates don't wait anymore.
|
||||
mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
|
||||
}
|
||||
}
|
||||
if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
|
||||
mSrmActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method starts the processing thread results. It will send the
|
||||
* initial request. If the response takes more then one packet, a thread
|
||||
* will be started to handle additional requests
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
private synchronized void startProcessing() throws IOException {
|
||||
|
||||
if (mPrivateInput == null) {
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
}
|
||||
boolean more = true;
|
||||
|
||||
if (mGetOperation) {
|
||||
if (!mOperationDone) {
|
||||
if (!mGetFinalFlag) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
while ((more) && (mReplyHeader.responseCode ==
|
||||
ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
|
||||
}
|
||||
// For GET we need to loop until all headers have been sent,
|
||||
// And then we wait for the first continue package with the
|
||||
// reply.
|
||||
if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
|
||||
null, mReplyHeader, mPrivateInput, mSrmActive);
|
||||
}
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
} else {
|
||||
checkForSrm();
|
||||
}
|
||||
} else {
|
||||
more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
|
||||
|
||||
if (more) {
|
||||
throw new IOException("FINAL_GET forced, data didn't fit into one packet");
|
||||
}
|
||||
|
||||
mOperationDone = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PUT operation
|
||||
if (!mOperationDone) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
|
||||
}
|
||||
}
|
||||
|
||||
if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
|
||||
null, mReplyHeader, mPrivateInput, mSrmActive);
|
||||
}
|
||||
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the operation since there is no data to read.
|
||||
* @param sendEmpty <code>true</code> if the operation should send an empty
|
||||
* packet or not send anything if there is no data to send
|
||||
* @param inStream <code>true</code> if the stream is input stream or is
|
||||
* output stream
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
|
||||
throws IOException {
|
||||
|
||||
// One path to the first put operation - the other one does not need to
|
||||
// handle SRM, as all will fit into one packet.
|
||||
|
||||
if (mGetOperation) {
|
||||
if ((inStream) && (!mOperationDone)) {
|
||||
// to deal with inputstream in get operation
|
||||
mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
|
||||
null, mReplyHeader, mPrivateInput, mSrmActive);
|
||||
/*
|
||||
* Determine if that was not the last packet in the operation
|
||||
*/
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
} else {
|
||||
checkForSrm();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if ((!inStream) && (!mOperationDone)) {
|
||||
// to deal with outputstream in get operation
|
||||
|
||||
if (mPrivateInput == null) {
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
}
|
||||
|
||||
if (!mGetFinalFlag) {
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_GET);
|
||||
} else {
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
|
||||
}
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (mOperationDone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// PUT operation
|
||||
if ((!inStream) && (!mOperationDone)) {
|
||||
// to deal with outputstream in put operation
|
||||
if (mReplyHeader.responseCode == -1) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
}
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_PUT);
|
||||
return true;
|
||||
} else if ((inStream) && (!mOperationDone)) {
|
||||
// How to deal with inputstream in put operation ?
|
||||
return false;
|
||||
|
||||
} else if (mOperationDone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the output or input stream is closed.
|
||||
* @param inStream <code>true</code> if the input stream is closed;
|
||||
* <code>false</code> if the output stream is closed
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void streamClosed(boolean inStream) throws IOException {
|
||||
if (!mGetOperation) {
|
||||
if ((!inStream) && (!mOperationDone)) {
|
||||
// to deal with outputstream in put operation
|
||||
|
||||
boolean more = true;
|
||||
|
||||
if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
|
||||
byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
|
||||
if (headerArray.length <= 0)
|
||||
more = false;
|
||||
}
|
||||
// If have not sent any data so send all now
|
||||
if (mReplyHeader.responseCode == -1) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
}
|
||||
|
||||
while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the IrOBEX specification, after the final put, you
|
||||
* only have a single reply to send. so we don't need the while
|
||||
* loop.
|
||||
*/
|
||||
while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
|
||||
}
|
||||
mOperationDone = true;
|
||||
} else if ((inStream) && (mOperationDone)) {
|
||||
// how to deal with input stream in put stream ?
|
||||
mOperationDone = true;
|
||||
}
|
||||
} else {
|
||||
if ((inStream) && (!mOperationDone)) {
|
||||
|
||||
// to deal with inputstream in get operation
|
||||
// Have not sent any data so send it all now
|
||||
|
||||
if (mReplyHeader.responseCode == -1) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
}
|
||||
|
||||
while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
|
||||
if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
|
||||
mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
|
||||
mReplyHeader, mPrivateInput, false);
|
||||
// Regardless of the SRM state, wait for the response.
|
||||
}
|
||||
mOperationDone = true;
|
||||
} else if ((!inStream) && (!mOperationDone)) {
|
||||
// to deal with outputstream in get operation
|
||||
// part of the data may have been sent in continueOperation.
|
||||
|
||||
boolean more = true;
|
||||
|
||||
if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
|
||||
byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
|
||||
if (headerArray.length <= 0)
|
||||
more = false;
|
||||
}
|
||||
|
||||
if (mPrivateInput == null) {
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
}
|
||||
if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
|
||||
more = false;
|
||||
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
|
||||
}
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
|
||||
// parent.sendRequest(0x83, null, replyHeaders, privateInput);
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void noBodyHeader(){
|
||||
mSendBodyHeader = false;
|
||||
}
|
||||
}
|
@ -1,616 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2015 Samsung LSI
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class in an implementation of the OBEX ClientSession.
|
||||
* @hide
|
||||
*/
|
||||
public final class ClientSession extends ObexSession {
|
||||
|
||||
private static final String TAG = "ClientSession";
|
||||
|
||||
private boolean mOpen;
|
||||
|
||||
// Determines if an OBEX layer connection has been established
|
||||
private boolean mObexConnected;
|
||||
|
||||
private byte[] mConnectionId = null;
|
||||
|
||||
/*
|
||||
* The max Packet size must be at least 255 according to the OBEX
|
||||
* specification.
|
||||
*/
|
||||
private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
|
||||
|
||||
private boolean mRequestActive;
|
||||
|
||||
private final InputStream mInput;
|
||||
|
||||
private final OutputStream mOutput;
|
||||
|
||||
private final boolean mLocalSrmSupported;
|
||||
|
||||
private final ObexTransport mTransport;
|
||||
|
||||
public ClientSession(final ObexTransport trans) throws IOException {
|
||||
mInput = trans.openInputStream();
|
||||
mOutput = trans.openOutputStream();
|
||||
mOpen = true;
|
||||
mRequestActive = false;
|
||||
mLocalSrmSupported = trans.isSrmSupported();
|
||||
mTransport = trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ClientSession
|
||||
* @param trans The transport to use for OBEX transactions
|
||||
* @param supportsSrm True if Single Response Mode should be used e.g. if the
|
||||
* supplied transport is a TCP or l2cap channel.
|
||||
* @throws IOException if it occurs while opening the transport streams.
|
||||
*/
|
||||
public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
|
||||
mInput = trans.openInputStream();
|
||||
mOutput = trans.openOutputStream();
|
||||
mOpen = true;
|
||||
mRequestActive = false;
|
||||
mLocalSrmSupported = supportsSrm;
|
||||
mTransport = trans;
|
||||
}
|
||||
|
||||
public HeaderSet connect(final HeaderSet header) throws IOException {
|
||||
ensureOpen();
|
||||
if (mObexConnected) {
|
||||
throw new IOException("Already connected to server");
|
||||
}
|
||||
setRequestActive();
|
||||
|
||||
int totalLength = 4;
|
||||
byte[] head = null;
|
||||
|
||||
// Determine the header byte array
|
||||
if (header != null) {
|
||||
if (header.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
head = ObexHelper.createHeader(header, false);
|
||||
totalLength += head.length;
|
||||
}
|
||||
/*
|
||||
* Write the OBEX CONNECT packet to the server.
|
||||
* Byte 0: 0x80
|
||||
* Byte 1&2: Connect Packet Length
|
||||
* Byte 3: OBEX Version Number (Presently, 0x10)
|
||||
* Byte 4: Flags (For TCP 0x00)
|
||||
* Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
|
||||
* Byte 7 to n: headers
|
||||
*/
|
||||
byte[] requestPacket = new byte[totalLength];
|
||||
int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
|
||||
// We just need to start at byte 3 since the sendRequest() method will
|
||||
// handle the length and 0x80.
|
||||
requestPacket[0] = (byte)0x10;
|
||||
requestPacket[1] = (byte)0x00;
|
||||
requestPacket[2] = (byte)(maxRxPacketSize >> 8);
|
||||
requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
|
||||
if (head != null) {
|
||||
System.arraycopy(head, 0, requestPacket, 4, head.length);
|
||||
}
|
||||
|
||||
// Since we are not yet connected, the peer max packet size is unknown,
|
||||
// hence we are only guaranteed the server will use the first 7 bytes.
|
||||
if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
|
||||
throw new IOException("Packet size exceeds max packet size for connect");
|
||||
}
|
||||
|
||||
HeaderSet returnHeaderSet = new HeaderSet();
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
|
||||
|
||||
/*
|
||||
* Read the response from the OBEX server.
|
||||
* Byte 0: Response Code (If successful then OBEX_HTTP_OK)
|
||||
* Byte 1&2: Packet Length
|
||||
* Byte 3: OBEX Version Number
|
||||
* Byte 4: Flags3
|
||||
* Byte 5&6: Max OBEX packet Length
|
||||
* Byte 7 to n: Optional HeaderSet
|
||||
*/
|
||||
if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
|
||||
mObexConnected = true;
|
||||
}
|
||||
setRequestInactive();
|
||||
|
||||
return returnHeaderSet;
|
||||
}
|
||||
|
||||
public Operation get(HeaderSet header) throws IOException {
|
||||
|
||||
if (!mObexConnected) {
|
||||
throw new IOException("Not connected to the server");
|
||||
}
|
||||
setRequestActive();
|
||||
|
||||
ensureOpen();
|
||||
|
||||
HeaderSet head;
|
||||
if (header == null) {
|
||||
head = new HeaderSet();
|
||||
} else {
|
||||
head = header;
|
||||
if (head.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
}
|
||||
// Add the connection ID if one exists
|
||||
if (mConnectionId != null) {
|
||||
head.mConnectionID = new byte[4];
|
||||
System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
|
||||
}
|
||||
|
||||
if(mLocalSrmSupported) {
|
||||
head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
|
||||
/* TODO: Consider creating an interface to get the wait state.
|
||||
* On an android system, I cannot see when this is to be used.
|
||||
* except perhaps if we are to wait for user accept on a push message.
|
||||
if(getLocalWaitState()) {
|
||||
head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return new ClientOperation(mMaxTxPacketSize, this, head, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 0xCB Connection Id an identifier used for OBEX connection multiplexing
|
||||
*/
|
||||
public void setConnectionID(long id) {
|
||||
if ((id < 0) || (id > 0xFFFFFFFFL)) {
|
||||
throw new IllegalArgumentException("Connection ID is not in a valid range");
|
||||
}
|
||||
mConnectionId = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
public HeaderSet delete(HeaderSet header) throws IOException {
|
||||
|
||||
Operation op = put(header);
|
||||
op.getResponseCode();
|
||||
HeaderSet returnValue = op.getReceivedHeader();
|
||||
op.close();
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public HeaderSet disconnect(HeaderSet header) throws IOException {
|
||||
if (!mObexConnected) {
|
||||
throw new IOException("Not connected to the server");
|
||||
}
|
||||
setRequestActive();
|
||||
|
||||
ensureOpen();
|
||||
// Determine the header byte array
|
||||
byte[] head = null;
|
||||
if (header != null) {
|
||||
if (header.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
// Add the connection ID if one exists
|
||||
if (mConnectionId != null) {
|
||||
header.mConnectionID = new byte[4];
|
||||
System.arraycopy(mConnectionId, 0, header.mConnectionID, 0, 4);
|
||||
}
|
||||
head = ObexHelper.createHeader(header, false);
|
||||
|
||||
if ((head.length + 3) > mMaxTxPacketSize) {
|
||||
throw new IOException("Packet size exceeds max packet size");
|
||||
}
|
||||
} else {
|
||||
// Add the connection ID if one exists
|
||||
if (mConnectionId != null) {
|
||||
head = new byte[5];
|
||||
head[0] = (byte)HeaderSet.CONNECTION_ID;
|
||||
System.arraycopy(mConnectionId, 0, head, 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
HeaderSet returnHeaderSet = new HeaderSet();
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
|
||||
|
||||
/*
|
||||
* An OBEX DISCONNECT reply from the server:
|
||||
* Byte 1: Response code
|
||||
* Bytes 2 & 3: packet size
|
||||
* Bytes 4 & up: headers
|
||||
*/
|
||||
|
||||
/* response code , and header are ignored
|
||||
* */
|
||||
|
||||
synchronized (this) {
|
||||
mObexConnected = false;
|
||||
setRequestInactive();
|
||||
}
|
||||
|
||||
return returnHeaderSet;
|
||||
}
|
||||
|
||||
public long getConnectionID() {
|
||||
|
||||
if (mConnectionId == null) {
|
||||
return -1;
|
||||
}
|
||||
return ObexHelper.convertToLong(mConnectionId);
|
||||
}
|
||||
|
||||
public Operation put(HeaderSet header) throws IOException {
|
||||
if (!mObexConnected) {
|
||||
throw new IOException("Not connected to the server");
|
||||
}
|
||||
setRequestActive();
|
||||
|
||||
ensureOpen();
|
||||
HeaderSet head;
|
||||
if (header == null) {
|
||||
head = new HeaderSet();
|
||||
} else {
|
||||
head = header;
|
||||
// when auth is initiated by client ,save the digest
|
||||
if (head.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the connection ID if one exists
|
||||
if (mConnectionId != null) {
|
||||
|
||||
head.mConnectionID = new byte[4];
|
||||
System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
|
||||
}
|
||||
|
||||
if(mLocalSrmSupported) {
|
||||
head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
|
||||
/* TODO: Consider creating an interface to get the wait state.
|
||||
* On an android system, I cannot see when this is to be used.
|
||||
if(getLocalWaitState()) {
|
||||
head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
|
||||
}
|
||||
*/
|
||||
}
|
||||
return new ClientOperation(mMaxTxPacketSize, this, head, false);
|
||||
}
|
||||
|
||||
public void setAuthenticator(Authenticator auth) throws IOException {
|
||||
if (auth == null) {
|
||||
throw new IOException("Authenticator may not be null");
|
||||
}
|
||||
mAuthenticator = auth;
|
||||
}
|
||||
|
||||
public HeaderSet setPath(HeaderSet header, boolean backup, boolean create) throws IOException {
|
||||
if (!mObexConnected) {
|
||||
throw new IOException("Not connected to the server");
|
||||
}
|
||||
setRequestActive();
|
||||
ensureOpen();
|
||||
|
||||
int totalLength = 2;
|
||||
byte[] head = null;
|
||||
HeaderSet headset;
|
||||
if (header == null) {
|
||||
headset = new HeaderSet();
|
||||
} else {
|
||||
headset = header;
|
||||
if (headset.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// when auth is initiated by client ,save the digest
|
||||
if (headset.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
|
||||
}
|
||||
|
||||
// Add the connection ID if one exists
|
||||
if (mConnectionId != null) {
|
||||
headset.mConnectionID = new byte[4];
|
||||
System.arraycopy(mConnectionId, 0, headset.mConnectionID, 0, 4);
|
||||
}
|
||||
|
||||
head = ObexHelper.createHeader(headset, false);
|
||||
totalLength += head.length;
|
||||
|
||||
if (totalLength > mMaxTxPacketSize) {
|
||||
throw new IOException("Packet size exceeds max packet size");
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
/*
|
||||
* The backup flag bit is bit 0 so if we add 1, this will set that bit
|
||||
*/
|
||||
if (backup) {
|
||||
flags++;
|
||||
}
|
||||
/*
|
||||
* The create bit is bit 1 so if we or with 2 the bit will be set.
|
||||
*/
|
||||
if (!create) {
|
||||
flags |= 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* An OBEX SETPATH packet to the server:
|
||||
* Byte 1: 0x85
|
||||
* Byte 2 & 3: packet size
|
||||
* Byte 4: flags
|
||||
* Byte 5: constants
|
||||
* Byte 6 & up: headers
|
||||
*/
|
||||
byte[] packet = new byte[totalLength];
|
||||
packet[0] = (byte)flags;
|
||||
packet[1] = (byte)0x00;
|
||||
if (headset != null) {
|
||||
System.arraycopy(head, 0, packet, 2, head.length);
|
||||
}
|
||||
|
||||
HeaderSet returnHeaderSet = new HeaderSet();
|
||||
sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
|
||||
|
||||
/*
|
||||
* An OBEX SETPATH reply from the server:
|
||||
* Byte 1: Response code
|
||||
* Bytes 2 & 3: packet size
|
||||
* Bytes 4 & up: headers
|
||||
*/
|
||||
|
||||
setRequestInactive();
|
||||
|
||||
return returnHeaderSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the connection is open.
|
||||
* @throws IOException if the connection is closed
|
||||
*/
|
||||
public synchronized void ensureOpen() throws IOException {
|
||||
if (!mOpen) {
|
||||
throw new IOException("Connection closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request inactive. Allows Put and get operation objects to tell this
|
||||
* object when they are done.
|
||||
*/
|
||||
/*package*/synchronized void setRequestInactive() {
|
||||
mRequestActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request to active.
|
||||
* @throws IOException if already active
|
||||
*/
|
||||
private synchronized void setRequestActive() throws IOException {
|
||||
if (mRequestActive) {
|
||||
throw new IOException("OBEX request is already being performed");
|
||||
}
|
||||
mRequestActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a standard request to the client. It will then wait for the reply
|
||||
* and update the header set object provided. If any authentication headers
|
||||
* (i.e. authentication challenge or authentication response) are received,
|
||||
* they will be processed.
|
||||
* @param opCode the type of request to send to the client
|
||||
* @param head the headers to send to the client
|
||||
* @param header the header object to update with the response
|
||||
* @param privateInput the input stream used by the Operation object; null
|
||||
* if this is called on a CONNECT, SETPATH or DISCONNECT
|
||||
* @return
|
||||
* <code>true</code> if the operation completed successfully;
|
||||
* <code>false</code> if an authentication response failed to pass
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
|
||||
PrivateInputStream privateInput, boolean srmActive) throws IOException {
|
||||
//check header length with local max size
|
||||
if (head != null) {
|
||||
if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
|
||||
// TODO: This is an implementation limit - not a specification requirement.
|
||||
throw new IOException("header too large ");
|
||||
}
|
||||
}
|
||||
|
||||
boolean skipSend = false;
|
||||
boolean skipReceive = false;
|
||||
if (srmActive == true) {
|
||||
if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
|
||||
// we are in the middle of a SRM PUT operation, don't expect a continue.
|
||||
skipReceive = true;
|
||||
} else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
|
||||
// We are still sending the get request, send, but don't expect continue
|
||||
// until the request is transfered (the final bit is set)
|
||||
skipReceive = true;
|
||||
} else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
|
||||
// All done sending the request, expect data from the server, without
|
||||
// sending continue.
|
||||
skipSend = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int bytesReceived;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write((byte)opCode);
|
||||
|
||||
// Determine if there are any headers to send
|
||||
if (head == null) {
|
||||
out.write(0x00);
|
||||
out.write(0x03);
|
||||
} else {
|
||||
out.write((byte)((head.length + 3) >> 8));
|
||||
out.write((byte)(head.length + 3));
|
||||
out.write(head);
|
||||
}
|
||||
|
||||
if (!skipSend) {
|
||||
// Write the request to the output stream and flush the stream
|
||||
mOutput.write(out.toByteArray());
|
||||
// TODO: is this really needed? if this flush is implemented
|
||||
// correctly, we will get a gap between each obex packet.
|
||||
// which is kind of the idea behind SRM to avoid.
|
||||
// Consider offloading to another thread (async action)
|
||||
mOutput.flush();
|
||||
}
|
||||
|
||||
if (!skipReceive) {
|
||||
header.responseCode = mInput.read();
|
||||
|
||||
int length = ((mInput.read() << 8) | (mInput.read()));
|
||||
|
||||
if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
throw new IOException("Packet received exceeds packet size limit");
|
||||
}
|
||||
if (length > ObexHelper.BASE_PACKET_LENGTH) {
|
||||
byte[] data = null;
|
||||
if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
|
||||
@SuppressWarnings("unused")
|
||||
int version = mInput.read();
|
||||
@SuppressWarnings("unused")
|
||||
int flags = mInput.read();
|
||||
mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
|
||||
|
||||
//check with local max size
|
||||
if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
|
||||
mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
|
||||
}
|
||||
|
||||
// check with transport maximum size
|
||||
if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
|
||||
// To increase this size, increase the buffer size in L2CAP layer
|
||||
// in Bluedroid.
|
||||
Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
|
||||
+ " requested. Transport only allows: "
|
||||
+ ObexHelper.getMaxTxPacketSize(mTransport)
|
||||
+ " Lowering limit to this value.");
|
||||
mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
|
||||
}
|
||||
|
||||
if (length > 7) {
|
||||
data = new byte[length - 7];
|
||||
|
||||
bytesReceived = mInput.read(data);
|
||||
while (bytesReceived != (length - 7)) {
|
||||
bytesReceived += mInput.read(data, bytesReceived, data.length
|
||||
- bytesReceived);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
data = new byte[length - 3];
|
||||
bytesReceived = mInput.read(data);
|
||||
|
||||
while (bytesReceived != (length - 3)) {
|
||||
bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
|
||||
}
|
||||
if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] body = ObexHelper.updateHeaderSet(header, data);
|
||||
if ((privateInput != null) && (body != null)) {
|
||||
privateInput.writeBytes(body, 1);
|
||||
}
|
||||
|
||||
if (header.mConnectionID != null) {
|
||||
mConnectionId = new byte[4];
|
||||
System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
|
||||
}
|
||||
|
||||
if (header.mAuthResp != null) {
|
||||
if (!handleAuthResp(header.mAuthResp)) {
|
||||
setRequestInactive();
|
||||
throw new IOException("Authentication Failed");
|
||||
}
|
||||
}
|
||||
|
||||
if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
|
||||
&& (header.mAuthChall != null)) {
|
||||
|
||||
if (handleAuthChall(header)) {
|
||||
out.write((byte)HeaderSet.AUTH_RESPONSE);
|
||||
out.write((byte)((header.mAuthResp.length + 3) >> 8));
|
||||
out.write((byte)(header.mAuthResp.length + 3));
|
||||
out.write(header.mAuthResp);
|
||||
header.mAuthChall = null;
|
||||
header.mAuthResp = null;
|
||||
|
||||
byte[] sendHeaders = new byte[out.size() - 3];
|
||||
System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
|
||||
|
||||
return sendRequest(opCode, sendHeaders, header, privateInput, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
mOpen = false;
|
||||
mInput.close();
|
||||
mOutput.close();
|
||||
}
|
||||
|
||||
public boolean isSrmSupported() {
|
||||
return mLocalSrmSupported;
|
||||
}
|
||||
}
|
@ -1,710 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The Android Open Source Project
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* This class implements the javax.obex.HeaderSet interface for OBEX over
|
||||
* RFCOMM or OBEX over l2cap.
|
||||
* @hide
|
||||
*/
|
||||
public final class HeaderSet {
|
||||
|
||||
/**
|
||||
* Represents the OBEX Count header. This allows the connection statement to
|
||||
* tell the server how many objects it plans to send or retrieve.
|
||||
* <P>
|
||||
* The value of <code>COUNT</code> is 0xC0 (192).
|
||||
*/
|
||||
public static final int COUNT = 0xC0;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Name header. This specifies the name of the object.
|
||||
* <P>
|
||||
* The value of <code>NAME</code> is 0x01 (1).
|
||||
*/
|
||||
public static final int NAME = 0x01;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Type header. This allows a request to specify the
|
||||
* type of the object (e.g. text, html, binary, etc.).
|
||||
* <P>
|
||||
* The value of <code>TYPE</code> is 0x42 (66).
|
||||
*/
|
||||
public static final int TYPE = 0x42;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Length header. This is the length of the object in
|
||||
* bytes.
|
||||
* <P>
|
||||
* The value of <code>LENGTH</code> is 0xC3 (195).
|
||||
*/
|
||||
public static final int LENGTH = 0xC3;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Time header using the ISO 8601 standards. This is the
|
||||
* preferred time header.
|
||||
* <P>
|
||||
* The value of <code>TIME_ISO_8601</code> is 0x44 (68).
|
||||
*/
|
||||
public static final int TIME_ISO_8601 = 0x44;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Time header using the 4 byte representation. This is
|
||||
* only included for backwards compatibility. It represents the number of
|
||||
* seconds since January 1, 1970.
|
||||
* <P>
|
||||
* The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
|
||||
*/
|
||||
public static final int TIME_4_BYTE = 0xC4;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Description header. This is a text description of the
|
||||
* object.
|
||||
* <P>
|
||||
* The value of <code>DESCRIPTION</code> is 0x05 (5).
|
||||
*/
|
||||
public static final int DESCRIPTION = 0x05;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Target header. This is the name of the service an
|
||||
* operation is targeted to.
|
||||
* <P>
|
||||
* The value of <code>TARGET</code> is 0x46 (70).
|
||||
*/
|
||||
public static final int TARGET = 0x46;
|
||||
|
||||
/**
|
||||
* Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
|
||||
* included in a request or reply.
|
||||
* <P>
|
||||
* The value of <code>HTTP</code> is 0x47 (71).
|
||||
*/
|
||||
public static final int HTTP = 0x47;
|
||||
|
||||
/**
|
||||
* Represents the OBEX BODY header.
|
||||
* <P>
|
||||
* The value of <code>BODY</code> is 0x48 (72).
|
||||
*/
|
||||
public static final int BODY = 0x48;
|
||||
|
||||
/**
|
||||
* Represents the OBEX End of BODY header.
|
||||
* <P>
|
||||
* The value of <code>BODY</code> is 0x49 (73).
|
||||
*/
|
||||
public static final int END_OF_BODY = 0x49;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Who header. Identifies the OBEX application to
|
||||
* determine if the two peers are talking to each other.
|
||||
* <P>
|
||||
* The value of <code>WHO</code> is 0x4A (74).
|
||||
*/
|
||||
public static final int WHO = 0x4A;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Connection ID header. Identifies used for OBEX
|
||||
* connection multiplexing.
|
||||
* <P>
|
||||
* The value of <code>CONNECTION_ID</code> is 0xCB (203).
|
||||
*/
|
||||
|
||||
public static final int CONNECTION_ID = 0xCB;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Application Parameter header. This header specifies
|
||||
* additional application request and response information.
|
||||
* <P>
|
||||
* The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
|
||||
*/
|
||||
public static final int APPLICATION_PARAMETER = 0x4C;
|
||||
|
||||
/**
|
||||
* Represents the OBEX authentication digest-challenge.
|
||||
* <P>
|
||||
* The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
|
||||
*/
|
||||
public static final int AUTH_CHALLENGE = 0x4D;
|
||||
|
||||
/**
|
||||
* Represents the OBEX authentication digest-response.
|
||||
* <P>
|
||||
* The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
|
||||
*/
|
||||
public static final int AUTH_RESPONSE = 0x4E;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Object Class header. This header specifies the OBEX
|
||||
* object class of the object.
|
||||
* <P>
|
||||
* The value of <code>OBJECT_CLASS</code> is 0x4F (79).
|
||||
*/
|
||||
public static final int OBJECT_CLASS = 0x4F;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Single Response Mode (SRM). This header is used
|
||||
* for Single response mode, introduced in OBEX 1.5.
|
||||
* <P>
|
||||
* The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151).
|
||||
*/
|
||||
public static final int SINGLE_RESPONSE_MODE = 0x97;
|
||||
|
||||
/**
|
||||
* Represents the OBEX Single Response Mode Parameters. This header is used
|
||||
* for Single response mode, introduced in OBEX 1.5.
|
||||
* <P>
|
||||
* The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152).
|
||||
*/
|
||||
public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98;
|
||||
|
||||
private Long mCount; // 4 byte unsigned integer
|
||||
|
||||
private String mName; // null terminated Unicode text string
|
||||
|
||||
private boolean mEmptyName;
|
||||
|
||||
private String mType; // null terminated ASCII text string
|
||||
|
||||
private Long mLength; // 4 byte unsigend integer
|
||||
|
||||
private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
|
||||
|
||||
private Calendar mByteTime; // 4 byte unsigned integer
|
||||
|
||||
private String mDescription; // null terminated Unicode text String
|
||||
|
||||
private byte[] mTarget; // byte sequence
|
||||
|
||||
private byte[] mHttpHeader; // byte sequence
|
||||
|
||||
private byte[] mWho; // length prefixed byte sequence
|
||||
|
||||
private byte[] mAppParam; // byte sequence of the form tag length value
|
||||
|
||||
private byte[] mObjectClass; // byte sequence
|
||||
|
||||
private String[] mUnicodeUserDefined; // null terminated unicode string
|
||||
|
||||
private byte[][] mSequenceUserDefined; // byte sequence user defined
|
||||
|
||||
private Byte[] mByteUserDefined; // 1 byte
|
||||
|
||||
private Long[] mIntegerUserDefined; // 4 byte unsigned integer
|
||||
|
||||
private SecureRandom mRandom = null;
|
||||
|
||||
private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM
|
||||
|
||||
private Byte mSrmParam; // byte representing the SRM parameters - only "wait"
|
||||
// is supported by Bluetooth
|
||||
|
||||
/*package*/ byte[] nonce;
|
||||
|
||||
public byte[] mAuthChall; // The authentication challenge header
|
||||
|
||||
public byte[] mAuthResp; // The authentication response header
|
||||
|
||||
public byte[] mConnectionID; // THe connection ID
|
||||
|
||||
public int responseCode;
|
||||
|
||||
/**
|
||||
* Creates new <code>HeaderSet</code> object.
|
||||
* @param size the max packet size for this connection
|
||||
*/
|
||||
public HeaderSet() {
|
||||
mUnicodeUserDefined = new String[16];
|
||||
mSequenceUserDefined = new byte[16][];
|
||||
mByteUserDefined = new Byte[16];
|
||||
mIntegerUserDefined = new Long[16];
|
||||
responseCode = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flag for special "value" of NAME header which should be empty. This
|
||||
* is not the same as NAME header with empty string in which case it will
|
||||
* have length of 5 bytes. It should be 3 bytes with only header id and
|
||||
* length field.
|
||||
*/
|
||||
public void setEmptyNameHeader() {
|
||||
mName = null;
|
||||
mEmptyName = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flag for special "value" of NAME header which should be empty. See
|
||||
* above.
|
||||
*/
|
||||
public boolean getEmptyNameHeader() {
|
||||
return mEmptyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the header identifier to the value provided. The type
|
||||
* of object must correspond to the Java type defined in the description of
|
||||
* this interface. If <code>null</code> is passed as the
|
||||
* <code>headerValue</code> then the header will be removed from the set of
|
||||
* headers to include in the next request.
|
||||
* @param headerID the identifier to include in the message
|
||||
* @param headerValue the value of the header identifier
|
||||
* @throws IllegalArgumentException if the header identifier provided is not
|
||||
* one defined in this interface or a user-defined header; if the
|
||||
* type of <code>headerValue</code> is not the correct Java type as
|
||||
* defined in the description of this interface\
|
||||
*/
|
||||
public void setHeader(int headerID, Object headerValue) {
|
||||
long temp = -1;
|
||||
|
||||
switch (headerID) {
|
||||
case COUNT:
|
||||
if (!(headerValue instanceof Long)) {
|
||||
if (headerValue == null) {
|
||||
mCount = null;
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("Count must be a Long");
|
||||
}
|
||||
temp = ((Long)headerValue).longValue();
|
||||
if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
|
||||
throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
|
||||
}
|
||||
mCount = (Long)headerValue;
|
||||
break;
|
||||
case NAME:
|
||||
if ((headerValue != null) && (!(headerValue instanceof String))) {
|
||||
throw new IllegalArgumentException("Name must be a String");
|
||||
}
|
||||
mEmptyName = false;
|
||||
mName = (String)headerValue;
|
||||
break;
|
||||
case TYPE:
|
||||
if ((headerValue != null) && (!(headerValue instanceof String))) {
|
||||
throw new IllegalArgumentException("Type must be a String");
|
||||
}
|
||||
mType = (String)headerValue;
|
||||
break;
|
||||
case LENGTH:
|
||||
if (!(headerValue instanceof Long)) {
|
||||
if (headerValue == null) {
|
||||
mLength = null;
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("Length must be a Long");
|
||||
}
|
||||
temp = ((Long)headerValue).longValue();
|
||||
if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
|
||||
throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
|
||||
}
|
||||
mLength = (Long)headerValue;
|
||||
break;
|
||||
case TIME_ISO_8601:
|
||||
if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
|
||||
throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
|
||||
}
|
||||
mIsoTime = (Calendar)headerValue;
|
||||
break;
|
||||
case TIME_4_BYTE:
|
||||
if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
|
||||
throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
|
||||
}
|
||||
mByteTime = (Calendar)headerValue;
|
||||
break;
|
||||
case DESCRIPTION:
|
||||
if ((headerValue != null) && (!(headerValue instanceof String))) {
|
||||
throw new IllegalArgumentException("Description must be a String");
|
||||
}
|
||||
mDescription = (String)headerValue;
|
||||
break;
|
||||
case TARGET:
|
||||
if (headerValue == null) {
|
||||
mTarget = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException("Target must be a byte array");
|
||||
} else {
|
||||
mTarget = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HTTP:
|
||||
if (headerValue == null) {
|
||||
mHttpHeader = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException("HTTP must be a byte array");
|
||||
} else {
|
||||
mHttpHeader = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WHO:
|
||||
if (headerValue == null) {
|
||||
mWho = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException("WHO must be a byte array");
|
||||
} else {
|
||||
mWho = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OBJECT_CLASS:
|
||||
if (headerValue == null) {
|
||||
mObjectClass = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException("Object Class must be a byte array");
|
||||
} else {
|
||||
mObjectClass = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case APPLICATION_PARAMETER:
|
||||
if (headerValue == null) {
|
||||
mAppParam = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException(
|
||||
"Application Parameter must be a byte array");
|
||||
} else {
|
||||
mAppParam = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SINGLE_RESPONSE_MODE:
|
||||
if (headerValue == null) {
|
||||
mSingleResponseMode = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof Byte)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Single Response Mode must be a Byte");
|
||||
} else {
|
||||
mSingleResponseMode = (Byte)headerValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SINGLE_RESPONSE_MODE_PARAMETER:
|
||||
if (headerValue == null) {
|
||||
mSrmParam = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof Byte)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Single Response Mode Parameter must be a Byte");
|
||||
} else {
|
||||
mSrmParam = (Byte)headerValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Verify that it was not a Unicode String user Defined
|
||||
if ((headerID >= 0x30) && (headerID <= 0x3F)) {
|
||||
if ((headerValue != null) && (!(headerValue instanceof String))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unicode String User Defined must be a String");
|
||||
}
|
||||
mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
|
||||
|
||||
break;
|
||||
}
|
||||
// Verify that it was not a byte sequence user defined value
|
||||
if ((headerID >= 0x70) && (headerID <= 0x7F)) {
|
||||
|
||||
if (headerValue == null) {
|
||||
mSequenceUserDefined[headerID - 0x70] = null;
|
||||
} else {
|
||||
if (!(headerValue instanceof byte[])) {
|
||||
throw new IllegalArgumentException(
|
||||
"Byte Sequence User Defined must be a byte array");
|
||||
} else {
|
||||
mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
|
||||
System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
|
||||
0, mSequenceUserDefined[headerID - 0x70].length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Verify that it was not a Byte user Defined
|
||||
if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
|
||||
if ((headerValue != null) && (!(headerValue instanceof Byte))) {
|
||||
throw new IllegalArgumentException("ByteUser Defined must be a Byte");
|
||||
}
|
||||
mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
|
||||
|
||||
break;
|
||||
}
|
||||
// Verify that is was not the 4 byte unsigned integer user
|
||||
// defined header
|
||||
if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
|
||||
if (!(headerValue instanceof Long)) {
|
||||
if (headerValue == null) {
|
||||
mIntegerUserDefined[headerID - 0xF0] = null;
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("Integer User Defined must be a Long");
|
||||
}
|
||||
temp = ((Long)headerValue).longValue();
|
||||
if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Integer User Defined must be between 0 and 0xFFFFFFFF");
|
||||
}
|
||||
mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid Header Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the header identifier provided. The type of the
|
||||
* Object returned is defined in the description of this interface.
|
||||
* @param headerID the header identifier whose value is to be returned
|
||||
* @return the value of the header provided or <code>null</code> if the
|
||||
* header identifier specified is not part of this
|
||||
* <code>HeaderSet</code> object
|
||||
* @throws IllegalArgumentException if the <code>headerID</code> is not one
|
||||
* defined in this interface or any of the user-defined headers
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the operation or if the connection has been closed
|
||||
*/
|
||||
public Object getHeader(int headerID) throws IOException {
|
||||
|
||||
switch (headerID) {
|
||||
case COUNT:
|
||||
return mCount;
|
||||
case NAME:
|
||||
return mName;
|
||||
case TYPE:
|
||||
return mType;
|
||||
case LENGTH:
|
||||
return mLength;
|
||||
case TIME_ISO_8601:
|
||||
return mIsoTime;
|
||||
case TIME_4_BYTE:
|
||||
return mByteTime;
|
||||
case DESCRIPTION:
|
||||
return mDescription;
|
||||
case TARGET:
|
||||
return mTarget;
|
||||
case HTTP:
|
||||
return mHttpHeader;
|
||||
case WHO:
|
||||
return mWho;
|
||||
case CONNECTION_ID:
|
||||
return mConnectionID;
|
||||
case OBJECT_CLASS:
|
||||
return mObjectClass;
|
||||
case APPLICATION_PARAMETER:
|
||||
return mAppParam;
|
||||
case SINGLE_RESPONSE_MODE:
|
||||
return mSingleResponseMode;
|
||||
case SINGLE_RESPONSE_MODE_PARAMETER:
|
||||
return mSrmParam;
|
||||
default:
|
||||
// Verify that it was not a Unicode String user Defined
|
||||
if ((headerID >= 0x30) && (headerID <= 0x3F)) {
|
||||
return mUnicodeUserDefined[headerID - 0x30];
|
||||
}
|
||||
// Verify that it was not a byte sequence user defined header
|
||||
if ((headerID >= 0x70) && (headerID <= 0x7F)) {
|
||||
return mSequenceUserDefined[headerID - 0x70];
|
||||
}
|
||||
// Verify that it was not a byte user defined header
|
||||
if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
|
||||
return mByteUserDefined[headerID - 0xB0];
|
||||
}
|
||||
// Verify that it was not a integer user defined header
|
||||
if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
|
||||
return mIntegerUserDefined[headerID - 0xF0];
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid Header Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of headers that may be retrieved via the
|
||||
* <code>getHeader</code> method that will not return <code>null</code>. In
|
||||
* other words, this method returns all the headers that are available in
|
||||
* this object.
|
||||
* @see #getHeader
|
||||
* @return the array of headers that are set in this object or
|
||||
* <code>null</code> if no headers are available
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the operation or the connection has been closed
|
||||
*/
|
||||
public int[] getHeaderList() throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
if (mCount != null) {
|
||||
out.write(COUNT);
|
||||
}
|
||||
if (mName != null) {
|
||||
out.write(NAME);
|
||||
}
|
||||
if (mType != null) {
|
||||
out.write(TYPE);
|
||||
}
|
||||
if (mLength != null) {
|
||||
out.write(LENGTH);
|
||||
}
|
||||
if (mIsoTime != null) {
|
||||
out.write(TIME_ISO_8601);
|
||||
}
|
||||
if (mByteTime != null) {
|
||||
out.write(TIME_4_BYTE);
|
||||
}
|
||||
if (mDescription != null) {
|
||||
out.write(DESCRIPTION);
|
||||
}
|
||||
if (mTarget != null) {
|
||||
out.write(TARGET);
|
||||
}
|
||||
if (mHttpHeader != null) {
|
||||
out.write(HTTP);
|
||||
}
|
||||
if (mWho != null) {
|
||||
out.write(WHO);
|
||||
}
|
||||
if (mAppParam != null) {
|
||||
out.write(APPLICATION_PARAMETER);
|
||||
}
|
||||
if (mObjectClass != null) {
|
||||
out.write(OBJECT_CLASS);
|
||||
}
|
||||
if(mSingleResponseMode != null) {
|
||||
out.write(SINGLE_RESPONSE_MODE);
|
||||
}
|
||||
if(mSrmParam != null) {
|
||||
out.write(SINGLE_RESPONSE_MODE_PARAMETER);
|
||||
}
|
||||
|
||||
for (int i = 0x30; i < 0x40; i++) {
|
||||
if (mUnicodeUserDefined[i - 0x30] != null) {
|
||||
out.write(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0x70; i < 0x80; i++) {
|
||||
if (mSequenceUserDefined[i - 0x70] != null) {
|
||||
out.write(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0xB0; i < 0xC0; i++) {
|
||||
if (mByteUserDefined[i - 0xB0] != null) {
|
||||
out.write(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0xF0; i < 0x100; i++) {
|
||||
if (mIntegerUserDefined[i - 0xF0] != null) {
|
||||
out.write(i);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] headers = out.toByteArray();
|
||||
out.close();
|
||||
|
||||
if ((headers == null) || (headers.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] result = new int[headers.length];
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
// Convert the byte to a positive integer. That is, an integer
|
||||
// between 0 and 256.
|
||||
result[i] = headers[i] & 0xFF;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication challenge header. The <code>realm</code> will be
|
||||
* encoded based upon the default encoding scheme used by the implementation
|
||||
* to encode strings. Therefore, the encoding scheme used to encode the
|
||||
* <code>realm</code> is application dependent.
|
||||
* @param realm a short description that describes what password to use; if
|
||||
* <code>null</code> no realm will be sent in the authentication
|
||||
* challenge header
|
||||
* @param userID if <code>true</code>, a user ID is required in the reply;
|
||||
* if <code>false</code>, no user ID is required
|
||||
* @param access if <code>true</code> then full access will be granted if
|
||||
* successful; if <code>false</code> then read-only access will be
|
||||
* granted if successful
|
||||
* @throws IOException
|
||||
*/
|
||||
public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
|
||||
throws IOException {
|
||||
|
||||
nonce = new byte[16];
|
||||
if(mRandom == null) {
|
||||
mRandom = new SecureRandom();
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
nonce[i] = (byte)mRandom.nextInt();
|
||||
}
|
||||
|
||||
mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response code received from the server. Response codes are
|
||||
* defined in the <code>ResponseCodes</code> class.
|
||||
* @see ResponseCodes
|
||||
* @return the response code retrieved from the server
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the transaction; if this method is called on a
|
||||
* <code>HeaderSet</code> object created by calling
|
||||
* <code>createHeaderSet()</code> in a <code>ClientSession</code>
|
||||
* object; if this object was created by an OBEX server
|
||||
*/
|
||||
public int getResponseCode() throws IOException {
|
||||
if (responseCode == -1) {
|
||||
throw new IOException("May not be called on a server");
|
||||
} else {
|
||||
return responseCode;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* The <code>ObexSession</code> interface characterizes the term
|
||||
* "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
|
||||
* could be the server-side view of an OBEX connection, or the client-side view
|
||||
* of the same connection, which is established by server's accepting of a
|
||||
* client issued "CONNECT".
|
||||
* <P>
|
||||
* This interface serves as the common super class for
|
||||
* <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
|
||||
* @hide
|
||||
*/
|
||||
public class ObexSession {
|
||||
|
||||
private static final String TAG = "ObexSession";
|
||||
private static final boolean V = ObexHelper.VDBG;
|
||||
|
||||
protected Authenticator mAuthenticator;
|
||||
|
||||
protected byte[] mChallengeDigest;
|
||||
|
||||
/**
|
||||
* Called when the server received an authentication challenge header. This
|
||||
* will cause the authenticator to handle the authentication challenge.
|
||||
* @param header the header with the authentication challenge
|
||||
* @return <code>true</code> if the last request should be resent;
|
||||
* <code>false</code> if the last request should not be resent
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean handleAuthChall(HeaderSet header) throws IOException {
|
||||
if (mAuthenticator == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* An authentication challenge is made up of one required and two
|
||||
* optional tag length value triplets. The tag 0x00 is required to be in
|
||||
* the authentication challenge and it represents the challenge digest
|
||||
* that was received. The tag 0x01 is the options tag. This tag tracks
|
||||
* if user ID is required and if full access will be granted. The tag
|
||||
* 0x02 is the realm, which provides a description of which user name
|
||||
* and password to use.
|
||||
*/
|
||||
byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall);
|
||||
byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall);
|
||||
byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall);
|
||||
|
||||
String realm = null;
|
||||
if (description != null) {
|
||||
byte[] realmString = new byte[description.length - 1];
|
||||
System.arraycopy(description, 1, realmString, 0, realmString.length);
|
||||
|
||||
switch (description[0] & 0xFF) {
|
||||
|
||||
case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII:
|
||||
// ASCII encoding
|
||||
// Fall through
|
||||
case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1:
|
||||
// ISO-8859-1 encoding
|
||||
try {
|
||||
realm = new String(realmString, "ISO8859_1");
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Unsupported Encoding Scheme");
|
||||
}
|
||||
break;
|
||||
|
||||
case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE:
|
||||
// UNICODE Encoding
|
||||
realm = ObexHelper.convertToUnicode(realmString, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IOException("Unsupported Encoding Scheme");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isUserIDRequired = false;
|
||||
boolean isFullAccess = true;
|
||||
if (option != null) {
|
||||
if ((option[0] & 0x01) != 0) {
|
||||
isUserIDRequired = true;
|
||||
}
|
||||
|
||||
if ((option[0] & 0x02) != 0) {
|
||||
isFullAccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
PasswordAuthentication result = null;
|
||||
header.mAuthChall = null;
|
||||
|
||||
try {
|
||||
result = mAuthenticator
|
||||
.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
|
||||
} catch (Exception e) {
|
||||
if (V) Log.d(TAG, "Exception occured - returning false", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no password is provided then we not resent the request
|
||||
*/
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] password = result.getPassword();
|
||||
if (password == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] userName = result.getUserName();
|
||||
|
||||
/*
|
||||
* Create the authentication response header. It includes 1 required and
|
||||
* 2 option tag length value triples. The required triple has a tag of
|
||||
* 0x00 and is the response digest. The first optional tag is 0x01 and
|
||||
* represents the user ID. If no user ID is provided, then no user ID
|
||||
* will be sent. The second optional tag is 0x02 and is the challenge
|
||||
* that was received. This will always be sent
|
||||
*/
|
||||
if (userName != null) {
|
||||
header.mAuthResp = new byte[38 + userName.length];
|
||||
header.mAuthResp[36] = (byte)0x01;
|
||||
header.mAuthResp[37] = (byte)userName.length;
|
||||
System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length);
|
||||
} else {
|
||||
header.mAuthResp = new byte[36];
|
||||
}
|
||||
|
||||
// Create the secret String
|
||||
byte[] digest = new byte[challenge.length + password.length + 1];
|
||||
System.arraycopy(challenge, 0, digest, 0, challenge.length);
|
||||
// Insert colon between challenge and password
|
||||
digest[challenge.length] = (byte)0x3A;
|
||||
System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
|
||||
|
||||
// Add the Response Digest
|
||||
header.mAuthResp[0] = (byte)0x00;
|
||||
header.mAuthResp[1] = (byte)0x10;
|
||||
|
||||
System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16);
|
||||
|
||||
// Add the challenge
|
||||
header.mAuthResp[18] = (byte)0x02;
|
||||
header.mAuthResp[19] = (byte)0x10;
|
||||
System.arraycopy(challenge, 0, header.mAuthResp, 20, 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server received an authentication response header. This
|
||||
* will cause the authenticator to handle the authentication response.
|
||||
* @param authResp the authentication response
|
||||
* @return <code>true</code> if the response passed; <code>false</code> if
|
||||
* the response failed
|
||||
*/
|
||||
public boolean handleAuthResp(byte[] authResp) {
|
||||
if (mAuthenticator == null) {
|
||||
return false;
|
||||
}
|
||||
// get the correct password from the application
|
||||
byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
|
||||
(byte)0x01, authResp));
|
||||
if (correctPassword == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] temp = new byte[correctPassword.length + 16];
|
||||
|
||||
System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
|
||||
System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
|
||||
|
||||
byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
|
||||
byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
|
||||
|
||||
// compare the MD5 hash array .
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (correctResponse[i] != actualResponse[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The <code>ObexTransport</code> interface defines the underlying transport
|
||||
* connection which carries the OBEX protocol( such as TCP, RFCOMM device file
|
||||
* exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
|
||||
* platform, Irda). This interface provides an abstract layer to be used by the
|
||||
* <code>ObexConnection</code>. Each kind of medium shall have its own
|
||||
* implementation to wrap and follow the same interface.
|
||||
* <P>
|
||||
* See section 1.2.2 of IrDA Object Exchange Protocol specification.
|
||||
* <P>
|
||||
* Different kind of medium may have different construction - for example, the
|
||||
* RFCOMM device file medium may be constructed from a file descriptor or simply
|
||||
* a string while the TCP medium usually from a socket.
|
||||
* @hide
|
||||
*/
|
||||
public interface ObexTransport {
|
||||
|
||||
void create() throws IOException;
|
||||
|
||||
void listen() throws IOException;
|
||||
|
||||
void close() throws IOException;
|
||||
|
||||
void connect() throws IOException;
|
||||
|
||||
void disconnect() throws IOException;
|
||||
|
||||
InputStream openInputStream() throws IOException;
|
||||
|
||||
OutputStream openOutputStream() throws IOException;
|
||||
|
||||
DataInputStream openDataInputStream() throws IOException;
|
||||
|
||||
DataOutputStream openDataOutputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Must return the maximum allowed OBEX packet that can be sent over
|
||||
* the transport. For L2CAP this will be the Max SDU reported by the
|
||||
* peer device.
|
||||
* The returned value will be used to set the outgoing OBEX packet
|
||||
* size. Therefore this value shall not change.
|
||||
* For RFCOMM or other transport types where the OBEX packets size
|
||||
* is unrelated to the transport packet size, return -1;
|
||||
* Exception can be made (like PBAP transport) with a smaller value
|
||||
* to avoid bad effect on other profiles using the RFCOMM;
|
||||
* @return the maximum allowed OBEX packet that can be send over
|
||||
* the transport. Or -1 in case of don't care.
|
||||
*/
|
||||
int getMaxTransmitPacketSize();
|
||||
|
||||
/**
|
||||
* Must return the maximum allowed OBEX packet that can be received over
|
||||
* the transport. For L2CAP this will be the Max SDU configured for the
|
||||
* L2CAP channel.
|
||||
* The returned value will be used to validate the incoming packet size
|
||||
* values.
|
||||
* For RFCOMM or other transport types where the OBEX packets size
|
||||
* is unrelated to the transport packet size, return -1;
|
||||
* @return the maximum allowed OBEX packet that can be send over
|
||||
* the transport. Or -1 in case of don't care.
|
||||
*/
|
||||
int getMaxReceivePacketSize();
|
||||
|
||||
/**
|
||||
* Shall return true if the transport in use supports SRM.
|
||||
* @return
|
||||
* <code>true</code> if SRM operation is supported, and is to be enabled.
|
||||
* <code>false</code> if SRM operations are not supported, or should not be used.
|
||||
*/
|
||||
boolean isSrmSupported();
|
||||
|
||||
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The <code>Operation</code> interface provides ways to manipulate a single
|
||||
* OBEX PUT or GET operation. The implementation of this interface sends OBEX
|
||||
* packets as they are built. If during the operation the peer in the operation
|
||||
* ends the operation, an <code>IOException</code> is thrown on the next read
|
||||
* from the input stream, write to the output stream, or call to
|
||||
* <code>sendHeaders()</code>.
|
||||
* <P>
|
||||
* <STRONG>Definition of methods inherited from <code>ContentConnection</code>
|
||||
* </STRONG>
|
||||
* <P>
|
||||
* <code>getEncoding()</code> will always return <code>null</code>. <BR>
|
||||
* <code>getLength()</code> will return the length specified by the OBEX Length
|
||||
* header or -1 if the OBEX Length header was not included. <BR>
|
||||
* <code>getType()</code> will return the value specified in the OBEX Type
|
||||
* header or <code>null</code> if the OBEX Type header was not included.<BR>
|
||||
* <P>
|
||||
* <STRONG>How Headers are Handled</STRONG>
|
||||
* <P>
|
||||
* As headers are received, they may be retrieved through the
|
||||
* <code>getReceivedHeaders()</code> method. If new headers are set during the
|
||||
* operation, the new headers will be sent during the next packet exchange.
|
||||
* <P>
|
||||
* <STRONG>PUT example</STRONG>
|
||||
* <P>
|
||||
* <PRE>
|
||||
* void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj) throws IOException {
|
||||
* // Include the length header
|
||||
* head.setHeader(head.LENGTH, new Long(obj.length));
|
||||
* // Initiate the PUT request
|
||||
* Operation op = conn.put(head);
|
||||
* // Open the output stream to put the object to it
|
||||
* DataOutputStream out = op.openDataOutputStream();
|
||||
* // Send the object to the server
|
||||
* out.write(obj);
|
||||
* // End the transaction
|
||||
* out.close();
|
||||
* op.close();
|
||||
* }
|
||||
* </PRE>
|
||||
* <P>
|
||||
* <STRONG>GET example</STRONG>
|
||||
* <P>
|
||||
* <PRE>
|
||||
* byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
|
||||
* // Send the initial GET request to the server
|
||||
* Operation op = conn.get(head);
|
||||
* // Retrieve the length of the object being sent back
|
||||
* int length = op.getLength();
|
||||
* // Create space for the object
|
||||
* byte[] obj = new byte[length];
|
||||
* // Get the object from the input stream
|
||||
* DataInputStream in = trans.openDataInputStream();
|
||||
* in.read(obj);
|
||||
* // End the transaction
|
||||
* in.close();
|
||||
* op.close();
|
||||
* return obj;
|
||||
* }
|
||||
* </PRE>
|
||||
*
|
||||
* <H3>Client PUT Operation Flow</H3> For PUT operations, a call to
|
||||
* <code>close()</code> the <code>OutputStream</code> returned from
|
||||
* <code>openOutputStream()</code> or <code>openDataOutputStream()</code> will
|
||||
* signal that the request is done. (In OBEX terms, the End-Of-Body header
|
||||
* should be sent and the final bit in the request will be set.) At this point,
|
||||
* the reply from the server may begin to be processed. A call to
|
||||
* <code>getResponseCode()</code> will do an implicit close on the
|
||||
* <code>OutputStream</code> and therefore signal that the request is done.
|
||||
* <H3>Client GET Operation Flow</H3> For GET operation, a call to
|
||||
* <code>openInputStream()</code> or <code>openDataInputStream()</code> will
|
||||
* signal that the request is done. (In OBEX terms, the final bit in the request
|
||||
* will be set.) A call to <code>getResponseCode()</code> will cause an implicit
|
||||
* close on the <code>InputStream</code>. No further data may be read at this
|
||||
* point.
|
||||
* @hide
|
||||
*/
|
||||
public interface Operation {
|
||||
|
||||
/**
|
||||
* Sends an ABORT message to the server. By calling this method, the
|
||||
* corresponding input and output streams will be closed along with this
|
||||
* object. No headers are sent in the abort request. This will end the
|
||||
* operation since <code>close()</code> will be called by this method.
|
||||
* @throws IOException if the transaction has already ended or if an OBEX
|
||||
* server calls this method
|
||||
*/
|
||||
void abort() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the headers that have been received during the operation.
|
||||
* Modifying the object returned has no effect on the headers that are sent
|
||||
* or retrieved.
|
||||
* @return the headers received during this <code>Operation</code>
|
||||
* @throws IOException if this <code>Operation</code> has been closed
|
||||
*/
|
||||
HeaderSet getReceivedHeader() throws IOException;
|
||||
|
||||
/**
|
||||
* Specifies the headers that should be sent in the next OBEX message that
|
||||
* is sent.
|
||||
* @param headers the headers to send in the next message
|
||||
* @throws IOException if this <code>Operation</code> has been closed or the
|
||||
* transaction has ended and no further messages will be exchanged
|
||||
* @throws IllegalArgumentException if <code>headers</code> was not created
|
||||
* by a call to <code>ServerRequestHandler.createHeaderSet()</code>
|
||||
* or <code>ClientSession.createHeaderSet()</code>
|
||||
* @throws NullPointerException if <code>headers</code> if <code>null</code>
|
||||
*/
|
||||
void sendHeaders(HeaderSet headers) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the response code received from the server. Response codes are
|
||||
* defined in the <code>ResponseCodes</code> class.
|
||||
* @see ResponseCodes
|
||||
* @return the response code retrieved from the server
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the transaction; if this object was created by an OBEX server
|
||||
*/
|
||||
int getResponseCode() throws IOException;
|
||||
|
||||
String getEncoding();
|
||||
|
||||
long getLength();
|
||||
|
||||
int getHeaderLength();
|
||||
|
||||
String getType();
|
||||
|
||||
InputStream openInputStream() throws IOException;
|
||||
|
||||
DataInputStream openDataInputStream() throws IOException;
|
||||
|
||||
OutputStream openOutputStream() throws IOException;
|
||||
|
||||
DataOutputStream openDataOutputStream() throws IOException;
|
||||
|
||||
void close() throws IOException;
|
||||
|
||||
int getMaxPacketSize();
|
||||
|
||||
public void noBodyHeader();
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
/**
|
||||
* This class holds user name and password combinations.
|
||||
* @hide
|
||||
*/
|
||||
public final class PasswordAuthentication {
|
||||
|
||||
private byte[] mUserName;
|
||||
|
||||
private final byte[] mPassword;
|
||||
|
||||
/**
|
||||
* Creates a new <code>PasswordAuthentication</code> with the user name and
|
||||
* password provided.
|
||||
* @param userName the user name to include; this may be <code>null</code>
|
||||
* @param password the password to include in the response
|
||||
* @throws NullPointerException if <code>password</code> is
|
||||
* <code>null</code>
|
||||
*/
|
||||
public PasswordAuthentication(final byte[] userName, final byte[] password) {
|
||||
if (userName != null) {
|
||||
mUserName = new byte[userName.length];
|
||||
System.arraycopy(userName, 0, mUserName, 0, userName.length);
|
||||
}
|
||||
|
||||
mPassword = new byte[password.length];
|
||||
System.arraycopy(password, 0, mPassword, 0, password.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user name that was specified in the constructor. The user
|
||||
* name may be <code>null</code>.
|
||||
* @return the user name
|
||||
*/
|
||||
public byte[] getUserName() {
|
||||
return mUserName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the password.
|
||||
* @return the password
|
||||
*/
|
||||
public byte[] getPassword() {
|
||||
return mPassword;
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This object provides an input stream to the Operation objects used in this
|
||||
* package.
|
||||
* @hide
|
||||
*/
|
||||
public final class PrivateInputStream extends InputStream {
|
||||
|
||||
private BaseStream mParent;
|
||||
|
||||
private byte[] mData;
|
||||
|
||||
private int mIndex;
|
||||
|
||||
private boolean mOpen;
|
||||
|
||||
/**
|
||||
* Creates an input stream for the <code>Operation</code> to read from
|
||||
* @param p the connection this input stream is for
|
||||
*/
|
||||
public PrivateInputStream(BaseStream p) {
|
||||
mParent = p;
|
||||
mData = new byte[0];
|
||||
mIndex = 0;
|
||||
mOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes that can be read (or skipped over) from this
|
||||
* input stream without blocking by the next caller of a method for this
|
||||
* input stream. The next caller might be the same thread or or another
|
||||
* thread.
|
||||
* @return the number of bytes that can be read from this input stream
|
||||
* without blocking
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public synchronized int available() throws IOException {
|
||||
ensureOpen();
|
||||
return mData.length - mIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from the input stream. The value byte is
|
||||
* returned as an int in the range 0 to 255. If no byte is available because
|
||||
* the end of the stream has been reached, the value -1 is returned. This
|
||||
* method blocks until input data is available, the end of the stream is
|
||||
* detected, or an exception is thrown.
|
||||
* @return the byte read from the input stream or -1 if it reaches the end of
|
||||
* stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
ensureOpen();
|
||||
while (mData.length == mIndex) {
|
||||
if (!mParent.continueOperation(true, true)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return (mData[mIndex++] & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read(byte[] b, int offset, int length) throws IOException {
|
||||
|
||||
if (b == null) {
|
||||
throw new IOException("buffer is null");
|
||||
}
|
||||
if ((offset | length) < 0 || length > b.length - offset) {
|
||||
throw new ArrayIndexOutOfBoundsException("index outof bound");
|
||||
}
|
||||
ensureOpen();
|
||||
|
||||
int currentDataLength = mData.length - mIndex;
|
||||
int remainReadLength = length;
|
||||
int offset1 = offset;
|
||||
int result = 0;
|
||||
|
||||
while (currentDataLength <= remainReadLength) {
|
||||
System.arraycopy(mData, mIndex, b, offset1, currentDataLength);
|
||||
mIndex += currentDataLength;
|
||||
offset1 += currentDataLength;
|
||||
result += currentDataLength;
|
||||
remainReadLength -= currentDataLength;
|
||||
|
||||
if (!mParent.continueOperation(true, true)) {
|
||||
return result == 0 ? -1 : result;
|
||||
}
|
||||
currentDataLength = mData.length - mIndex;
|
||||
}
|
||||
if (remainReadLength > 0) {
|
||||
System.arraycopy(mData, mIndex, b, offset1, remainReadLength);
|
||||
mIndex += remainReadLength;
|
||||
result += remainReadLength;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the <code>OperationImpl</code> thread to add body data to the
|
||||
* input stream.
|
||||
* @param body the data to add to the stream
|
||||
* @param start the start of the body to array to copy
|
||||
*/
|
||||
public synchronized void writeBytes(byte[] body, int start) {
|
||||
|
||||
int length = (body.length - start) + (mData.length - mIndex);
|
||||
byte[] temp = new byte[length];
|
||||
|
||||
System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex);
|
||||
System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start);
|
||||
|
||||
mData = temp;
|
||||
mIndex = 0;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that this stream is open
|
||||
* @throws IOException if the stream is not open
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
mParent.ensureOpen();
|
||||
if (!mOpen) {
|
||||
throw new IOException("Input stream is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the input stream. If the input stream is already closed, do
|
||||
* nothing.
|
||||
* @throws IOException this will never happen
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mOpen = false;
|
||||
mParent.streamClosed(true);
|
||||
}
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* This object provides an output stream to the Operation objects used in this
|
||||
* package.
|
||||
* @hide
|
||||
*/
|
||||
public final class PrivateOutputStream extends OutputStream {
|
||||
|
||||
private BaseStream mParent;
|
||||
|
||||
private ByteArrayOutputStream mArray;
|
||||
|
||||
private boolean mOpen;
|
||||
|
||||
private int mMaxPacketSize;
|
||||
|
||||
/**
|
||||
* Creates an empty <code>PrivateOutputStream</code> to write to.
|
||||
* @param p the connection that this stream runs over
|
||||
*/
|
||||
public PrivateOutputStream(BaseStream p, int maxSize) {
|
||||
mParent = p;
|
||||
mArray = new ByteArrayOutputStream();
|
||||
mMaxPacketSize = maxSize;
|
||||
mOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many bytes have been written to the output stream.
|
||||
* @return the number of bytes written to the output stream
|
||||
*/
|
||||
public int size() {
|
||||
return mArray.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this output stream. The general contract for
|
||||
* write is that one byte is written to the output stream. The byte to be
|
||||
* written is the eight low-order bits of the argument b. The 24 high-order
|
||||
* bits of b are ignored.
|
||||
* @param b the byte to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
ensureOpen();
|
||||
mParent.ensureNotDone();
|
||||
mArray.write(b);
|
||||
if (mArray.size() == mMaxPacketSize) {
|
||||
mParent.continueOperation(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer) throws IOException {
|
||||
write(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
|
||||
int offset1 = offset;
|
||||
int remainLength = count;
|
||||
|
||||
if (buffer == null) {
|
||||
throw new IOException("buffer is null");
|
||||
}
|
||||
if ((offset | count) < 0 || count > buffer.length - offset) {
|
||||
throw new IndexOutOfBoundsException("index outof bound");
|
||||
}
|
||||
|
||||
ensureOpen();
|
||||
mParent.ensureNotDone();
|
||||
while ((mArray.size() + remainLength) >= mMaxPacketSize) {
|
||||
int bufferLeft = mMaxPacketSize - mArray.size();
|
||||
mArray.write(buffer, offset1, bufferLeft);
|
||||
offset1 += bufferLeft;
|
||||
remainLength -= bufferLeft;
|
||||
mParent.continueOperation(true, false);
|
||||
}
|
||||
if (remainLength > 0) {
|
||||
mArray.write(buffer, offset1, remainLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the bytes that have been written to this stream.
|
||||
* @param size the size of the array to return
|
||||
* @return the byte array that is written
|
||||
*/
|
||||
public synchronized byte[] readBytes(int size) {
|
||||
if (mArray.size() > 0) {
|
||||
byte[] temp = mArray.toByteArray();
|
||||
mArray.reset();
|
||||
byte[] result = new byte[size];
|
||||
System.arraycopy(temp, 0, result, 0, size);
|
||||
if (temp.length != size) {
|
||||
mArray.write(temp, size, temp.length - size);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that this stream is open
|
||||
* @throws IOException if the stream is not open
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
mParent.ensureOpen();
|
||||
if (!mOpen) {
|
||||
throw new IOException("Output stream is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the output stream. If the input stream is already closed, do
|
||||
* nothing.
|
||||
* @throws IOException this will never happen
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mOpen = false;
|
||||
mParent.streamClosed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the connection is closed
|
||||
* @return <code>true</code> if the connection is closed; <code>false</code>
|
||||
* if the connection is open
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return !mOpen;
|
||||
}
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
/**
|
||||
* The <code>ResponseCodes</code> class contains the list of valid response
|
||||
* codes a server may send to a client.
|
||||
* <P>
|
||||
* <STRONG>IMPORTANT NOTE</STRONG>
|
||||
* <P>
|
||||
* The values in this interface represent the values defined in the IrOBEX
|
||||
* specification, which is different with the HTTP specification.
|
||||
* <P>
|
||||
* <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
|
||||
* further description since they are not defined in HTTP. The server will send
|
||||
* an <code>OBEX_DATABASE_FULL</code> message when the client requests that
|
||||
* something be placed into a database but the database is full (cannot take
|
||||
* more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
|
||||
* client wishes to access a database, database table, or database record that
|
||||
* has been locked.
|
||||
* @hide
|
||||
*/
|
||||
public final class ResponseCodes {
|
||||
|
||||
/**
|
||||
* Defines the OBEX CONTINUE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
|
||||
*/
|
||||
public static final int OBEX_HTTP_CONTINUE = 0x90;
|
||||
|
||||
/**
|
||||
* Defines the OBEX SUCCESS response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
|
||||
*/
|
||||
public static final int OBEX_HTTP_OK = 0xA0;
|
||||
|
||||
/**
|
||||
* Defines the OBEX CREATED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
|
||||
*/
|
||||
public static final int OBEX_HTTP_CREATED = 0xA1;
|
||||
|
||||
/**
|
||||
* Defines the OBEX ACCEPTED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
|
||||
*/
|
||||
public static final int OBEX_HTTP_ACCEPTED = 0xA2;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NO CONTENT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
|
||||
|
||||
/**
|
||||
* Defines the OBEX RESET CONTENT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
|
||||
*/
|
||||
public static final int OBEX_HTTP_RESET = 0xA5;
|
||||
|
||||
/**
|
||||
* Defines the OBEX PARTIAL CONTENT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
|
||||
*/
|
||||
public static final int OBEX_HTTP_PARTIAL = 0xA6;
|
||||
|
||||
/**
|
||||
* Defines the OBEX MULTIPLE_CHOICES response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
|
||||
*/
|
||||
public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
|
||||
|
||||
/**
|
||||
* Defines the OBEX MOVED PERMANENTLY response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
|
||||
*/
|
||||
public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
|
||||
|
||||
/**
|
||||
* Defines the OBEX MOVED TEMPORARILY response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
|
||||
*/
|
||||
public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
|
||||
|
||||
/**
|
||||
* Defines the OBEX SEE OTHER response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
|
||||
*/
|
||||
public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NOT MODIFIED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
|
||||
|
||||
/**
|
||||
* Defines the OBEX USE PROXY response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
|
||||
*/
|
||||
public static final int OBEX_HTTP_USE_PROXY = 0xB5;
|
||||
|
||||
/**
|
||||
* Defines the OBEX BAD REQUEST response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
|
||||
*/
|
||||
public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
|
||||
|
||||
/**
|
||||
* Defines the OBEX UNAUTHORIZED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
|
||||
*/
|
||||
public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
|
||||
|
||||
/**
|
||||
* Defines the OBEX PAYMENT REQUIRED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
|
||||
*/
|
||||
public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
|
||||
|
||||
/**
|
||||
* Defines the OBEX FORBIDDEN response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
|
||||
*/
|
||||
public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NOT FOUND response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
|
||||
|
||||
/**
|
||||
* Defines the OBEX METHOD NOT ALLOWED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
|
||||
*/
|
||||
public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NOT ACCEPTABLE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
|
||||
|
||||
/**
|
||||
* Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
|
||||
*/
|
||||
public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
|
||||
|
||||
/**
|
||||
* Defines the OBEX REQUEST TIME OUT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
|
||||
*/
|
||||
public static final int OBEX_HTTP_TIMEOUT = 0xC8;
|
||||
|
||||
/**
|
||||
* Defines the OBEX METHOD CONFLICT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
|
||||
*/
|
||||
public static final int OBEX_HTTP_CONFLICT = 0xC9;
|
||||
|
||||
/**
|
||||
* Defines the OBEX METHOD GONE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
|
||||
*/
|
||||
public static final int OBEX_HTTP_GONE = 0xCA;
|
||||
|
||||
/**
|
||||
* Defines the OBEX METHOD LENGTH REQUIRED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
|
||||
*/
|
||||
public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
|
||||
|
||||
/**
|
||||
* Defines the OBEX PRECONDITION FAILED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
|
||||
*/
|
||||
public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
|
||||
|
||||
/**
|
||||
* Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
|
||||
*/
|
||||
public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
|
||||
|
||||
/**
|
||||
* Defines the OBEX REQUESTED URL TOO LARGE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
|
||||
*/
|
||||
public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
|
||||
|
||||
/**
|
||||
* Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
|
||||
*/
|
||||
public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
|
||||
|
||||
/**
|
||||
* Defines the OBEX INTERNAL SERVER ERROR response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
|
||||
*/
|
||||
public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
|
||||
|
||||
/**
|
||||
* Defines the OBEX NOT IMPLEMENTED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
|
||||
*/
|
||||
public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
|
||||
|
||||
/**
|
||||
* Defines the OBEX BAD GATEWAY response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
|
||||
*/
|
||||
public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
|
||||
|
||||
/**
|
||||
* Defines the OBEX SERVICE UNAVAILABLE response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
|
||||
*/
|
||||
public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
|
||||
|
||||
/**
|
||||
* Defines the OBEX GATEWAY TIMEOUT response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
|
||||
*/
|
||||
public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
|
||||
|
||||
/**
|
||||
* Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
|
||||
*/
|
||||
public static final int OBEX_HTTP_VERSION = 0xD5;
|
||||
|
||||
/**
|
||||
* Defines the OBEX DATABASE FULL response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
|
||||
*/
|
||||
public static final int OBEX_DATABASE_FULL = 0xE0;
|
||||
|
||||
/**
|
||||
* Defines the OBEX DATABASE LOCKED response code.
|
||||
* <P>
|
||||
* The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
|
||||
*/
|
||||
public static final int OBEX_DATABASE_LOCKED = 0xE1;
|
||||
|
||||
/**
|
||||
* Constructor does nothing.
|
||||
*/
|
||||
private ResponseCodes() {
|
||||
}
|
||||
}
|
@ -1,861 +0,0 @@
|
||||
/* Copyright (c) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2015 Samsung LSI
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class implements the Operation interface for server side connections.
|
||||
* <P>
|
||||
* <STRONG>Request Codes</STRONG> There are four different request codes that
|
||||
* are in this class. 0x02 is a PUT request that signals that the request is not
|
||||
* complete and requires an additional OBEX packet. 0x82 is a PUT request that
|
||||
* says that request is complete. In this case, the server can begin sending the
|
||||
* response. The 0x03 is a GET request that signals that the request is not
|
||||
* finished. When the server receives a 0x83, the client is signaling the server
|
||||
* that it is done with its request. TODO: Extend the ClientOperation and reuse
|
||||
* the methods defined TODO: in that class.
|
||||
* @hide
|
||||
*/
|
||||
public final class ServerOperation implements Operation, BaseStream {
|
||||
|
||||
private static final String TAG = "ServerOperation";
|
||||
|
||||
private static final boolean V = ObexHelper.VDBG; // Verbose debugging
|
||||
|
||||
public boolean isAborted;
|
||||
|
||||
public HeaderSet requestHeader;
|
||||
|
||||
public HeaderSet replyHeader;
|
||||
|
||||
public boolean finalBitSet;
|
||||
|
||||
private InputStream mInput;
|
||||
|
||||
private ServerSession mParent;
|
||||
|
||||
private int mMaxPacketLength;
|
||||
|
||||
private int mResponseSize;
|
||||
|
||||
private boolean mClosed;
|
||||
|
||||
private boolean mGetOperation;
|
||||
|
||||
private PrivateInputStream mPrivateInput;
|
||||
|
||||
private PrivateOutputStream mPrivateOutput;
|
||||
|
||||
private ObexTransport mTransport;
|
||||
|
||||
private boolean mPrivateOutputOpen;
|
||||
|
||||
private String mExceptionString;
|
||||
|
||||
private ServerRequestHandler mListener;
|
||||
|
||||
private boolean mRequestFinished;
|
||||
|
||||
private boolean mHasBody;
|
||||
|
||||
private boolean mSendBodyHeader = true;
|
||||
// Assume SRM disabled - needs to be explicit
|
||||
// enabled by client
|
||||
private boolean mSrmEnabled = false;
|
||||
// A latch - when triggered, there is not way back ;-)
|
||||
private boolean mSrmActive = false;
|
||||
// Set to true when a SRM enable response have been send
|
||||
private boolean mSrmResponseSent = false;
|
||||
// keep waiting until final-bit is received in request
|
||||
// to handle the case where the SRM enable header is in
|
||||
// a different OBEX packet than the SRMP header.
|
||||
private boolean mSrmWaitingForRemote = true;
|
||||
// Why should we wait? - currently not exposed to apps.
|
||||
private boolean mSrmLocalWait = false;
|
||||
|
||||
/**
|
||||
* Creates new ServerOperation
|
||||
* @param p the parent that created this object
|
||||
* @param in the input stream to read from
|
||||
* @param out the output stream to write to
|
||||
* @param request the initial request that was received from the client
|
||||
* @param maxSize the max packet size that the client will accept
|
||||
* @param listen the listener that is responding to the request
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
|
||||
ServerRequestHandler listen) throws IOException {
|
||||
|
||||
isAborted = false;
|
||||
mParent = p;
|
||||
mInput = in;
|
||||
mMaxPacketLength = maxSize;
|
||||
mClosed = false;
|
||||
requestHeader = new HeaderSet();
|
||||
replyHeader = new HeaderSet();
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
mResponseSize = 3;
|
||||
mListener = listen;
|
||||
mRequestFinished = false;
|
||||
mPrivateOutputOpen = false;
|
||||
mHasBody = false;
|
||||
ObexPacket packet;
|
||||
mTransport = p.getTransport();
|
||||
|
||||
/*
|
||||
* Determine if this is a PUT request
|
||||
*/
|
||||
if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
|
||||
(request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
|
||||
/*
|
||||
* It is a PUT request.
|
||||
*/
|
||||
mGetOperation = false;
|
||||
|
||||
/*
|
||||
* Determine if the final bit is set
|
||||
*/
|
||||
if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
|
||||
finalBitSet = false;
|
||||
} else {
|
||||
finalBitSet = true;
|
||||
mRequestFinished = true;
|
||||
}
|
||||
} else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
|
||||
(request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
|
||||
/*
|
||||
* It is a GET request.
|
||||
*/
|
||||
mGetOperation = true;
|
||||
|
||||
// For Get request, final bit set is decided by server side logic
|
||||
finalBitSet = false;
|
||||
|
||||
if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
|
||||
mRequestFinished = true;
|
||||
}
|
||||
} else {
|
||||
throw new IOException("ServerOperation can not handle such request");
|
||||
}
|
||||
|
||||
packet = ObexPacket.read(request, mInput);
|
||||
|
||||
/*
|
||||
* Determine if the packet length is larger than this device can receive
|
||||
*/
|
||||
if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
|
||||
throw new IOException("Packet received was too large. Length: "
|
||||
+ packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if any headers were sent in the initial request
|
||||
*/
|
||||
if (packet.mLength > 3) {
|
||||
if(!handleObexPacket(packet)) {
|
||||
return;
|
||||
}
|
||||
/* Don't Pre-Send continue when Remote requested for SRM
|
||||
* Let the Application confirm.
|
||||
*/
|
||||
if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
|
||||
+ " not hasBody case: " + mHasBody);
|
||||
if (!mHasBody && !mSrmEnabled) {
|
||||
while ((!mGetOperation) && (!finalBitSet)) {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
if (mPrivateInput.available() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Don't Pre-Send continue when Remote requested for SRM
|
||||
* Let the Application confirm.
|
||||
*/
|
||||
if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
|
||||
+ " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
|
||||
while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
|
||||
&& (mPrivateInput.available() == 0)) {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
if (mPrivateInput.available() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for get request finished !!!!
|
||||
while (mGetOperation && !mRequestFinished) {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse headers and update member variables
|
||||
* @param packet the received obex packet
|
||||
* @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
|
||||
* response have been send. Else true.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean handleObexPacket(ObexPacket packet) throws IOException {
|
||||
byte[] body = updateRequestHeaders(packet);
|
||||
|
||||
if (body != null) {
|
||||
mHasBody = true;
|
||||
}
|
||||
if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
|
||||
mListener.setConnectionId(ObexHelper
|
||||
.convertToLong(requestHeader.mConnectionID));
|
||||
} else {
|
||||
mListener.setConnectionId(1);
|
||||
}
|
||||
|
||||
if (requestHeader.mAuthResp != null) {
|
||||
if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
|
||||
mExceptionString = "Authentication Failed";
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
|
||||
mClosed = true;
|
||||
requestHeader.mAuthResp = null;
|
||||
return false;
|
||||
}
|
||||
requestHeader.mAuthResp = null;
|
||||
}
|
||||
|
||||
if (requestHeader.mAuthChall != null) {
|
||||
mParent.handleAuthChall(requestHeader);
|
||||
// send the auhtResp to the client
|
||||
replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
|
||||
System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
|
||||
replyHeader.mAuthResp.length);
|
||||
requestHeader.mAuthResp = null;
|
||||
requestHeader.mAuthChall = null;
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
mPrivateInput.writeBytes(body, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the request header set, and sniff on SRM headers to update local state.
|
||||
* @param data the OBEX packet data
|
||||
* @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
|
||||
* @throws IOException
|
||||
*/
|
||||
private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
|
||||
byte[] body = null;
|
||||
if (packet.mPayload != null) {
|
||||
body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
|
||||
}
|
||||
Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
|
||||
if(mTransport.isSrmSupported() && srmMode != null
|
||||
&& srmMode == ObexHelper.OBEX_SRM_ENABLE) {
|
||||
mSrmEnabled = true;
|
||||
if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
|
||||
}
|
||||
checkForSrmWait(packet.mHeaderId);
|
||||
if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
|
||||
if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
|
||||
mSrmActive = true;
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this only when a complete request have been received.
|
||||
* (This is not optimal, but the current design is not really suited to
|
||||
* the way SRM is specified.)
|
||||
*/
|
||||
private void checkForSrmWait(int headerId){
|
||||
if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
|
||||
|| headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
|
||||
|| headerId == ObexHelper.OBEX_OPCODE_PUT)) {
|
||||
try {
|
||||
mSrmWaitingForRemote = false;
|
||||
Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
|
||||
if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
|
||||
mSrmWaitingForRemote = true;
|
||||
// Clear the wait header, as the absents of the header when the final bit is set
|
||||
// indicates don't wait.
|
||||
requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
|
||||
}
|
||||
} catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValidBody() {
|
||||
return mHasBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the operation should continue or should wait. If it should
|
||||
* continue, this method will continue the operation.
|
||||
* @param sendEmpty if <code>true</code> then this will continue the
|
||||
* operation even if no headers will be sent; if <code>false</code>
|
||||
* then this method will only continue the operation if there are
|
||||
* headers to send
|
||||
* @param inStream if<code>true</code> the stream is input stream, otherwise
|
||||
* output stream
|
||||
* @return <code>true</code> if the operation was completed;
|
||||
* <code>false</code> if no operation took place
|
||||
*/
|
||||
public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
|
||||
throws IOException {
|
||||
if (!mGetOperation) {
|
||||
if (!finalBitSet) {
|
||||
if (sendEmpty) {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
return true;
|
||||
} else {
|
||||
if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
|
||||
* will wait for a response from the client before ending unless SRM is active.
|
||||
* @param type the response code to send back to the client
|
||||
* @return <code>true</code> if the final bit was not set on the reply;
|
||||
* <code>false</code> if no reply was received because the operation
|
||||
* ended, an abort was received, the final bit was set in the
|
||||
* reply or SRM is active.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public synchronized boolean sendReply(int type) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
boolean skipSend = false;
|
||||
boolean skipReceive = false;
|
||||
boolean srmRespSendPending = false;
|
||||
|
||||
long id = mListener.getConnectionId();
|
||||
if (id == -1) {
|
||||
replyHeader.mConnectionID = null;
|
||||
} else {
|
||||
replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
if(mSrmEnabled && !mSrmResponseSent) {
|
||||
// As we are not ensured that the SRM enable is in the first OBEX packet
|
||||
// We must check for each reply.
|
||||
if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
|
||||
replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
|
||||
srmRespSendPending = true;
|
||||
}
|
||||
|
||||
if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
|
||||
replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
|
||||
}
|
||||
|
||||
byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
|
||||
int bodyLength = -1;
|
||||
int orginalBodyLength = -1;
|
||||
|
||||
if (mPrivateOutput != null) {
|
||||
bodyLength = mPrivateOutput.size();
|
||||
orginalBodyLength = bodyLength;
|
||||
}
|
||||
|
||||
if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
|
||||
|
||||
int end = 0;
|
||||
int start = 0;
|
||||
|
||||
while (end != headerArray.length) {
|
||||
end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
|
||||
- ObexHelper.BASE_PACKET_LENGTH);
|
||||
if (end == -1) {
|
||||
|
||||
mClosed = true;
|
||||
|
||||
if (mPrivateInput != null) {
|
||||
mPrivateInput.close();
|
||||
}
|
||||
|
||||
if (mPrivateOutput != null) {
|
||||
mPrivateOutput.close();
|
||||
}
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
|
||||
throw new IOException("OBEX Packet exceeds max packet size");
|
||||
}
|
||||
byte[] sendHeader = new byte[end - start];
|
||||
System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
|
||||
|
||||
mParent.sendResponse(type, sendHeader);
|
||||
start = end;
|
||||
}
|
||||
|
||||
if (bodyLength > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
out.write(headerArray);
|
||||
}
|
||||
|
||||
// For Get operation: if response code is OBEX_HTTP_OK, then this is the
|
||||
// last packet; so set finalBitSet to true.
|
||||
if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
|
||||
finalBitSet = true;
|
||||
}
|
||||
|
||||
if(mSrmActive) {
|
||||
if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
|
||||
mSrmResponseSent == true) {
|
||||
// we are in the middle of a SRM PUT operation, don't send a continue.
|
||||
skipSend = true;
|
||||
} else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
|
||||
// We are still receiving the get request, receive, but don't send continue.
|
||||
skipSend = true;
|
||||
} else if(mGetOperation && mRequestFinished == true) {
|
||||
// All done receiving the GET request, send data to the client, without
|
||||
// expecting a continue.
|
||||
skipReceive = true;
|
||||
}
|
||||
if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
|
||||
+ " skipReceive==" + skipReceive);
|
||||
}
|
||||
if(srmRespSendPending) {
|
||||
if(V)Log.v(TAG,
|
||||
"SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
|
||||
mSrmResponseSent = true;
|
||||
}
|
||||
|
||||
if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
|
||||
if (bodyLength > 0) {
|
||||
/*
|
||||
* Determine if I can send the whole body or just part of
|
||||
* the body. Remember that there is the 3 bytes for the
|
||||
* response message and 3 bytes for the header ID and length
|
||||
*/
|
||||
if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
|
||||
bodyLength = mMaxPacketLength - headerArray.length - 6;
|
||||
}
|
||||
|
||||
byte[] body = mPrivateOutput.readBytes(bodyLength);
|
||||
|
||||
/*
|
||||
* Since this is a put request if the final bit is set or
|
||||
* the output stream is closed we need to send the 0x49
|
||||
* (End of Body) otherwise, we need to send 0x48 (Body)
|
||||
*/
|
||||
if ((finalBitSet) || (mPrivateOutput.isClosed())) {
|
||||
if(mSendBodyHeader == true) {
|
||||
out.write(0x49);
|
||||
bodyLength += 3;
|
||||
out.write((byte)(bodyLength >> 8));
|
||||
out.write((byte)bodyLength);
|
||||
out.write(body);
|
||||
}
|
||||
} else {
|
||||
if(mSendBodyHeader == true) {
|
||||
out.write(0x48);
|
||||
bodyLength += 3;
|
||||
out.write((byte)(bodyLength >> 8));
|
||||
out.write((byte)bodyLength);
|
||||
out.write(body);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
|
||||
if(mSendBodyHeader) {
|
||||
out.write(0x49);
|
||||
orginalBodyLength = 3;
|
||||
out.write((byte)(orginalBodyLength >> 8));
|
||||
out.write((byte)orginalBodyLength);
|
||||
}
|
||||
}
|
||||
|
||||
if(skipSend == false) {
|
||||
mResponseSize = 3;
|
||||
mParent.sendResponse(type, out.toByteArray());
|
||||
}
|
||||
|
||||
if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
|
||||
if(mGetOperation && skipReceive) {
|
||||
// Here we need to check for and handle abort (throw an exception).
|
||||
// Any other signal received should be discarded silently (only on server side)
|
||||
checkSrmRemoteAbort();
|
||||
} else {
|
||||
// Receive and handle data (only send reply if !skipSend)
|
||||
// Read a complete OBEX Packet
|
||||
ObexPacket packet = ObexPacket.read(mInput);
|
||||
|
||||
int headerId = packet.mHeaderId;
|
||||
if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
|
||||
&& (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
|
||||
&& (headerId != ObexHelper.OBEX_OPCODE_GET)
|
||||
&& (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
|
||||
|
||||
/*
|
||||
* Determine if an ABORT was sent as the reply
|
||||
*/
|
||||
if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
|
||||
handleRemoteAbort();
|
||||
} else {
|
||||
// TODO:shall we send this if it occurs during SRM? Errata on the subject
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
|
||||
mClosed = true;
|
||||
mExceptionString = "Bad Request Received";
|
||||
throw new IOException("Bad Request Received");
|
||||
}
|
||||
} else {
|
||||
|
||||
if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
|
||||
finalBitSet = true;
|
||||
} else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
|
||||
mRequestFinished = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the packet length is larger than the negotiated packet size
|
||||
*/
|
||||
if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
|
||||
throw new IOException("Packet received was too large");
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if any headers were sent in the initial request
|
||||
*/
|
||||
if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
|
||||
if(handleObexPacket(packet) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will look for an abort from the peer during a SRM transfer.
|
||||
* The function will not block if no data has been received from the remote device.
|
||||
* If data have been received, the function will block while reading the incoming
|
||||
* OBEX package.
|
||||
* An Abort request will be handled, and cause an IOException("Abort Received").
|
||||
* Other messages will be discarded silently as per GOEP specification.
|
||||
* @throws IOException if an abort request have been received.
|
||||
* TODO: I think this is an error in the specification. If we discard other messages,
|
||||
* the peer device will most likely stall, as it will not receive the expected
|
||||
* response for the message...
|
||||
* I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
|
||||
* header values shall be ignored by the receiving device."
|
||||
* If any signal is received during an active SRM transfer it is unexpected regardless
|
||||
* whether or not it contains SRM/SRMP headers...
|
||||
*/
|
||||
private void checkSrmRemoteAbort() throws IOException {
|
||||
if(mInput.available() > 0) {
|
||||
ObexPacket packet = ObexPacket.read(mInput);
|
||||
/*
|
||||
* Determine if an ABORT was sent as the reply
|
||||
*/
|
||||
if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
|
||||
handleRemoteAbort();
|
||||
} else {
|
||||
// TODO: should we throw an exception here anyway? - don't see how to
|
||||
// ignore SRM/SRMP headers without ignoring the complete signal
|
||||
// (in this particular case).
|
||||
Log.w(TAG, "Received unexpected request from client - discarding...\n"
|
||||
+ " headerId: " + packet.mHeaderId + " length: " + packet.mLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRemoteAbort() throws IOException {
|
||||
/* TODO: To increase the speed of the abort operation in SRM, we need
|
||||
* to be able to flush the L2CAP queue for the PSM in use.
|
||||
* This could be implemented by introducing a control
|
||||
* message to be send over the socket, that in the abort case
|
||||
* could carry a flush command. */
|
||||
mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
|
||||
mClosed = true;
|
||||
isAborted = true;
|
||||
mExceptionString = "Abort Received";
|
||||
throw new IOException("Abort Received");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an ABORT message to the server. By calling this method, the
|
||||
* corresponding input and output streams will be closed along with this
|
||||
* object.
|
||||
* @throws IOException if the transaction has already ended or if an OBEX
|
||||
* server called this method
|
||||
*/
|
||||
public void abort() throws IOException {
|
||||
throw new IOException("Called from a server");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the headers that have been received during the operation.
|
||||
* Modifying the object returned has no effect on the headers that are sent
|
||||
* or retrieved.
|
||||
* @return the headers received during this <code>Operation</code>
|
||||
* @throws IOException if this <code>Operation</code> has been closed
|
||||
*/
|
||||
public HeaderSet getReceivedHeader() throws IOException {
|
||||
ensureOpen();
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the headers that should be sent in the next OBEX message that
|
||||
* is sent.
|
||||
* @param headers the headers to send in the next message
|
||||
* @throws IOException if this <code>Operation</code> has been closed or the
|
||||
* transaction has ended and no further messages will be exchanged
|
||||
* @throws IllegalArgumentException if <code>headers</code> was not created
|
||||
* by a call to <code>ServerRequestHandler.createHeaderSet()</code>
|
||||
*/
|
||||
public void sendHeaders(HeaderSet headers) throws IOException {
|
||||
ensureOpen();
|
||||
|
||||
if (headers == null) {
|
||||
throw new IOException("Headers may not be null");
|
||||
}
|
||||
|
||||
int[] headerList = headers.getHeaderList();
|
||||
if (headerList != null) {
|
||||
for (int i = 0; i < headerList.length; i++) {
|
||||
replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the response code retrieved from the server. Response codes are
|
||||
* defined in the <code>ResponseCodes</code> interface.
|
||||
* @return the response code retrieved from the server
|
||||
* @throws IOException if an error occurred in the transport layer during
|
||||
* the transaction; if this method is called on a
|
||||
* <code>HeaderSet</code> object created by calling
|
||||
* <code>createHeaderSet</code> in a <code>ClientSession</code>
|
||||
* object; if this is called from a server
|
||||
*/
|
||||
public int getResponseCode() throws IOException {
|
||||
throw new IOException("Called from a server");
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns <code>null</code>
|
||||
* @return <code>null</code>
|
||||
*/
|
||||
public String getEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of content that the resource connected to is providing.
|
||||
* E.g. if the connection is via HTTP, then the value of the content-type
|
||||
* header field is returned.
|
||||
* @return the content type of the resource that the URL references, or
|
||||
* <code>null</code> if not known
|
||||
*/
|
||||
public String getType() {
|
||||
try {
|
||||
return (String)requestHeader.getHeader(HeaderSet.TYPE);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the content which is being provided. E.g. if the
|
||||
* connection is via HTTP, then the value of the content-length header field
|
||||
* is returned.
|
||||
* @return the content length of the resource that this connection's URL
|
||||
* references, or -1 if the content length is not known
|
||||
*/
|
||||
public long getLength() {
|
||||
try {
|
||||
Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
|
||||
|
||||
if (temp == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return temp.longValue();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxPacketSize() {
|
||||
return mMaxPacketLength - 6 - getHeaderLength();
|
||||
}
|
||||
|
||||
public int getHeaderLength() {
|
||||
long id = mListener.getConnectionId();
|
||||
if (id == -1) {
|
||||
replyHeader.mConnectionID = null;
|
||||
} else {
|
||||
replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
|
||||
|
||||
return headerArray.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return an input stream for a connection.
|
||||
* @return an input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public InputStream openInputStream() throws IOException {
|
||||
ensureOpen();
|
||||
return mPrivateInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return a data input stream for a connection.
|
||||
* @return an input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public DataInputStream openDataInputStream() throws IOException {
|
||||
return new DataInputStream(openInputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return an output stream for a connection.
|
||||
* @return an output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
ensureOpen();
|
||||
|
||||
if (mPrivateOutputOpen) {
|
||||
throw new IOException("no more input streams available, stream already opened");
|
||||
}
|
||||
|
||||
if (!mRequestFinished) {
|
||||
throw new IOException("no output streams available ,request not finished");
|
||||
}
|
||||
|
||||
if (mPrivateOutput == null) {
|
||||
mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
|
||||
}
|
||||
mPrivateOutputOpen = true;
|
||||
return mPrivateOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return a data output stream for a connection.
|
||||
* @return an output stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public DataOutputStream openDataOutputStream() throws IOException {
|
||||
return new DataOutputStream(openOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection and ends the transaction
|
||||
* @throws IOException if the operation has already ended or is closed
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
ensureOpen();
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the connection is open and no exceptions should be thrown.
|
||||
* @throws IOException if an exception needs to be thrown
|
||||
*/
|
||||
public void ensureOpen() throws IOException {
|
||||
if (mExceptionString != null) {
|
||||
throw new IOException(mExceptionString);
|
||||
}
|
||||
if (mClosed) {
|
||||
throw new IOException("Operation has already ended");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that additional information may be sent. In other words, the
|
||||
* operation is not done.
|
||||
* <P>
|
||||
* Included to implement the BaseStream interface only. It does not do
|
||||
* anything on the server side since the operation of the Operation object
|
||||
* is not done until after the handler returns from its method.
|
||||
* @throws IOException if the operation is completed
|
||||
*/
|
||||
public void ensureNotDone() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the output or input stream is closed. It does not do anything
|
||||
* on the server side since the operation of the Operation object is not
|
||||
* done until after the handler returns from its method.
|
||||
* @param inStream <code>true</code> if the input stream is closed;
|
||||
* <code>false</code> if the output stream is closed
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void streamClosed(boolean inStream) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
public void noBodyHeader(){
|
||||
mSendBodyHeader = false;
|
||||
}
|
||||
}
|
@ -1,287 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
/**
|
||||
* The <code>ServerRequestHandler</code> class defines an event listener that
|
||||
* will respond to OBEX requests made to the server.
|
||||
* <P>
|
||||
* The <code>onConnect()</code>, <code>onSetPath()</code>,
|
||||
* <code>onDelete()</code>, <code>onGet()</code>, and <code>onPut()</code>
|
||||
* methods may return any response code defined in the
|
||||
* <code>ResponseCodes</code> class except for <code>OBEX_HTTP_CONTINUE</code>.
|
||||
* If <code>OBEX_HTTP_CONTINUE</code> or a value not defined in the
|
||||
* <code>ResponseCodes</code> class is returned, the server implementation will
|
||||
* send an <code>OBEX_HTTP_INTERNAL_ERROR</code> response to the client.
|
||||
* <P>
|
||||
* <STRONG>Connection ID and Target Headers</STRONG>
|
||||
* <P>
|
||||
* According to the IrOBEX specification, a packet may not contain a Connection
|
||||
* ID and Target header. Since the Connection ID header is managed by the
|
||||
* implementation, it will not send a Connection ID header, if a Connection ID
|
||||
* was specified, in a packet that has a Target header. In other words, if an
|
||||
* application adds a Target header to a <code>HeaderSet</code> object used in
|
||||
* an OBEX operation and a Connection ID was specified, no Connection ID will be
|
||||
* sent in the packet containing the Target header.
|
||||
* <P>
|
||||
* <STRONG>CREATE-EMPTY Requests</STRONG>
|
||||
* <P>
|
||||
* A CREATE-EMPTY request allows clients to create empty objects on the server.
|
||||
* When a CREATE-EMPTY request is received, the <code>onPut()</code> method will
|
||||
* be called by the implementation. To differentiate between a normal PUT
|
||||
* request and a CREATE-EMPTY request, an application must open the
|
||||
* <code>InputStream</code> from the <code>Operation</code> object passed to the
|
||||
* <code>onPut()</code> method. For a PUT request, the application will be able
|
||||
* to read Body data from this <code>InputStream</code>. For a CREATE-EMPTY
|
||||
* request, there will be no Body data to read. Therefore, a call to
|
||||
* <code>InputStream.read()</code> will return -1.
|
||||
* @hide
|
||||
*/
|
||||
public class ServerRequestHandler {
|
||||
|
||||
private long mConnectionId;
|
||||
|
||||
/**
|
||||
* Creates a <code>ServerRequestHandler</code>.
|
||||
*/
|
||||
protected ServerRequestHandler() {
|
||||
/*
|
||||
* A connection ID of -1 implies there is no conenction ID
|
||||
*/
|
||||
mConnectionId = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection ID header to include in the reply packets.
|
||||
* @param connectionId the connection ID to use; -1 if no connection ID
|
||||
* should be sent
|
||||
* @throws IllegalArgumentException if <code>id</code> is not in the range
|
||||
* -1 to 2<sup>32</sup>-1
|
||||
*/
|
||||
public void setConnectionId(final long connectionId) {
|
||||
if ((connectionId < -1) || (connectionId > 0xFFFFFFFFL)) {
|
||||
throw new IllegalArgumentException("Illegal Connection ID");
|
||||
}
|
||||
mConnectionId = connectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the connection ID that is being used in the present connection.
|
||||
* This method will return -1 if no connection ID is being used.
|
||||
* @return the connection id being used or -1 if no connection ID is being
|
||||
* used
|
||||
*/
|
||||
public long getConnectionId() {
|
||||
return mConnectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a CONNECT request is received.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* <code>onConnect()</code> will always return an <code>OBEX_HTTP_OK</code>
|
||||
* response code.
|
||||
* <P>
|
||||
* The headers received in the request can be retrieved from the
|
||||
* <code>request</code> argument. The headers that should be sent in the
|
||||
* reply must be specified in the <code>reply</code> argument.
|
||||
* @param request contains the headers sent by the client;
|
||||
* <code>request</code> will never be <code>null</code>
|
||||
* @param reply the headers that should be sent in the reply;
|
||||
* <code>reply</code> will never be <code>null</code>
|
||||
* @return a response code defined in <code>ResponseCodes</code> that will
|
||||
* be returned to the client; if an invalid response code is
|
||||
* provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
|
||||
* will be used
|
||||
*/
|
||||
public int onConnect(HeaderSet request, HeaderSet reply) {
|
||||
return ResponseCodes.OBEX_HTTP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a DISCONNECT request is received.
|
||||
* <P>
|
||||
* The headers received in the request can be retrieved from the
|
||||
* <code>request</code> argument. The headers that should be sent in the
|
||||
* reply must be specified in the <code>reply</code> argument.
|
||||
* @param request contains the headers sent by the client;
|
||||
* <code>request</code> will never be <code>null</code>
|
||||
* @param reply the headers that should be sent in the reply;
|
||||
* <code>reply</code> will never be <code>null</code>
|
||||
*/
|
||||
public void onDisconnect(HeaderSet request, HeaderSet reply) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a SETPATH request is received.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* <code>onSetPath()</code> will always return an
|
||||
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
|
||||
* <P>
|
||||
* The headers received in the request can be retrieved from the
|
||||
* <code>request</code> argument. The headers that should be sent in the
|
||||
* reply must be specified in the <code>reply</code> argument.
|
||||
* @param request contains the headers sent by the client;
|
||||
* <code>request</code> will never be <code>null</code>
|
||||
* @param reply the headers that should be sent in the reply;
|
||||
* <code>reply</code> will never be <code>null</code>
|
||||
* @param backup <code>true</code> if the client requests that the server
|
||||
* back up one directory before changing to the path described by
|
||||
* <code>name</code>; <code>false</code> to apply the request to the
|
||||
* present path
|
||||
* @param create <code>true</code> if the path should be created if it does
|
||||
* not already exist; <code>false</code> if the path should not be
|
||||
* created if it does not exist and an error code should be returned
|
||||
* @return a response code defined in <code>ResponseCodes</code> that will
|
||||
* be returned to the client; if an invalid response code is
|
||||
* provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
|
||||
* will be used
|
||||
*/
|
||||
public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
|
||||
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a DELETE request is received.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* <code>onDelete()</code> will always return an
|
||||
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
|
||||
* <P>
|
||||
* The headers received in the request can be retrieved from the
|
||||
* <code>request</code> argument. The headers that should be sent in the
|
||||
* reply must be specified in the <code>reply</code> argument.
|
||||
* @param request contains the headers sent by the client;
|
||||
* <code>request</code> will never be <code>null</code>
|
||||
* @param reply the headers that should be sent in the reply;
|
||||
* <code>reply</code> will never be <code>null</code>
|
||||
* @return a response code defined in <code>ResponseCodes</code> that will
|
||||
* be returned to the client; if an invalid response code is
|
||||
* provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
|
||||
* will be used
|
||||
*/
|
||||
public int onDelete(HeaderSet request, HeaderSet reply) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a ABORT request is received.
|
||||
*/
|
||||
public int onAbort(HeaderSet request, HeaderSet reply) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a PUT request is received.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* <code>onPut()</code> will always return an
|
||||
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
|
||||
* <P>
|
||||
* If an ABORT request is received during the processing of a PUT request,
|
||||
* <code>op</code> will be closed by the implementation.
|
||||
* @param operation contains the headers sent by the client and allows new
|
||||
* headers to be sent in the reply; <code>op</code> will never be
|
||||
* <code>null</code>
|
||||
* @return a response code defined in <code>ResponseCodes</code> that will
|
||||
* be returned to the client; if an invalid response code is
|
||||
* provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
|
||||
* will be used
|
||||
*/
|
||||
public int onPut(Operation operation) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a GET request is received.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* <code>onGet()</code> will always return an
|
||||
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
|
||||
* <P>
|
||||
* If an ABORT request is received during the processing of a GET request,
|
||||
* <code>op</code> will be closed by the implementation.
|
||||
* @param operation contains the headers sent by the client and allows new
|
||||
* headers to be sent in the reply; <code>op</code> will never be
|
||||
* <code>null</code>
|
||||
* @return a response code defined in <code>ResponseCodes</code> that will
|
||||
* be returned to the client; if an invalid response code is
|
||||
* provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
|
||||
* will be used
|
||||
*/
|
||||
public int onGet(Operation operation) {
|
||||
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this object attempts to authenticate a client and the
|
||||
* authentication request fails because the response digest in the
|
||||
* authentication response header was wrong.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* this method will do nothing.
|
||||
* @param userName the user name returned in the authentication response;
|
||||
* <code>null</code> if no user name was provided in the response
|
||||
*/
|
||||
public void onAuthenticationFailure(byte[] userName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by ServerSession to update the status of current transaction
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* this method will do nothing.
|
||||
*/
|
||||
public void updateStatus(String message) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when session is closed.
|
||||
* <P>
|
||||
* If this method is not implemented by the class that extends this class,
|
||||
* this method will do nothing.
|
||||
*/
|
||||
public void onClose() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to add Single Response Mode support - e.g. if the supplied
|
||||
* transport is l2cap.
|
||||
* @return True if SRM is supported, else False
|
||||
*/
|
||||
public boolean isSrmSupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,742 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (c) 2015 Samsung LSI
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* This class in an implementation of the OBEX ServerSession.
|
||||
* @hide
|
||||
*/
|
||||
public final class ServerSession extends ObexSession implements Runnable {
|
||||
|
||||
private static final String TAG = "Obex ServerSession";
|
||||
private static final boolean V = ObexHelper.VDBG;
|
||||
|
||||
private ObexTransport mTransport;
|
||||
|
||||
private InputStream mInput;
|
||||
|
||||
private OutputStream mOutput;
|
||||
|
||||
private ServerRequestHandler mListener;
|
||||
|
||||
private Thread mProcessThread;
|
||||
|
||||
private int mMaxPacketLength;
|
||||
|
||||
private boolean mClosed;
|
||||
|
||||
/**
|
||||
* Creates new ServerSession.
|
||||
* @param trans the connection to the client
|
||||
* @param handler the event listener that will process requests
|
||||
* @param auth the authenticator to use with this connection
|
||||
* @throws IOException if an error occurred while opening the input and
|
||||
* output streams
|
||||
*/
|
||||
public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
|
||||
throws IOException {
|
||||
mAuthenticator = auth;
|
||||
mTransport = trans;
|
||||
mInput = mTransport.openInputStream();
|
||||
mOutput = mTransport.openOutputStream();
|
||||
mListener = handler;
|
||||
mMaxPacketLength = 256;
|
||||
|
||||
mClosed = false;
|
||||
mProcessThread = new Thread(this);
|
||||
mProcessThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes requests made to the server and forwards them to the
|
||||
* appropriate event listener.
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
boolean done = false;
|
||||
while (!done && !mClosed) {
|
||||
if(V) Log.v(TAG, "Waiting for incoming request...");
|
||||
int requestType = mInput.read();
|
||||
if(V) Log.v(TAG, "Read request: " + requestType);
|
||||
switch (requestType) {
|
||||
case ObexHelper.OBEX_OPCODE_CONNECT:
|
||||
handleConnectRequest();
|
||||
break;
|
||||
|
||||
case ObexHelper.OBEX_OPCODE_DISCONNECT:
|
||||
handleDisconnectRequest();
|
||||
break;
|
||||
|
||||
case ObexHelper.OBEX_OPCODE_GET:
|
||||
case ObexHelper.OBEX_OPCODE_GET_FINAL:
|
||||
handleGetRequest(requestType);
|
||||
break;
|
||||
|
||||
case ObexHelper.OBEX_OPCODE_PUT:
|
||||
case ObexHelper.OBEX_OPCODE_PUT_FINAL:
|
||||
handlePutRequest(requestType);
|
||||
break;
|
||||
|
||||
case ObexHelper.OBEX_OPCODE_SETPATH:
|
||||
handleSetPathRequest();
|
||||
break;
|
||||
case ObexHelper.OBEX_OPCODE_ABORT:
|
||||
handleAbortRequest();
|
||||
break;
|
||||
|
||||
case -1:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/*
|
||||
* Received a request type that is not recognized so I am
|
||||
* just going to read the packet and send a not implemented
|
||||
* to the client
|
||||
*/
|
||||
int length = mInput.read();
|
||||
length = (length << 8) + mInput.read();
|
||||
for (int i = 3; i < length; i++) {
|
||||
mInput.read();
|
||||
}
|
||||
sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NullPointerException e) {
|
||||
Log.d(TAG, "Exception occured - ignoring", e);
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Exception occured - ignoring", e);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a ABORT request from a client. This method will read the rest of
|
||||
* the request from the client. Assuming the request is valid, it will
|
||||
* create a <code>HeaderSet</code> object to pass to the
|
||||
* <code>ServerRequestHandler</code> object. After the handler processes the
|
||||
* request, this method will create a reply message to send to the server.
|
||||
*
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handleAbortRequest() throws IOException {
|
||||
int code = ResponseCodes.OBEX_HTTP_OK;
|
||||
HeaderSet request = new HeaderSet();
|
||||
HeaderSet reply = new HeaderSet();
|
||||
|
||||
int length = mInput.read();
|
||||
length = (length << 8) + mInput.read();
|
||||
if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
} else {
|
||||
for (int i = 3; i < length; i++) {
|
||||
mInput.read();
|
||||
}
|
||||
code = mListener.onAbort(request, reply);
|
||||
Log.v(TAG, "onAbort request handler return value- " + code);
|
||||
code = validateResponseCode(code);
|
||||
}
|
||||
sendResponse(code, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a PUT request from a client. This method will provide a
|
||||
* <code>ServerOperation</code> object to the request handler. The
|
||||
* <code>ServerOperation</code> object will handle the rest of the request.
|
||||
* It will also send replies and receive requests until the final reply
|
||||
* should be sent. When the final reply should be sent, this method will get
|
||||
* the response code to use and send the reply. The
|
||||
* <code>ServerOperation</code> object will always reply with a
|
||||
* OBEX_HTTP_CONTINUE reply. It will only reply if further information is
|
||||
* needed.
|
||||
* @param type the type of request received; either 0x02 or 0x82
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handlePutRequest(int type) throws IOException {
|
||||
ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
|
||||
try {
|
||||
int response = -1;
|
||||
|
||||
if ((op.finalBitSet) && !op.isValidBody()) {
|
||||
response = validateResponseCode(mListener
|
||||
.onDelete(op.requestHeader, op.replyHeader));
|
||||
} else {
|
||||
response = validateResponseCode(mListener.onPut(op));
|
||||
}
|
||||
if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
|
||||
op.sendReply(response);
|
||||
} else if (!op.isAborted) {
|
||||
// wait for the final bit
|
||||
while (!op.finalBitSet) {
|
||||
op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
|
||||
}
|
||||
op.sendReply(response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
/*To fix bugs in aborted cases,
|
||||
*(client abort file transfer prior to the last packet which has the end of body header,
|
||||
*internal error should not be sent because server has already replied with
|
||||
*OK response in "sendReply")
|
||||
*/
|
||||
if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
|
||||
if (!op.isAborted) {
|
||||
sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a GET request from a client. This method will provide a
|
||||
* <code>ServerOperation</code> object to the request handler. The
|
||||
* <code>ServerOperation</code> object will handle the rest of the request.
|
||||
* It will also send replies and receive requests until the final reply
|
||||
* should be sent. When the final reply should be sent, this method will get
|
||||
* the response code to use and send the reply. The
|
||||
* <code>ServerOperation</code> object will always reply with a
|
||||
* OBEX_HTTP_CONTINUE reply. It will only reply if further information is
|
||||
* needed.
|
||||
* @param type the type of request received; either 0x03 or 0x83
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handleGetRequest(int type) throws IOException {
|
||||
ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
|
||||
try {
|
||||
int response = validateResponseCode(mListener.onGet(op));
|
||||
|
||||
if (!op.isAborted) {
|
||||
op.sendReply(response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
|
||||
sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send standard response.
|
||||
* @param code the response code to send
|
||||
* @param header the headers to include in the response
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void sendResponse(int code, byte[] header) throws IOException {
|
||||
int totalLength = 3;
|
||||
byte[] data = null;
|
||||
OutputStream op = mOutput;
|
||||
if (op == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (header != null) {
|
||||
totalLength += header.length;
|
||||
data = new byte[totalLength];
|
||||
data[0] = (byte)code;
|
||||
data[1] = (byte)(totalLength >> 8);
|
||||
data[2] = (byte)totalLength;
|
||||
System.arraycopy(header, 0, data, 3, header.length);
|
||||
} else {
|
||||
data = new byte[totalLength];
|
||||
data[0] = (byte)code;
|
||||
data[1] = (byte)0x00;
|
||||
data[2] = (byte)totalLength;
|
||||
}
|
||||
op.write(data);
|
||||
op.flush(); // TODO: Do we need to flush?
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a SETPATH request from a client. This method will read the rest
|
||||
* of the request from the client. Assuming the request is valid, it will
|
||||
* create a <code>HeaderSet</code> object to pass to the
|
||||
* <code>ServerRequestHandler</code> object. After the handler processes the
|
||||
* request, this method will create a reply message to send to the server
|
||||
* with the response code provided.
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handleSetPathRequest() throws IOException {
|
||||
int length;
|
||||
int flags;
|
||||
@SuppressWarnings("unused")
|
||||
int constants;
|
||||
int totalLength = 3;
|
||||
byte[] head = null;
|
||||
int code = -1;
|
||||
int bytesReceived;
|
||||
HeaderSet request = new HeaderSet();
|
||||
HeaderSet reply = new HeaderSet();
|
||||
|
||||
length = mInput.read();
|
||||
length = (length << 8) + mInput.read();
|
||||
flags = mInput.read();
|
||||
constants = mInput.read();
|
||||
|
||||
if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
totalLength = 3;
|
||||
} else {
|
||||
if (length > 5) {
|
||||
byte[] headers = new byte[length - 5];
|
||||
bytesReceived = mInput.read(headers);
|
||||
|
||||
while (bytesReceived != headers.length) {
|
||||
bytesReceived += mInput.read(headers, bytesReceived, headers.length
|
||||
- bytesReceived);
|
||||
}
|
||||
|
||||
ObexHelper.updateHeaderSet(request, headers);
|
||||
|
||||
if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
|
||||
mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
|
||||
} else {
|
||||
mListener.setConnectionId(1);
|
||||
}
|
||||
// the Auth chan is initiated by the server, client sent back the authResp .
|
||||
if (request.mAuthResp != null) {
|
||||
if (!handleAuthResp(request.mAuthResp)) {
|
||||
code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
|
||||
mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
|
||||
request.mAuthResp));
|
||||
}
|
||||
request.mAuthResp = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
|
||||
// the Auth challenge is initiated by the client
|
||||
// the server will send back the authResp to the client
|
||||
if (request.mAuthChall != null) {
|
||||
handleAuthChall(request);
|
||||
reply.mAuthResp = new byte[request.mAuthResp.length];
|
||||
System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
|
||||
reply.mAuthResp.length);
|
||||
request.mAuthChall = null;
|
||||
request.mAuthResp = null;
|
||||
}
|
||||
boolean backup = false;
|
||||
boolean create = true;
|
||||
if (!((flags & 1) == 0)) {
|
||||
backup = true;
|
||||
}
|
||||
if (!((flags & 2) == 0)) {
|
||||
create = false;
|
||||
}
|
||||
|
||||
try {
|
||||
code = mListener.onSetPath(request, reply, backup, create);
|
||||
} catch (Exception e) {
|
||||
if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
|
||||
sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
|
||||
return;
|
||||
}
|
||||
|
||||
code = validateResponseCode(code);
|
||||
|
||||
if (reply.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
|
||||
} else {
|
||||
mChallengeDigest = null;
|
||||
}
|
||||
|
||||
long id = mListener.getConnectionId();
|
||||
if (id == -1) {
|
||||
reply.mConnectionID = null;
|
||||
} else {
|
||||
reply.mConnectionID = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
head = ObexHelper.createHeader(reply, false);
|
||||
totalLength += head.length;
|
||||
|
||||
if (totalLength > mMaxPacketLength) {
|
||||
totalLength = 3;
|
||||
head = null;
|
||||
code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Length of OBEX SETPATH packet
|
||||
byte[] replyData = new byte[totalLength];
|
||||
replyData[0] = (byte)code;
|
||||
replyData[1] = (byte)(totalLength >> 8);
|
||||
replyData[2] = (byte)totalLength;
|
||||
if (head != null) {
|
||||
System.arraycopy(head, 0, replyData, 3, head.length);
|
||||
}
|
||||
/*
|
||||
* Write the OBEX SETPATH packet to the server. Byte 0: response code
|
||||
* Byte 1&2: Connect Packet Length Byte 3 to n: headers
|
||||
*/
|
||||
mOutput.write(replyData);
|
||||
mOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a disconnect request from a client. This method will read the
|
||||
* rest of the request from the client. Assuming the request is valid, it
|
||||
* will create a <code>HeaderSet</code> object to pass to the
|
||||
* <code>ServerRequestHandler</code> object. After the handler processes the
|
||||
* request, this method will create a reply message to send to the server.
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handleDisconnectRequest() throws IOException {
|
||||
int length;
|
||||
int code = ResponseCodes.OBEX_HTTP_OK;
|
||||
int totalLength = 3;
|
||||
byte[] head = null;
|
||||
int bytesReceived;
|
||||
HeaderSet request = new HeaderSet();
|
||||
HeaderSet reply = new HeaderSet();
|
||||
|
||||
length = mInput.read();
|
||||
length = (length << 8) + mInput.read();
|
||||
|
||||
if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
totalLength = 3;
|
||||
} else {
|
||||
if (length > 3) {
|
||||
byte[] headers = new byte[length - 3];
|
||||
bytesReceived = mInput.read(headers);
|
||||
|
||||
while (bytesReceived != headers.length) {
|
||||
bytesReceived += mInput.read(headers, bytesReceived, headers.length
|
||||
- bytesReceived);
|
||||
}
|
||||
|
||||
ObexHelper.updateHeaderSet(request, headers);
|
||||
}
|
||||
|
||||
if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
|
||||
mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
|
||||
} else {
|
||||
mListener.setConnectionId(1);
|
||||
}
|
||||
|
||||
if (request.mAuthResp != null) {
|
||||
if (!handleAuthResp(request.mAuthResp)) {
|
||||
code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
|
||||
mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
|
||||
request.mAuthResp));
|
||||
}
|
||||
request.mAuthResp = null;
|
||||
}
|
||||
|
||||
if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
|
||||
|
||||
if (request.mAuthChall != null) {
|
||||
handleAuthChall(request);
|
||||
request.mAuthChall = null;
|
||||
}
|
||||
|
||||
try {
|
||||
mListener.onDisconnect(request, reply);
|
||||
} catch (Exception e) {
|
||||
if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
|
||||
sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
|
||||
return;
|
||||
}
|
||||
|
||||
long id = mListener.getConnectionId();
|
||||
if (id == -1) {
|
||||
reply.mConnectionID = null;
|
||||
} else {
|
||||
reply.mConnectionID = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
head = ObexHelper.createHeader(reply, false);
|
||||
totalLength += head.length;
|
||||
|
||||
if (totalLength > mMaxPacketLength) {
|
||||
totalLength = 3;
|
||||
head = null;
|
||||
code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Length of OBEX CONNECT packet
|
||||
byte[] replyData;
|
||||
if (head != null) {
|
||||
replyData = new byte[3 + head.length];
|
||||
} else {
|
||||
replyData = new byte[3];
|
||||
}
|
||||
replyData[0] = (byte)code;
|
||||
replyData[1] = (byte)(totalLength >> 8);
|
||||
replyData[2] = (byte)totalLength;
|
||||
if (head != null) {
|
||||
System.arraycopy(head, 0, replyData, 3, head.length);
|
||||
}
|
||||
/*
|
||||
* Write the OBEX DISCONNECT packet to the server. Byte 0: response code
|
||||
* Byte 1&2: Connect Packet Length Byte 3 to n: headers
|
||||
*/
|
||||
mOutput.write(replyData);
|
||||
mOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a connect request from a client. This method will read the rest
|
||||
* of the request from the client. Assuming the request is valid, it will
|
||||
* create a <code>HeaderSet</code> object to pass to the
|
||||
* <code>ServerRequestHandler</code> object. After the handler processes the
|
||||
* request, this method will create a reply message to send to the server
|
||||
* with the response code provided.
|
||||
* @throws IOException if an error occurred at the transport layer
|
||||
*/
|
||||
private void handleConnectRequest() throws IOException {
|
||||
int packetLength;
|
||||
@SuppressWarnings("unused")
|
||||
int version;
|
||||
@SuppressWarnings("unused")
|
||||
int flags;
|
||||
int totalLength = 7;
|
||||
byte[] head = null;
|
||||
int code = -1;
|
||||
HeaderSet request = new HeaderSet();
|
||||
HeaderSet reply = new HeaderSet();
|
||||
int bytesReceived;
|
||||
|
||||
if(V) Log.v(TAG,"handleConnectRequest()");
|
||||
|
||||
/*
|
||||
* Read in the length of the OBEX packet, OBEX version, flags, and max
|
||||
* packet length
|
||||
*/
|
||||
packetLength = mInput.read();
|
||||
packetLength = (packetLength << 8) + mInput.read();
|
||||
if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
|
||||
|
||||
version = mInput.read();
|
||||
flags = mInput.read();
|
||||
mMaxPacketLength = mInput.read();
|
||||
mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
|
||||
|
||||
if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
|
||||
+ " MaxLength: " + mMaxPacketLength + " flags: " + flags);
|
||||
|
||||
// should we check it?
|
||||
if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
|
||||
mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
|
||||
}
|
||||
|
||||
if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
|
||||
Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
|
||||
+ " is larger than the max size supported by the transport: "
|
||||
+ ObexHelper.getMaxTxPacketSize(mTransport)
|
||||
+ " Reducing to this size.");
|
||||
mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
|
||||
}
|
||||
|
||||
if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
|
||||
code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
|
||||
totalLength = 7;
|
||||
} else {
|
||||
if (packetLength > 7) {
|
||||
byte[] headers = new byte[packetLength - 7];
|
||||
bytesReceived = mInput.read(headers);
|
||||
|
||||
while (bytesReceived != headers.length) {
|
||||
bytesReceived += mInput.read(headers, bytesReceived, headers.length
|
||||
- bytesReceived);
|
||||
}
|
||||
|
||||
ObexHelper.updateHeaderSet(request, headers);
|
||||
}
|
||||
|
||||
if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
|
||||
mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
|
||||
} else {
|
||||
mListener.setConnectionId(1);
|
||||
}
|
||||
|
||||
if (request.mAuthResp != null) {
|
||||
if (!handleAuthResp(request.mAuthResp)) {
|
||||
code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
|
||||
mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
|
||||
request.mAuthResp));
|
||||
}
|
||||
request.mAuthResp = null;
|
||||
}
|
||||
|
||||
if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
|
||||
if (request.mAuthChall != null) {
|
||||
handleAuthChall(request);
|
||||
reply.mAuthResp = new byte[request.mAuthResp.length];
|
||||
System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
|
||||
reply.mAuthResp.length);
|
||||
request.mAuthChall = null;
|
||||
request.mAuthResp = null;
|
||||
}
|
||||
|
||||
try {
|
||||
code = mListener.onConnect(request, reply);
|
||||
code = validateResponseCode(code);
|
||||
|
||||
if (reply.nonce != null) {
|
||||
mChallengeDigest = new byte[16];
|
||||
System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
|
||||
} else {
|
||||
mChallengeDigest = null;
|
||||
}
|
||||
long id = mListener.getConnectionId();
|
||||
if (id == -1) {
|
||||
reply.mConnectionID = null;
|
||||
} else {
|
||||
reply.mConnectionID = ObexHelper.convertToByteArray(id);
|
||||
}
|
||||
|
||||
head = ObexHelper.createHeader(reply, false);
|
||||
totalLength += head.length;
|
||||
|
||||
if (totalLength > mMaxPacketLength) {
|
||||
totalLength = 7;
|
||||
head = null;
|
||||
code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
|
||||
totalLength = 7;
|
||||
head = null;
|
||||
code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Length of OBEX CONNECT packet
|
||||
byte[] length = ObexHelper.convertToByteArray(totalLength);
|
||||
|
||||
/*
|
||||
* Write the OBEX CONNECT packet to the server. Byte 0: response code
|
||||
* Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
|
||||
* (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
|
||||
* Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
|
||||
*/
|
||||
byte[] sendData = new byte[totalLength];
|
||||
int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
|
||||
if (maxRxLength > mMaxPacketLength) {
|
||||
if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength +
|
||||
" and MaxNegotiated from Client: " + mMaxPacketLength);
|
||||
maxRxLength = mMaxPacketLength;
|
||||
}
|
||||
sendData[0] = (byte)code;
|
||||
sendData[1] = length[2];
|
||||
sendData[2] = length[3];
|
||||
sendData[3] = (byte)0x10;
|
||||
sendData[4] = (byte)0x00;
|
||||
sendData[5] = (byte)(maxRxLength >> 8);
|
||||
sendData[6] = (byte)(maxRxLength & 0xFF);
|
||||
|
||||
if (head != null) {
|
||||
System.arraycopy(head, 0, sendData, 7, head.length);
|
||||
}
|
||||
|
||||
mOutput.write(sendData);
|
||||
mOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the server session - in detail close I/O streams and the
|
||||
* underlying transport layer. Internal flag is also set so that later
|
||||
* attempt to read/write will throw an exception.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (mListener != null) {
|
||||
mListener.onClose();
|
||||
}
|
||||
try {
|
||||
/* Set state to closed before interrupting the thread by closing the streams */
|
||||
mClosed = true;
|
||||
if(mInput != null)
|
||||
mInput.close();
|
||||
if(mOutput != null)
|
||||
mOutput.close();
|
||||
if(mTransport != null)
|
||||
mTransport.close();
|
||||
} catch (Exception e) {
|
||||
if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
|
||||
}
|
||||
mTransport = null;
|
||||
mInput = null;
|
||||
mOutput = null;
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the response code is valid. If it is not valid, it will
|
||||
* return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
|
||||
* @param code the response code to check
|
||||
* @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
|
||||
* if <code>code</code> is not valid
|
||||
*/
|
||||
private int validateResponseCode(int code) {
|
||||
|
||||
if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
|
||||
return code;
|
||||
}
|
||||
if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
|
||||
&& (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
|
||||
return code;
|
||||
}
|
||||
if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
|
||||
&& (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
|
||||
return code;
|
||||
}
|
||||
if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
|
||||
&& (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
|
||||
return code;
|
||||
}
|
||||
if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
|
||||
&& (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
|
||||
return code;
|
||||
}
|
||||
return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
public ObexTransport getTransport() {
|
||||
return mTransport;
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Motorola, Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package javax.obex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The <code>SessionNotifier</code> interface defines a connection notifier for
|
||||
* server-side OBEX connections. When a <code>SessionNotifier</code> is created
|
||||
* and calls <code>acceptAndOpen()</code>, it will begin listening for clients
|
||||
* to create a connection at the transport layer. When the transport layer
|
||||
* connection is received, the <code>acceptAndOpen()</code> method will return a
|
||||
* <code>javax.microedition.io.Connection</code> that is the connection to the
|
||||
* client. The <code>acceptAndOpen()</code> method also takes a
|
||||
* <code>ServerRequestHandler</code> argument that will process the requests
|
||||
* from the client that connects to the server.
|
||||
* @hide
|
||||
*/
|
||||
public interface SessionNotifier {
|
||||
|
||||
/**
|
||||
* Waits for a transport layer connection to be established and specifies
|
||||
* the handler to handle the requests from the client. No authenticator is
|
||||
* associated with this connection, therefore, it is implementation
|
||||
* dependent as to how an authentication challenge and authentication
|
||||
* response header will be received and processed.
|
||||
* <P>
|
||||
* <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
|
||||
* on a <code>SessionNotifier</code> object that does not have a
|
||||
* <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
|
||||
* for this object will be added to the SDDB. This method requests the BCC
|
||||
* to put the local device in connectable mode so that it will respond to
|
||||
* connection attempts by clients.
|
||||
* <P>
|
||||
* The following checks are done to verify that the service record provided
|
||||
* is valid. If any of these checks fail, then a
|
||||
* <code>ServiceRegistrationException</code> is thrown.
|
||||
* <UL>
|
||||
* <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
|
||||
* attributes for a <code>btgoep</code> service record, must be present in
|
||||
* the <code>ServiceRecord</code> associated with this notifier.
|
||||
* <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
|
||||
* <LI>The <code>ServiceRecord</code> associated with this notifier must not
|
||||
* have changed the RFCOMM server channel number
|
||||
* </UL>
|
||||
* <P>
|
||||
* This method will not ensure that <code>ServiceRecord</code> associated
|
||||
* with this notifier is a completely valid service record. It is the
|
||||
* responsibility of the application to ensure that the service record
|
||||
* follows all of the applicable syntactic and semantic rules for service
|
||||
* record correctness.
|
||||
* @param handler the request handler that will respond to OBEX requests
|
||||
* @return the connection to the client
|
||||
* @throws IOException if an error occurs in the transport layer
|
||||
* @throws NullPointerException if <code>handler</code> is <code>null</code>
|
||||
*/
|
||||
ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
|
||||
|
||||
/**
|
||||
* Waits for a transport layer connection to be established and specifies
|
||||
* the handler to handle the requests from the client and the
|
||||
* <code>Authenticator</code> to use to respond to authentication challenge
|
||||
* and authentication response headers.
|
||||
* <P>
|
||||
* <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
|
||||
* on a <code>SessionNotifier</code> object that does not have a
|
||||
* <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
|
||||
* for this object will be added to the SDDB. This method requests the BCC
|
||||
* to put the local device in connectable mode so that it will respond to
|
||||
* connection attempts by clients.
|
||||
* <P>
|
||||
* The following checks are done to verify that the service record provided
|
||||
* is valid. If any of these checks fail, then a
|
||||
* <code>ServiceRegistrationException</code> is thrown.
|
||||
* <UL>
|
||||
* <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
|
||||
* attributes for a <code>btgoep</code> service record, must be present in
|
||||
* the <code>ServiceRecord</code> associated with this notifier.
|
||||
* <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
|
||||
* <LI>The <code>ServiceRecord</code> associated with this notifier must not
|
||||
* have changed the RFCOMM server channel number
|
||||
* </UL>
|
||||
* <P>
|
||||
* This method will not ensure that <code>ServiceRecord</code> associated
|
||||
* with this notifier is a completely valid service record. It is the
|
||||
* responsibility of the application to ensure that the service record
|
||||
* follows all of the applicable syntactic and semantic rules for service
|
||||
* record correctness.
|
||||
* @param handler the request handler that will respond to OBEX requests
|
||||
* @param auth the <code>Authenticator</code> to use with this connection;
|
||||
* if <code>null</code> then no <code>Authenticator</code> will be
|
||||
* used
|
||||
* @return the connection to the client
|
||||
* @throws IOException if an error occurs in the transport layer
|
||||
* @throws NullPointerException if <code>handler</code> is <code>null</code>
|
||||
*/
|
||||
ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth) throws IOException;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user