Source code: com/synchrona/jred/IrLMP.java
1 /*
2 **************************************************************************
3 ** $Header: /cvsroot/jred/jred/src/com/synchrona/jred/IrLMP.java,v 1.5 2000/07/30 20:18:12 mpatters Exp $
4 **
5 ** Copyright (C) 2000 Synchrona, Inc. All rights reserved.
6 **
7 ** This file is part of JRed, a 100% Java implementation of the IrDA
8 ** infrared communications protocols.
9 **
10 ** This file may be distributed under the terms of the Synchrona Public
11 ** License as defined by Synchrona, Inc. and appearing in the file
12 ** LICENSE included in the packaging of this file. The Synchrona Public
13 ** License is based on the Q Public License as defined by Troll Tech AS
14 ** of Norway; it differs only in its use of the courts of Florida, USA
15 ** rather than those of Oslo, Norway.
16 **************************************************************************
17 */
18 package com.synchrona.jred;
19
20 import com.synchrona.jred.iIrLMPService;
21 import com.synchrona.jred.IrIAS;
22 import com.synchrona.jred.IrLMPConnection;
23 import com.synchrona.jred.irlap.ConnectionInformation;
24 import com.synchrona.jred.irlap.DiscoveryInformation;
25 import com.synchrona.jred.irlap.iIrLAPListener;
26 import com.synchrona.jred.irlap.IrLAPContext;
27 import com.synchrona.util.Assert;
28 import com.synchrona.util.Log;
29 import com.synchrona.util.Utilities;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.util.Vector;
33 import javax.swing.Timer;
34
35 /**
36 ** Implement link management as defined in the IrLMP specification.
37 */
38 public class IrLMP implements iIrLAPListener {
39
40 public static final int DEFAULT_AUTO_DISCOVERY_INTERVAL = 300;
41 public static final String AUTO_DISCOVERY_PROPERTY = "IrLMP.autoDiscovery";
42 public static final String AUTO_DISCOVERY_INTERVAL_PROPERTY = "IrLMP.autoDiscoveryInterval";
43
44 private static final byte OPCODE_CONNECT_REQUEST = (byte) 0x01;
45 private static final byte OPCODE_CONNECT_CONFIRM = (byte) 0x81;
46
47 private int _autoDiscoveryInterval;
48 private Timer _autoDiscoveryTimer;
49 private byte _irlapConnection;
50 private IrLAPContext _irlap;
51 private boolean _doAutoDiscovery;
52 private Log _log;
53 private Vector _services;
54
55 //----------------------------------------------------------------
56 // User-level methods.
57 //----------------------------------------------------------------
58
59 /**
60 **
61 */
62 public IrLMP(Log log) throws Exception{
63 Assert.fail( log != null, "Log must not be null.");
64
65 _log = log;
66 _services = new Vector(10);
67
68 // IAS is required, and serves at LSAP=0 per the IrLMP spec.
69 IrIAS ias = IrIAS.getInstance();
70 ias.setLog(_log);
71 _services.add(0, ias);
72
73 configureAutoDiscovery();
74 }
75
76 /**
77 ** IrLMP allows multiple service providers (IrOBEX, IrCOMM, etc.) to
78 ** listen to a single IrDA port, although only one service is
79 ** active at a time. This method allows a new service to let IrLMP
80 ** know that it's available.
81 ** <B>This method is broken. It only adds IrOBEX, and does so at
82 ** LSAP 5. This will change when IrIAS is augmented.</B>
83 */
84 public void addService(iIrLMPService service) {
85 for ( int i = 1; i < _services.capacity(); i++ ) {
86 _services.add(service);
87 }
88 }
89
90 /**
91 ** Instructs IrLMP to enter a discovery sequence, by which other
92 ** IrDA hosts can be found.
93 */
94 public void discover() throws Exception {
95 Assert.fail(_irlap != null, "No IrLAP instance.");
96
97 if ( _irlap.isMediaBusy() ) {
98 _log.debug("IrLMP", "The IrLAP layer is busy now.");
99 } else {
100 _irlap.startDiscovery();
101 }
102 }
103
104 /**
105 ** Request a connection with a service on another IrDA host.
106 ** If the request is granted, IrLMP invokes the connect()
107 ** method of the IrLMPService that requested the connection.
108 ** This method cannot be used unless a Host has identified
109 ** itself to us in response to a discovery sequence initiated
110 ** by us, or as a result of our identification to the Host
111 ** during a discovery sequence initiated by the Host.
112 public Connection requestConnection(Host host, int sourceLSAP, int destination LSAP) {
113 return new Connection(this, sourceLSAP, destinationLSAP);
114 }
115 */
116
117 /**
118 ** If you want IrLMP to be able to initiate things like discovery,
119 ** it needs to hold a reference to an IrLAP instance.
120 */
121 public void setIrLAPContext(IrLAPContext irlap) {
122 _irlap = irlap;
123 }
124
125 /**
126 ** An IrLMP service (IrOBEX, for instance) has agreed to a connect request
127 ** from a host.
128 */
129 public void connectConfirm(byte destination, byte source, byte [] userData) {
130 _log.debug("IrLMP", "entered connectConfirm");
131 _log.debug("IrLMP", "destination: " + destination);
132 _log.debug("IrLMP", "source: " + source);
133
134 if ( null == _irlap ) {
135 _log.error("IrLMP", "IrLAP is null");
136 return;
137 }
138
139 byte [] confirmMsg = new byte[255];
140 int confirmLength = 0;
141
142 confirmMsg[confirmLength++] = (byte) (0x80 | destination);
143 confirmMsg[confirmLength++] = source;
144 confirmMsg[confirmLength++] = OPCODE_CONNECT_CONFIRM;
145 confirmMsg[confirmLength++] = (byte) 0x00; // optional 0x00
146
147 if ( null != userData ) {
148 _log.debug("IrLMP", "userData.length: " + userData.length);
149 for ( int i = 0; i < userData.length; i++ ) {
150 confirmMsg[confirmLength++] = userData[i];
151 }
152 }
153
154 try {
155 _log.debug("IrLMP", "_irlapConnection: " + _irlapConnection);
156 _log.debug("IrLMP", "confirmLength: " + confirmLength);
157 _irlap.sendData(_irlapConnection, confirmMsg, 0, confirmLength);
158 } catch ( Exception e ) {
159 _log.debug("IrLMP", "(connectConfirm) " + e);
160 }
161 }
162
163 //-------------------------------------------------------------
164 // Implement iIrLAPListener
165 //-------------------------------------------------------------
166
167 /**
168 ** The IrLAP layer has made a connection with another IrLAP layer.
169 */
170 public void connectIndication(IrLAPContext context, ConnectionInformation info) {
171 _log.debug("IrLMP", "connectIndication");
172
173 try {
174 _irlapConnection = (byte) info.getConnection();
175 int remoteAddress = info.getRemoteAddress();
176
177 _log.debug("IrLMP", "_irlapConnection:" + _irlapConnection);
178 _log.debug("IrLMP", "remoteAddress:" + remoteAddress);
179
180 context.connectResponse(remoteAddress, _irlapConnection, info.getCommParameters());
181 } catch ( Exception e ) {
182 _log.error("IrLMP", e.toString());
183 }
184 }
185
186 /**
187 ** A host has sent data.
188 */
189 public void dataIndication(IrLAPContext context, ConnectionInformation info, byte [] data) {
190 _log.debug("IrLMP", "(dataIndication) data length: " + data.length);
191
192 // high bit of first byte is set if this is a command
193 boolean isCommand = (0 != (0x80 & data[0]));
194
195 // destination (us) is the lower 7 bits of the first byte
196 byte destination = (byte) (0x7F & data[0]);
197
198 // source (them) is the lower 7 bits of the second byte
199 byte source = (byte) (0x7F & data[1]);
200
201 // opcode is the third byte
202 byte opcode = data[2];
203
204 _log.debug("IrLMP", "isCommand: " + isCommand);
205 _log.debug("IrLMP", "destination: " + Utilities.byteToString(destination));
206 _log.debug("IrLMP", "source: " + Utilities.byteToString(source));
207 _log.debug("IrLMP", "opcode: " + Utilities.byteToString(opcode));
208
209 //Assert.fail(destination >= 0,
210 // "Destination must be >= zero: " + destination);
211 //Assert.fail(destination < (_services.size() - 1),
212 // "Destination exceeds number of registered services: " + destination);
213
214 _log.debug("IrLMP", "retrieving service " + destination);
215 iIrLMPService service = (iIrLMPService) _services.get(destination);
216 if ( null == service ) {
217 _log.error("IrLMP", "Destination service is null: " + destination);
218 }
219
220 _log.debug("IrLMP", "creating IrLMPConnection");
221 IrLMPConnection irlmpConn = new IrLMPConnection(this, destination, source, info);
222
223 if ( isCommand ) {
224 _log.debug("IrLMP", "processing command");
225
226 // the high bit of the 2nd (source) byte tells us if this is
227 // a message confirming a connect request
228 boolean isConfirm = (0 != (0x80 & data[2]));
229
230 // we don't use the parameters right now.
231 byte parameters = data[3];
232
233 // this is for debugging
234 String message = "(unknown)";
235
236 switch ( opcode ) {
237 case OPCODE_CONNECT_REQUEST:
238 message = (isConfirm ? "Connect Confirmation" : "Connect");
239
240 // frame length - dest - source - opcode - reserved byte
241 byte [] userData = null;
242 if ( data.length > 4 ) {
243 userData = new byte[data.length - 4];
244 for ( int i = 0; i < userData.length; i++ ) {
245 userData[i] = data[i + 4];
246 }
247 }
248 _log.debug("IrLMP", "invoking service's connectRequest()");
249 service.connectRequest(this, destination, source, userData);
250 break;
251 case 2:
252 message = "Disconnect";
253 break;
254 case 3:
255 message = (isConfirm ? "Access Mode (confirm)" : "Access Mode");
256 break;
257 }
258 } else {
259 try {
260 _log.debug("IrLMP", "invoking service's serviceRequest()");
261 service.serviceRequest(this, irlmpConn, data, 0, data.length);
262 } catch ( Exception e ) {
263 _log.error("IrLMP", "Caught this " + e);
264 }
265 }
266 }
267
268 /**
269 ** A host has been discovered.
270 */
271 public void discoveryIndication(IrLAPContext context, DiscoveryInformation discoveryInfo) {
272 _log.debug("IrLMP", "Host discovered: " + discoveryInfo);
273 }
274
275 protected void sendData(int destination, int source, byte[] data, int offset, int length)
276 throws Exception {
277 _log.debug("IrLMP", "(sendData) destination: " + destination + " source: " + source);
278 send(destination, source, data, offset, length);
279 }
280
281 /**
282 ** Send data to a host to whom we are connected.
283 */
284 public void send(int destination, int source, byte[] data, int offset, int length)
285 throws Exception {
286 _log.debug("IrLMP", "(send) destination: " + destination + " source: " + source);
287 Assert.fail(null != _irlap, "IrLAP is null.");
288
289 try {
290 byte [] frame = new byte[2 + length];
291 frame[0] = (byte) destination;
292 frame[1] = (byte) source;
293 for ( int i = 0; i < length; i++ ) {
294 frame[i + 2] = data[offset + i];
295 }
296
297 _irlap.sendData(_irlapConnection, frame, 0, frame.length);
298 } catch ( Exception e ) {
299 _log.debug("IrLMP ", e.toString());
300 }
301 }
302
303 /**
304 ** @param doAutoDiscovery Autodiscovery is enabled if this value is <CODE>true</CODE>.
305 */
306 public void setAutoDiscovery(boolean doAutoDiscovery) {
307 _doAutoDiscovery = doAutoDiscovery;
308
309 if ( !_doAutoDiscovery && (null != _autoDiscoveryTimer) ) {
310 _autoDiscoveryTimer.stop();
311 } else {
312 _autoDiscoveryTimer.start();
313 }
314 }
315
316 /**
317 ** Find out if IrLMP needs to automatically discover other hosts.
318 */
319 private void configureAutoDiscovery() {
320 _doAutoDiscovery = Boolean.getBoolean(AUTO_DISCOVERY_PROPERTY);
321 _log.debug("IrLMP", "_doAutoDiscovery: " + _doAutoDiscovery);
322
323 Integer interval = Integer.getInteger(AUTO_DISCOVERY_INTERVAL_PROPERTY);
324 if ( null != interval ) {
325 _autoDiscoveryInterval = interval.intValue();
326 } else {
327 _log.debug("IrLMP", "autoDiscoveryInterval was undefined, using default (300s)");
328 _autoDiscoveryInterval = DEFAULT_AUTO_DISCOVERY_INTERVAL;
329 }
330 _log.debug("IrLMP", "_autoDiscoveryInterval: " + _autoDiscoveryInterval);
331
332 if ( _doAutoDiscovery ) {
333 _log.debug("IrLMP", "creating autoDiscoveryTimer");
334
335 _autoDiscoveryTimer = new Timer(_autoDiscoveryInterval * 1000, new ActionListener() {
336 public void actionPerformed(ActionEvent event) {
337 _log.debug("IrLMP", "autoDiscoveryTimer fired");
338 try {
339 discover();
340 } catch ( Exception e ) {
341 _log.error("IrLMP", e.toString());
342 }
343 }
344 });
345 _autoDiscoveryTimer.start();
346 }
347 }
348 }