com.intel.bluetooth.obex.OBEXClientOperation Maven / Gradle / Ivy
/**
* BlueCove - Java library for Bluetooth
* Copyright (C) 2007-2008 Vlad Skarzhevskyy
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @version $Id: OBEXClientOperation.java 2324 2008-07-04 04:33:58Z skarzhevskyy $
*/
package com.intel.bluetooth.obex;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import com.intel.bluetooth.DebugLog;
abstract class OBEXClientOperation implements Operation, OBEXOperation {
/**
* This is not 100% by JSR-82 doc. But some know implementations of OBEX are
* working this way. This solves the problems for Samsung phones that are
* sending nothing in response to GET request without final bit.
*
* Basically instead of sending at least two packets 'initial' and 'final'
* we are sending just 'final' one when applicable.
*/
final static boolean SHORT_REQUEST_PHASE = true;
protected OBEXClientSessionImpl session;
protected char operationId;
protected HeaderSet replyHeaders;
protected boolean isClosed;
protected boolean operationInProgress;
protected OBEXOperationOutputStream outputStream;
protected boolean outputStreamOpened = false;
protected OBEXOperationInputStream inputStream;
protected boolean inputStreamOpened = false;
protected boolean errorReceived = false;
protected boolean requestEnded = false;
protected boolean finalBodyReceived = false;
protected HeaderSet startOperationHeaders = null;
protected Object lock;
OBEXClientOperation(OBEXClientSessionImpl session, char operationId) throws IOException {
this.session = session;
this.operationId = operationId;
this.isClosed = false;
this.operationInProgress = false;
this.lock = new Object();
}
static boolean isShortRequestPhase() {
return SHORT_REQUEST_PHASE;
}
protected void startOperation(HeaderSet sendHeaders) throws IOException {
if (SHORT_REQUEST_PHASE) {
this.startOperationHeaders = sendHeaders;
} else {
this.operationInProgress = true;
exchangePacket(OBEXHeaderSetImpl.toByteArray(sendHeaders));
}
}
protected void endRequestPhase() throws IOException {
if (requestEnded) {
return;
}
DebugLog.debug("client end Request Phase");
this.operationInProgress = false;
this.requestEnded = true;
this.operationId |= OBEXOperationCodes.FINAL_BIT;
if (SHORT_REQUEST_PHASE) {
exchangePacket(OBEXHeaderSetImpl.toByteArray(this.startOperationHeaders));
this.startOperationHeaders = null;
} else {
exchangePacket(null);
}
}
protected void exchangePacket(byte[] data) throws IOException {
boolean success = false;
try {
session.writeOperation(this.operationId, data);
byte[] b = session.readOperation();
HeaderSet dataHeaders = OBEXHeaderSetImpl.readHeaders(b[0], b, 3);
int responseCode = dataHeaders.getResponseCode();
DebugLog.debug0x("client operation got reply", OBEXUtils.toStringObexResponseCodes(responseCode),
responseCode);
switch (responseCode) {
case OBEXOperationCodes.OBEX_RESPONSE_SUCCESS:
processIncommingHeaders(dataHeaders);
processIncommingData(dataHeaders, true);
this.operationInProgress = false;
break;
case OBEXOperationCodes.OBEX_RESPONSE_CONTINUE:
processIncommingHeaders(dataHeaders);
processIncommingData(dataHeaders, false);
break;
default:
errorReceived = true;
// responseCode may be reported by getResponseCode()
processIncommingHeaders(dataHeaders);
if ((this.operationId & OBEXOperationCodes.FINAL_BIT) == 0) {
throw new IOException("Operation error, 0x" + Integer.toHexString(responseCode) + " "
+ OBEXUtils.toStringObexResponseCodes(responseCode));
}
}
success = true;
} finally {
if (!success) {
errorReceived = true;
}
}
}
protected void processIncommingHeaders(HeaderSet dataHeaders) throws IOException {
if (replyHeaders != null) {
OBEXHeaderSetImpl.appendHeaders(dataHeaders, replyHeaders);
}
// replyHeaders will contain responseCode from last reply
this.replyHeaders = dataHeaders;
}
protected void processIncommingData(HeaderSet dataHeaders, boolean eof) throws IOException {
byte[] data = (byte[]) dataHeaders.getHeader(OBEXHeaderSetImpl.OBEX_HDR_BODY);
if (data == null) {
data = (byte[]) dataHeaders.getHeader(OBEXHeaderSetImpl.OBEX_HDR_BODY_END);
if (data != null) {
finalBodyReceived = true;
eof = true;
}
}
if (data != null) {
DebugLog.debug("client received Data eof: " + eof + " len: ", data.length);
inputStream.appendData(data, eof);
} else if (eof) {
inputStream.appendData(null, eof);
}
}
/*
* (non-Javadoc)
*
* @see javax.obex.Operation#abort()
*/
public void abort() throws IOException {
validateOperationIsOpen();
if (!this.operationInProgress) {
throw new IOException("the transaction has already ended");
}
synchronized (lock) {
if (outputStream != null) {
outputStream.abort();
}
this.inputStream.close();
}
writeAbort();
}
protected void writeAbort() throws IOException {
try {
session.writeOperation(OBEXOperationCodes.ABORT, null);
byte[] b = session.readOperation();
HeaderSet dataHeaders = OBEXHeaderSetImpl.readHeaders(b[0], b, 3);
if (dataHeaders.getResponseCode() != OBEXOperationCodes.OBEX_RESPONSE_SUCCESS) {
throw new IOException("Fails to abort operation");
}
} finally {
this.isClosed = true;
closeStream();
}
}
abstract void closeStream() throws IOException;
protected void validateOperationIsOpen() throws IOException {
if (isClosed) {
throw new IOException("operation closed");
}
}
/*
* (non-Javadoc)
*
* @see javax.obex.Operation#getReceivedHeaders()
*/
public HeaderSet getReceivedHeaders() throws IOException {
validateOperationIsOpen();
endRequestPhase();
return OBEXHeaderSetImpl.cloneHeaders(this.replyHeaders);
}
/*
* (non-Javadoc)
*
* @see javax.obex.Operation#getResponseCode()
*
* A call will do an implicit close on the Stream and therefore signal that
* the request is done.
*/
public int getResponseCode() throws IOException {
validateOperationIsOpen();
endRequestPhase();
closeStream();
return this.replyHeaders.getResponseCode();
}
public void sendHeaders(HeaderSet headers) throws IOException {
if (headers == null) {
throw new NullPointerException("headers are null");
}
OBEXHeaderSetImpl.validateCreatedHeaderSet(headers);
validateOperationIsOpen();
if (this.requestEnded) {
throw new IOException("the request phase has already ended");
}
if (SHORT_REQUEST_PHASE && (this.startOperationHeaders != null)) {
exchangePacket(OBEXHeaderSetImpl.toByteArray(this.startOperationHeaders));
this.startOperationHeaders = null;
}
exchangePacket(OBEXHeaderSetImpl.toByteArray(headers));
}
/*
* (non-Javadoc)
*
* @see javax.microedition.io.ContentConnection#getEncoding() getEncoding()
* will always return null
*/
public String getEncoding() {
return null;
}
/*
* (non-Javadoc)
*
* @see javax.microedition.io.ContentConnection#getLength() getLength()
* will return the length specified by the OBEX Length header or -1 if
* the OBEX Length header was not included.
*/
public long getLength() {
Long len;
try {
len = (Long) replyHeaders.getHeader(HeaderSet.LENGTH);
} catch (IOException e) {
return -1;
}
if (len == null) {
return -1;
}
return len.longValue();
}
/*
* (non-Javadoc)
*
* @see javax.microedition.io.ContentConnection#getType() getType()
* will return the value specified in the OBEX Type header or null
* if the OBEX Type header was not included.
*/
public String getType() {
try {
return (String) replyHeaders.getHeader(HeaderSet.TYPE);
} catch (IOException e) {
return null;
}
}
public DataInputStream openDataInputStream() throws IOException {
return new DataInputStream(openInputStream());
}
public DataOutputStream openDataOutputStream() throws IOException {
return new DataOutputStream(openOutputStream());
}
/*
* (non-Javadoc)
*
* @see javax.microedition.io.Connection#close()
*/
public void close() throws IOException {
try {
endRequestPhase();
} finally {
closeStream();
if (!this.isClosed) {
this.isClosed = true;
DebugLog.debug("client operation closed");
}
}
}
public boolean isClosed() {
return this.isClosed || this.errorReceived;
}
}