1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package net.sf.bluecove.obex.server;
26
27 import java.io.File;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InterruptedIOException;
32 import java.util.Timer;
33 import java.util.TimerTask;
34
35 import javax.bluetooth.DataElement;
36 import javax.bluetooth.DiscoveryAgent;
37 import javax.bluetooth.LocalDevice;
38 import javax.bluetooth.ServiceRecord;
39 import javax.bluetooth.UUID;
40 import javax.microedition.io.Connection;
41 import javax.microedition.io.Connector;
42 import javax.obex.HeaderSet;
43 import javax.obex.Operation;
44 import javax.obex.ResponseCodes;
45 import javax.obex.ServerRequestHandler;
46 import javax.obex.SessionNotifier;
47
48
49
50
51 public class OBEXServer implements Runnable {
52
53 private SessionNotifier serverConnection;
54
55 private boolean isStoped = false;
56
57 private boolean isRunning = false;
58
59 public final UUID OBEX_OBJECT_PUSH = new UUID(0x1105);
60
61 public static final String SERVER_NAME = "OBEX Object Push";
62
63 private UserInteraction interaction;
64
65 private OBEXServer(UserInteraction interaction) {
66 this.interaction = interaction;
67 }
68
69 public static OBEXServer startServer(UserInteraction interaction) {
70 OBEXServer srv = new OBEXServer(interaction);
71 Thread thread = new Thread(srv);
72 thread.start();
73 while (!srv.isRunning && !srv.isStoped) {
74 try {
75 Thread.sleep(100);
76 } catch (InterruptedException e) {
77 throw new Error(e);
78 }
79 }
80 if (!srv.isRunning) {
81 throw new Error("Can't start server");
82 }
83 return srv;
84 }
85
86
87
88
89
90
91 public void run() {
92 isStoped = false;
93 LocalDevice localDevice;
94 try {
95 localDevice = LocalDevice.getLocalDevice();
96 if (!localDevice.setDiscoverable(DiscoveryAgent.GIAC)) {
97 Logger.error("Fail to set LocalDevice Discoverable");
98 }
99 serverConnection = (SessionNotifier) Connector.open("btgoep://localhost:" + OBEX_OBJECT_PUSH + ";name="
100 + SERVER_NAME);
101 } catch (Throwable e) {
102 Logger.error("OBEX Server start error", e);
103 isStoped = true;
104 return;
105 }
106
107 try {
108 ServiceRecord record = localDevice.getRecord(serverConnection);
109 String url = record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
110 Logger.debug("BT server url: " + url);
111
112 final int OBJECT_TRANSFER_SERVICE = 0x100000;
113
114 try {
115 record.setDeviceServiceClasses(OBJECT_TRANSFER_SERVICE);
116 } catch (Throwable e) {
117 Logger.debug("setDeviceServiceClasses", e);
118 }
119
120 DataElement bluetoothProfileDescriptorList = new DataElement(DataElement.DATSEQ);
121 DataElement obbexPushProfileDescriptor = new DataElement(DataElement.DATSEQ);
122 obbexPushProfileDescriptor.addElement(new DataElement(DataElement.UUID, OBEX_OBJECT_PUSH));
123 obbexPushProfileDescriptor.addElement(new DataElement(DataElement.U_INT_2, 0x100));
124 bluetoothProfileDescriptorList.addElement(obbexPushProfileDescriptor);
125 record.setAttributeValue(0x0009, bluetoothProfileDescriptorList);
126
127 final short ATTR_SUPPORTED_FORMAT_LIST_LIST = 0x0303;
128 DataElement supportedFormatList = new DataElement(DataElement.DATSEQ);
129
130 supportedFormatList.addElement(new DataElement(DataElement.U_INT_1, 0xFF));
131 record.setAttributeValue(ATTR_SUPPORTED_FORMAT_LIST_LIST, supportedFormatList);
132
133 final short UUID_PUBLICBROWSE_GROUP = 0x1002;
134 final short ATTR_BROWSE_GRP_LIST = 0x0005;
135 DataElement browseClassIDList = new DataElement(DataElement.DATSEQ);
136 UUID browseClassUUID = new UUID(UUID_PUBLICBROWSE_GROUP);
137 browseClassIDList.addElement(new DataElement(DataElement.UUID, browseClassUUID));
138 record.setAttributeValue(ATTR_BROWSE_GRP_LIST, browseClassIDList);
139
140 localDevice.updateRecord(record);
141 } catch (Throwable e) {
142 Logger.error("Updating SDP", e);
143 }
144
145 try {
146 int errorCount = 0;
147 int count = 0;
148 isRunning = true;
149 while (!isStoped) {
150 RequestHandler handler = new RequestHandler();
151 try {
152 count++;
153 Logger.debug("Accepting OBEX connections");
154 handler.connectionAccepted(serverConnection.acceptAndOpen(handler));
155 } catch (InterruptedIOException e) {
156 isStoped = true;
157 break;
158 } catch (Throwable e) {
159 if ("Stack closed".equals(e.getMessage())) {
160 isStoped = true;
161 }
162 if (isStoped) {
163 return;
164 }
165 errorCount++;
166 Logger.error("acceptAndOpen ", e);
167 continue;
168 }
169 errorCount = 0;
170 }
171 } finally {
172 close();
173 Logger.debug("OBEX Server finished!");
174 isRunning = false;
175 }
176 }
177
178 public void close() {
179 isStoped = true;
180 try {
181 if (serverConnection != null) {
182 serverConnection.close();
183 }
184 Logger.debug("OBEX ServerConnection closed");
185 } catch (Throwable e) {
186 Logger.error("OBEX Server stop error", e);
187 }
188 }
189
190 private static File homePath() {
191 String path = "bluetooth";
192 boolean isWindows = false;
193 String sysName = System.getProperty("os.name");
194 if (sysName != null) {
195 sysName = sysName.toLowerCase();
196 if (sysName.indexOf("windows") != -1) {
197 isWindows = true;
198 path = "My Documents";
199 }
200 }
201 File dir;
202 try {
203 dir = new File(System.getProperty("user.home"), path);
204 if (!dir.exists()) {
205 if (!dir.mkdirs()) {
206 throw new SecurityException();
207 }
208 }
209 } catch (SecurityException e) {
210 dir = new File(new File(System.getProperty("java.io.tmpdir"), System.getProperty("user.name")), path);
211 }
212 if (isWindows) {
213 dir = new File(dir, "Bluetooth Exchange Folder");
214 }
215 if (!dir.exists()) {
216 if (!dir.mkdirs()) {
217 return null;
218 }
219 } else if (!dir.isDirectory()) {
220 dir.delete();
221 if (!dir.mkdirs()) {
222 return null;
223 }
224 }
225 return dir;
226 }
227
228 private void showStatus(final String message) {
229 interaction.showStatus(message);
230 }
231
232 private class RequestHandler extends ServerRequestHandler {
233
234 Timer notConnectedTimer = new Timer();
235
236 boolean isConnected = false;
237
238 boolean receivedOk = false;
239
240 Connection cconn;
241
242 void connectionAccepted(Connection cconn) {
243 Logger.debug("Received OBEX connection");
244 showStatus("Client connected");
245 this.cconn = cconn;
246 if (!isConnected) {
247 notConnectedTimer.schedule(new TimerTask() {
248 public void run() {
249 notConnectedClose();
250 }
251 }, 1000 * 30);
252 }
253 }
254
255 void notConnectedClose() {
256 if (!isConnected) {
257 Logger.debug("OBEX connection timeout");
258 try {
259 cconn.close();
260 } catch (IOException e) {
261 }
262 if (!receivedOk) {
263 showStatus("Disconnected");
264 }
265 }
266 }
267
268 public int onConnect(HeaderSet request, HeaderSet reply) {
269 isConnected = true;
270 notConnectedTimer.cancel();
271 Logger.debug("OBEX onConnect");
272 return ResponseCodes.OBEX_HTTP_OK;
273 }
274
275 public void onDisconnect(HeaderSet request, HeaderSet reply) {
276 Logger.debug("OBEX onDisconnect");
277 if (!receivedOk) {
278 showStatus("Disconnected");
279 }
280 }
281
282 public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
283 Logger.debug("OBEX onSetPath");
284 return super.onSetPath(request, reply, backup, create);
285 }
286
287 public int onDelete(HeaderSet request, HeaderSet reply) {
288 Logger.debug("OBEX onDelete");
289 return super.onDelete(request, reply);
290 }
291
292 public int onPut(Operation op) {
293 Logger.debug("OBEX onPut");
294 try {
295 HeaderSet hs = op.getReceivedHeaders();
296 String name = (String) hs.getHeader(HeaderSet.NAME);
297 if (name != null) {
298 Logger.debug("name:" + name);
299 showStatus("Receiving " + name);
300 } else {
301 name = "xxx.xx";
302 showStatus("Receiving file");
303 }
304 Long len = (Long) hs.getHeader(HeaderSet.LENGTH);
305 if (len != null) {
306 Logger.debug("file lenght:" + len);
307 interaction.setProgressValue(0);
308 interaction.setProgressMaximum(len.intValue());
309 }
310 File f = new File(homePath(), name);
311 FileOutputStream out = new FileOutputStream(f);
312 InputStream is = op.openInputStream();
313
314 int received = 0;
315
316 while (!isStoped) {
317 int data = is.read();
318 if (data == -1) {
319 Logger.debug("EOS received");
320 break;
321 }
322 out.write(data);
323 received++;
324 if ((len != null) && (received % 100 == 0)) {
325 interaction.setProgressValue(received);
326 }
327 }
328 op.close();
329 out.close();
330 Logger.debug("file saved:" + f.getAbsolutePath());
331 showStatus("Received " + name);
332 receivedOk = true;
333 return ResponseCodes.OBEX_HTTP_OK;
334 } catch (IOException e) {
335 Logger.error("OBEX Server onPut error", e);
336 return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
337 } finally {
338 Logger.debug("OBEX onPut ends");
339 interaction.setProgressDone();
340 }
341 }
342
343 public int onGet(Operation op) {
344 Logger.debug("OBEX onGet");
345 try {
346 HeaderSet hs = op.getReceivedHeaders();
347 String name = (String) hs.getHeader(HeaderSet.NAME);
348
349 return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
350
351 } catch (IOException e) {
352 Logger.error("OBEX Server onGet error", e);
353 return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
354 } finally {
355 Logger.debug("OBEX onGet ends");
356 }
357 }
358
359 public void onAuthenticationFailure(byte[] userName) {
360 Logger.debug("OBEX AuthFailure " + new String(userName));
361 }
362
363 }
364 }