📄 irobex.java
字号:
/*
**************************************************************************
** $Header: /cvsroot/jred/jred/src/com/synchrona/jred/IrOBEX.java,v 1.2 2000/07/29 20:29:29 mpatters Exp $
**
** Copyright (C) 2000 Synchrona, Inc. All rights reserved.
**
** This file is part of JRed, a 100% Java implementation of the IrDA
** infrared communications protocols.
**
** This file may be distributed under the terms of the Synchrona Public
** License as defined by Synchrona, Inc. and appearing in the file
** LICENSE included in the packaging of this file. The Synchrona Public
** License is based on the Q Public License as defined by Troll Tech AS
** of Norway; it differs only in its use of the courts of Florida, USA
** rather than those of Oslo, Norway.
**************************************************************************
*/
package com.synchrona.jred;
import com.synchrona.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* Implements the Infrared Data Association (IrDA) Object Exchange (OBEX)
* protocol.
*/
public class IrOBEX {
public static final byte IROBEX_FLAGS = (byte) 0x00;
public static final byte IROBEX_VERSION = (byte) 0x10;
public static final byte OPCODE_CONNECT_REQUEST = (byte) 0x00;
public static final byte OPCODE_DISCONNECT_REQUEST = (byte) 0x01;
public static final byte OPCODE_PUT_REQUEST = (byte) 0x02;
public static final int STATE_READY = 37;
public static final int STATE_RECEIVING = 51;
private byte [] m_ayPacket = null;
private boolean m_bDone = false;
private File m_inbox = null;
private Log m_log = null;
private int m_nPacketLength = 0;
private int m_nState = STATE_READY;
private ByteArrayOutputStream m_outStream = null;
private Hashtable m_params = new Hashtable();
public IrOBEX(Log log) {
m_log = log;
// We hope the user defined an inbox directory. If not, we'll
// try to use the current directory. If that doesn't work, we
// won't be able to save anything.
String strInbox = System.getProperty("IrOBEX.InboxDirectory");
if ( null == strInbox ) {
m_log.warn("IrOBEX", "IrOBEX.InboxDirectory was not defined, using ./inbox instead.");
strInbox = "." + File.separator + "inbox";
}
m_inbox = new File(strInbox);
boolean bUseCurrent = false;
if ( m_inbox.exists() ) {
if ( !m_inbox.isDirectory() ) {
m_log.error("IrOBEX", "IrOBEX.InboxDirectory (" + strInbox + ") exists but is not a directory...using current instead.");
bUseCurrent = true;
}
} else {
// If mkdir() fails (most likely because of permissions), we'll
// have to use the current directory.
m_log.warn("IrOBEX", "Creating a new inbox directory: \"" + strInbox + "\"");
bUseCurrent = !m_inbox.mkdir();
if ( bUseCurrent ) {
m_log.error("IrOBEX", "Could not create IrOBEX.InboxDirectory (" + strInbox + ")...using current instead.");
} else if ( !m_inbox.canWrite() ) {
m_log.error("IrOBEX", "Cannot write to IrOBEX.InboxDirectory (" + strInbox + ")...using current instead.");
}
}
if ( bUseCurrent ) {
m_inbox = new File(".");
if ( m_inbox.canWrite() ) {
// If we weren't able to write to the current directory,
// we just can't save anymore.
m_log.error("IrOBEX", "Cannot write to current directory. IrOBEX will not be able to save any beamed objects.");
}
}
}
public void serviceRequest(TinyTP tinyTP, IrLMPConnection conn, byte [] ayData, int nOffset, int nLength) throws Exception {
if ( STATE_READY == m_nState ) {
m_outStream = new ByteArrayOutputStream();
m_nPacketLength = (0x0000FF00 & (ayData[nOffset + 1] << 8)) | (0x000000FF & ayData[nOffset + 2]);
m_log.debug("IrOBEX", "(serviceRequest) starting new packet: opcode=" + ayData[nOffset] + " length=" + m_nPacketLength);
} else {
m_log.debug("IrOBEX",
"(serviceRequest) appending to current packet / length: " + nLength);
}
m_outStream.write(ayData, nOffset, nLength);
m_bDone = (m_nPacketLength == m_outStream.size());
m_log.debug("IrOBEX", "m_bDone: " + m_bDone);
if ( m_bDone ) {
m_log.debug("IrOBEX", "(serviceRequest) packet complete " + m_outStream.size());
m_nState = STATE_READY;
m_ayPacket = m_outStream.toByteArray();
processPacket(tinyTP);
} else {
m_nState = STATE_RECEIVING;
}
}
/**
* Save a beamed object as a file in the inbox directory.
*/
private void doPut() {
m_log.debug("IrOBEX", "(doPut)");
String strName = (String) m_params.get("Name");
m_log.debug("IrOBEX", "\tName: " + strName);
String strDescription = (String) m_params.get("Description");
m_log.debug("IrOBEX", "\tDescription: " + strDescription);
ByteArrayOutputStream body = (ByteArrayOutputStream) m_params.get("Body");
m_log.debug("IrOBEX", "\tBody: " + body.toString());
//
// Object names can contain characters with special meanings to the
// receiver. For instance, Palm application names can have '/' characters
// in them, which Unix platforms use to denote directories. We need to
// replace path separators with something, and here we use '_' characters.
//
String newName = strName.replace(File.separatorChar, '_');
File beamedObject = new File(m_inbox.getName() + File.separator + newName);
m_log.debug("IrOBEX", "\tsaving to " + beamedObject.getName());
try {
FileOutputStream out = new FileOutputStream(beamedObject);
body.writeTo(out);
out.close();
} catch ( IOException ioException ) {
m_log.error("IrOBEX", ioException.getMessage());
}
}
private void processPacket(TinyTP tinyTP) {
m_log.debug("IrOBEX", "(processPacket)");
boolean bFinal = (0 != (m_ayPacket[0] & 0x80));
byte yOpcode = (byte) (m_ayPacket[0] & 0x7F);
switch ( yOpcode ) {
case OPCODE_CONNECT_REQUEST:
handleConnectRequest(tinyTP, bFinal);
break;
case OPCODE_DISCONNECT_REQUEST:
handleDisconnectRequest(tinyTP, bFinal);
break;
case OPCODE_PUT_REQUEST:
handlePutRequest(tinyTP, bFinal);
break;
default:
m_log.warn("IrOBEX", "Unimplemented opcode: " + yOpcode);
break;
}
}
private void handleConnectRequest(TinyTP tinyTP, boolean bFinal) {
m_log.debug("IrOBEX", "Connect-Request");
int nPacketLength = (m_ayPacket[1] << 8) | m_ayPacket[2];
byte yVersion = m_ayPacket[3];
byte yFlags = m_ayPacket[4];
int nMaxPacketLength = (m_ayPacket[5] << 8) | m_ayPacket[6];
byte [] ayResult = new byte[255];
int nLength = 0;
ayResult[nLength++] = (byte) 0xA0; // 0: success
nLength += 2; // 1 & 2: leave space for packet length
ayResult[nLength++] = (byte) IROBEX_VERSION; // 3: IrOBEX version
ayResult[nLength++] = (byte) IROBEX_FLAGS; // 4: flags
// bytes 5 & 6: maximum packet length
ayResult[nLength++] = (byte) ((nMaxPacketLength & 0x0000FF00) >> 8);
ayResult[nLength++] = (byte) (nMaxPacketLength & 0x000000FF);
ayResult[1] = (byte) ((nLength & 0x0000FF00) >> 8);
ayResult[2] = (byte) (nLength & 0x0000000FF);
tinyTP.dataRequest(ayResult, 0, nLength);
}
private void handleDisconnectRequest(TinyTP tinyTP, boolean bFinal) {
m_log.debug("IrOBEX", "Disconnect-Request");
byte [] ayResponse = new byte[3];
int nResponseLength = 0;
ayResponse[nResponseLength++] = (byte) 0xA0;
ayResponse[nResponseLength++] = (byte) 0x00;
ayResponse[nResponseLength++] = (byte) 0x03;
tinyTP.dataRequest(ayResponse, 0, nResponseLength);
}
private void handlePutRequest(TinyTP tinyTP, boolean bFinal) {
m_log.debug("IrOBEX", "Put-Request packet-length=" + m_nPacketLength);
try {
parseHeaders();
if ( bFinal ) {
doPut();
m_params.clear();
}
} catch ( Exception e ) {
m_log.error("IrOBEX", e.toString());
}
byte [] ayResponse = new byte[3];
int nResponseLength = 0;
ayResponse[nResponseLength++] = (byte) (bFinal ? 0xA0 : 0x90);
ayResponse[nResponseLength++] = (byte) 0x00;
ayResponse[nResponseLength++] = (byte) 0x03;
tinyTP.dataRequest(ayResponse, 0, nResponseLength);
}
private void parseHeaders() throws Exception {
int nLength = 0;
DataInputStream data = new DataInputStream(new ByteArrayInputStream(m_ayPacket));
// Packets begin with 1-byte opcode, but we already know it's
// a PUT request (although we should assert that here)
data.readByte();
// Next is a 16-bit packet length
short nPacketLength = data.readShort();
m_log.debug("IrOBEX", "(parseHeaders) nPacketLength: " + nPacketLength);
while ( data.available() > 0 ) {
// Figure out datatype
String strName = null;
Object value = null;
byte [] ayBody = null;
boolean bBody = false; // reinitialize each time through
// Each header begins with a 1-byte type.
int yHeader = data.readByte();
m_log.debug("IrOBEX", "yHeader=" + yHeader);
switch ( (byte) (0x000000FF & yHeader) ) {
case (byte) 0xC0: strName = "Count"; break;
case (byte) 0x01: strName = "Name"; break;
case (byte) 0x42: strName = "Type"; break;
case (byte) 0xC3: strName = "Length"; break;
case (byte) 0x44: strName = "Time"; break;
case (byte) 0x05: strName = "Description"; break;
case (byte) 0x46: strName = "Target"; break;
case (byte) 0x47: strName = "HTTP"; break;
// See below for special handling of Body & End-Of-Body
// chunks.
case (byte) 0x48: // fall through
case (byte) 0x49:
strName = "Body";
bBody = true;
break;
case (byte) 0x4A: strName = "Who"; break;
case (byte) 0xCB: strName = "Connection-ID"; break;
case (byte) 0x4C: strName = "App-Parameters"; break;
case (byte) 0x4D: strName = "Challenge"; break;
case (byte) 0x4E: strName = "Response"; break;
case (byte) 0x4F: strName = "Object-Class"; break;
// Not part of the IrOBEX specification, and outside
// the range of ID's that are supposed to be recognized
// by IrOBEX services. It's a 4-byte ASCII string disguised
// as an int. Is this considered intellectual property
// of Palm Computing? If so, we need to ignore it.
case (byte) 0xCF: strName = "Palm-Creator-ID"; break;
default:
strName = "Unknown (" + yHeader + ")";
break;
}
m_log.debug("IrOBEX", strName);
switch ( 0x000000C0 & yHeader ) {
case 0x00000000:
// Unicode text, null terminated, prefixed by 2-byte length
// Length includes header byte and 2-byte length, so we need
// to subtract 3 before continuing
nLength = data.readShort() - 3;
m_log.debug("IrOBEX", "reading string length=" + nLength);
// Java uses modified UTF-8, while OBEX specifies
// UTF-16. We can convert by reading 16-bit characters
// and constructing a string from them.
char [] acText = new char[nLength / 2];
for ( int i = 0; i < acText.length; i++ ) {
acText[i] = data.readChar();
}
// Convert to string, minus the trailing null
String strValue = new String(acText, 0, acText.length - 1);
m_log.debug("IrOBEX", "strValue: #" + strValue + "#");
value = strValue;
break;
case 0x00000040:
// Byte sequence prefaced by 2-byte length
// Length includes header byte and 2-byte length, so we need
// to subtract 3 before continuing
nLength = data.readShort() - 3;
m_log.debug("IrOBEX", "reading binary sequence length=" + nLength);
ayBody = new byte[nLength];
data.read(ayBody);
// If this sequence is a Body or End-Of-Body chunk,
// it will be contenated into the preceding body
// segments below.
String strBody = new String(ayBody);
m_log.debug("IrOBEX", "body: #" + strBody + "#");
break;
case 0x00000080: // 1 byte
m_log.debug("IrOBEX", "reading byte");
value = new Byte(data.readByte());
break;
case 0x000000C0: // 4-byte integer
m_log.debug("IrOBEX", "reading int");
value = new Integer(data.readInt());
break;
default:
throw new Exception("Unhandled datatype: " + yHeader);
}
m_log.debug("IrOBEX", "(parseHeaders) " + strName);
// We need to handle Body and End-Of-Body chunks differently,
// because they need to be concatenated into a single stream
// of bytes.
if ( bBody ) {
m_log.debug("IrOBEX", "bBody was true");
if ( (null != ayBody) && (ayBody.length > 0 ) ) {
ByteArrayOutputStream body =
(ByteArrayOutputStream) m_params.get("Body");
if ( null == body ) {
m_log.debug("IrOBEX", "allocating new ByteArrayOutputStream");
body = new ByteArrayOutputStream();
}
body.write(ayBody, 0, ayBody.length);
m_log.debug("IrOBEX", "putting body into hash table");
m_params.put("Body", body);
}
} else {
m_log.debug("IrOBEX", "putting key [" + strName + "] value [" + value + "]");
m_params.put(strName, value);
}
}
}
private void connectRequest(TinyTP tinyTP) {
byte [] ayRequest = new byte[255];
int nRequestLength = 0;
ayRequest[nRequestLength++] = (byte) 0x80; // 0: opcode
nRequestLength += 2; // 1 & 2: leave space for packet length
ayRequest[nRequestLength++] = (byte) IROBEX_VERSION; // 3: IrOBEX version
ayRequest[nRequestLength++] = (byte) IROBEX_FLAGS; // 4: flags
// bytes 5 & 6: maximum packet length
ayRequest[nRequestLength++] = (byte) 0xFF;
ayRequest[nRequestLength++] = (byte) 0xFF;
ayRequest[1] = (byte) ((nRequestLength & 0x0000FF00) >> 8);
ayRequest[2] = (byte) (nRequestLength & 0x0000000FF);
tinyTP.dataRequest(ayRequest, 0, nRequestLength);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -