diff options
Diffstat (limited to 'javax/obex/HeaderSet.java')
-rw-r--r-- | javax/obex/HeaderSet.java | 728 |
1 files changed, 728 insertions, 0 deletions
diff --git a/javax/obex/HeaderSet.java b/javax/obex/HeaderSet.java new file mode 100644 index 0000000..8ce8d5f --- /dev/null +++ b/javax/obex/HeaderSet.java @@ -0,0 +1,728 @@ +/* + * 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.security.SecureRandom; +import java.util.Calendar; + +/** + * This class implements the javax.obex.HeaderSet interface for OBEX over + * RFCOMM or OBEX over l2cap. + */ +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). + * + * @hide + */ + public static final int BODY = 0x48; + + /** + * Represents the OBEX End of BODY header. + * <P> + * The value of <code>BODY</code> is 0x49 (73). + * + * @hide + */ + 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). + * + * @hide + */ + 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). + * + * @hide + */ + public static final int AUTH_CHALLENGE = 0x4D; + + /** + * Represents the OBEX authentication digest-response. + * <P> + * The value of <code>AUTH_RESPONSE</code> is 0x4E (78). + * + * @hide + */ + 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). + * + * @hide + */ + 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). + * + * @hide + */ + 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. + */ + 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. + * + * @hide + */ + 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 + * + * @hide + */ + 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 + * + * @hide + */ + 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; + } + } +} |