PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/biodyn/src/br/unb/biodyn/BluetoothService.java

https://gitlab.com/Yeltsinn/biodynapp
Java | 581 lines | 342 code | 91 blank | 148 comment | 40 complexity | c15baf3c99cb043e7dc5d8d925622c89 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright (C) 2009 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Modified for biodyn project
  17. */
  18. package br.unb.biodyn;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.lang.reflect.InvocationTargetException;
  22. import java.lang.reflect.Method;
  23. import java.util.UUID;
  24. import br.unb.biodyn.model.LogFormat;
  25. import android.app.Service;
  26. import android.bluetooth.BluetoothAdapter;
  27. import android.bluetooth.BluetoothDevice;
  28. import android.bluetooth.BluetoothServerSocket;
  29. import android.bluetooth.BluetoothSocket;
  30. import android.content.BroadcastReceiver;
  31. import android.content.Context;
  32. import android.content.Intent;
  33. import android.content.IntentFilter;
  34. import android.os.IBinder;
  35. import android.support.v4.content.LocalBroadcastManager;
  36. import android.util.Log;
  37. /**
  38. * This class does all the work for setting up and managing Bluetooth
  39. * connections with other devices. It has a thread that listens for incoming
  40. * connections, a thread for connecting with a device, and a thread for
  41. * performing data transmissions when connected.
  42. */
  43. public class BluetoothService extends Service {
  44. /********************************** CONSTANTS ***********************************/
  45. // Debugging
  46. private static final String TAG = "BluetoothChatService";
  47. private static final boolean DEBUG = true;
  48. // Constants that indicate the current connection state
  49. public static final int STATE_NONE = 0; // we're doing nothing
  50. public static final int STATE_LISTEN = 1; // now listening for incoming
  51. // connections
  52. public static final int STATE_CONNECTING = 2; // now initiating an outgoing
  53. // connection
  54. public static final int STATE_CONNECTED = 3; // now connected to a remote
  55. // device
  56. // Message types sent to UI Thread
  57. public static final int MESSAGE_STATE_CHANGE = 1;
  58. public static final int MESSAGE_READ = 2;
  59. public static final int MESSAGE_WRITE = 3;
  60. public static final int MESSAGE_DEVICE_NAME = 4;
  61. public static final int MESSAGE_TOAST = 5;
  62. // Action to start this service
  63. public static final String ACTION_BLUETOOTH_SERVICE = "br.unb.biodyn.BluetoothService";
  64. public static final String KEY_HANDLER = "handler";
  65. // Action to connect to device
  66. protected static final String ACTION_CONNECT_TO_DEVICE = "br.unb.biodyn.BluetoothService.connect_device";
  67. protected static final String KEY_DEVICE_TO_CONNECT = "device_to_connect";
  68. // Action to stop all threads
  69. protected static final String ACTION_STOP_ALL_THREADS = "br.unb.biodyn.BluetoothService.stop_all_threads";
  70. // Actions to communicate wth activity
  71. protected static final String ACTION_DATA_RECEIVED = "br.unb.biodyn.BluetoothService.data_received";
  72. protected static final String KEY_DATA_RECEIVED = "data_received";
  73. protected static final String ACTION_MESSAGE_DEVICE_NAME = "br.unb.biodyn.BluetoothService.device_name";
  74. protected static final String KEY_DEVICE_NAME = "device_name";
  75. protected static final String ACTION_MESSAGE_SHOW_TOAST = "br.unb.biodyn.BluetoothService.show_toast";
  76. protected static final String KEY_TOAST_MESSAGE = "toast_message";
  77. // Name for the SDP record when creating server socket
  78. private static final String NAME = "BluetoothChat";
  79. // Unique UUID for this application
  80. private static final UUID MY_UUID = UUID
  81. .fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
  82. /********************************** CLASS FIELDS ***********************************/
  83. private final BluetoothAdapter mAdapter = BluetoothAdapter
  84. .getDefaultAdapter();
  85. private AcceptThread mAcceptThread;
  86. private ConnectThread mConnectThread;
  87. private ConnectedThread mConnectedThread;
  88. private int mState;
  89. private StringBuilder currentMessage=new StringBuilder();
  90. private BroadcastReceiver mReceiver = new BroadcastReceiver() {
  91. @Override
  92. public void onReceive(Context context, Intent intent) {
  93. if (intent.getAction().equals(ACTION_CONNECT_TO_DEVICE)) {
  94. BluetoothDevice device = (BluetoothDevice) intent.getExtras()
  95. .get(KEY_DEVICE_TO_CONNECT);
  96. try {
  97. connect(device);
  98. } catch (Exception e) {
  99. Log.e(TAG, "exception in the receiver: ", e);
  100. }
  101. } else if (intent.getAction().equals(ACTION_STOP_ALL_THREADS)) {
  102. stop();
  103. }
  104. }
  105. };
  106. /********************************** LIFECYCLE METHODS ***********************************/
  107. @Override
  108. public void onCreate() {
  109. super.onCreate();
  110. // Registering BroadcastReceiver to receive some commands
  111. IntentFilter filter = new IntentFilter();
  112. filter.addAction(ACTION_CONNECT_TO_DEVICE);
  113. filter.addAction(ACTION_STOP_ALL_THREADS);
  114. LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
  115. filter);
  116. // Drawable dr = getResources().getDrawable(R.drawable.app_icon);
  117. // // Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
  118. // Intent i = new Intent(this, MainActivity.class);
  119. // i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
  120. // | Intent.FLAG_ACTIVITY_SINGLE_TOP);
  121. // PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
  122. // Notification note = new NotificationCompat.Builder(this)
  123. // .setContentTitle("Biodyn application")
  124. // .setContentText("Bluetooth connection active")
  125. // .setSmallIcon(R.drawable.app_icon)
  126. // // .setLargeIcon(Bitmap.createScaledBitmap(bitmap, 82, 50,
  127. // // true))
  128. // .setContentIntent(pi).build();
  129. // note.flags |= Notification.FLAG_NO_CLEAR;
  130. // startForeground(1, note);
  131. start();
  132. };
  133. @Override
  134. public int onStartCommand(Intent intent, int flags, int startId) {
  135. mState = STATE_NONE;
  136. return super.onStartCommand(intent, flags, startId);
  137. }
  138. @Override
  139. public void onDestroy() {
  140. LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
  141. stop();
  142. super.onDestroy();
  143. }
  144. @Override
  145. public IBinder onBind(Intent intent) {
  146. return null;
  147. }
  148. /********************************** OTHER METHODS ***********************************/
  149. /**
  150. * Set the current state of the chat connection
  151. *
  152. * @param state
  153. * An integer defining the current connection state
  154. */
  155. private synchronized void setState(int state) {
  156. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "setState() " + mState + " -> " + state);
  157. mState = state;
  158. // TODO send intent with state if necessary
  159. }
  160. public synchronized int getState() {
  161. return mState;
  162. }
  163. /**
  164. * Start the chat service. Specifically start AcceptThread to begin a
  165. * session in listening (server) mode.
  166. */
  167. public synchronized void start() {
  168. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "start");
  169. // Cancel any thread attempting to make a connection
  170. if (mConnectThread != null) {
  171. mConnectThread.cancel();
  172. mConnectThread = null;
  173. }
  174. // Cancel any thread currently running a connection
  175. if (mConnectedThread != null) {
  176. mConnectedThread.cancel();
  177. mConnectedThread = null;
  178. }
  179. // Start the thread to listen on a BluetoothServerSocket
  180. if (mAcceptThread == null) {
  181. mAcceptThread = new AcceptThread();
  182. mAcceptThread.start();
  183. }
  184. setState(STATE_LISTEN);
  185. }
  186. /**
  187. * Start the ConnectThread to initiate a connection to a remote device.
  188. *
  189. * @param device
  190. * The BluetoothDevice to connect
  191. * @throws InvocationTargetException
  192. * @throws IllegalAccessException
  193. * @throws IllegalArgumentException
  194. */
  195. public synchronized void connect(BluetoothDevice device)
  196. throws IllegalArgumentException, IllegalAccessException,
  197. InvocationTargetException {
  198. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "connect to: " + device);
  199. // Cancel any thread attempting to make a connection
  200. if (mState == STATE_CONNECTING) {
  201. if (mConnectThread != null) {
  202. mConnectThread.cancel();
  203. mConnectThread = null;
  204. }
  205. }
  206. // Cancel any thread currently running a connection
  207. if (mConnectedThread != null) {
  208. mConnectedThread.cancel();
  209. mConnectedThread = null;
  210. }
  211. // Start the thread to connect with the given device
  212. mConnectThread = new ConnectThread(device);
  213. mConnectThread.start();
  214. setState(STATE_CONNECTING);
  215. }
  216. /**
  217. * Start the ConnectedThread to begin managing a Bluetooth connection
  218. *
  219. * @param socket
  220. * The BluetoothSocket on which the connection was made
  221. * @param device
  222. * The BluetoothDevice that has been connected
  223. */
  224. public synchronized void connected(BluetoothSocket socket,
  225. BluetoothDevice device) {
  226. if (DEBUG)
  227. Log.d(TAG, "connected");
  228. // Cancel the thread that completed the connection
  229. if (mConnectThread != null) {
  230. mConnectThread.cancel();
  231. mConnectThread = null;
  232. }
  233. // Cancel any thread currently running a connection
  234. if (mConnectedThread != null) {
  235. mConnectedThread.cancel();
  236. mConnectedThread = null;
  237. }
  238. // Cancel the accept thread because we only want to connect to one
  239. // device
  240. if (mAcceptThread != null) {
  241. mAcceptThread.cancel();
  242. mAcceptThread = null;
  243. }
  244. // Start the thread to manage the connection and perform transmissions
  245. mConnectedThread = new ConnectedThread(socket);
  246. mConnectedThread.start();
  247. // Send the name of the connected device back to the UI Activity
  248. Intent intent = new Intent(ACTION_MESSAGE_DEVICE_NAME);
  249. intent.putExtra(KEY_DEVICE_NAME, device.getName());
  250. LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
  251. setState(STATE_CONNECTED);
  252. }
  253. /**
  254. * Stop all threads
  255. */
  256. public synchronized void stop() {
  257. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE,"stop");
  258. if (mConnectThread != null) {
  259. mConnectThread.cancel();
  260. mConnectThread = null;
  261. }
  262. if (mConnectedThread != null) {
  263. mConnectedThread.cancel();
  264. mConnectedThread = null;
  265. }
  266. if (mAcceptThread != null) {
  267. mAcceptThread.cancel();
  268. mAcceptThread = null;
  269. }
  270. setState(STATE_NONE);
  271. }
  272. /**
  273. * Indicate that the connection attempt failed and notify the UI Activity.
  274. */
  275. private void connectionFailed() {
  276. setState(STATE_LISTEN);
  277. // Send a failure message back to the Activity
  278. Intent intent = new Intent(ACTION_MESSAGE_SHOW_TOAST);
  279. intent.putExtra(KEY_TOAST_MESSAGE, "Unable to connect device");
  280. // TODO extract to strings.xml
  281. LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
  282. start();
  283. }
  284. /**
  285. * Indicate that the connection was lost and notify the UI Activity.
  286. */
  287. private void connectionLost() {
  288. setState(STATE_LISTEN);
  289. // Send a failure message back to the Activity
  290. Intent intent = new Intent(ACTION_MESSAGE_SHOW_TOAST);
  291. intent.putExtra(KEY_TOAST_MESSAGE, "Device connection was lost");
  292. // TODO extract to strings.xml
  293. LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
  294. start();
  295. }
  296. /********************************** PRIVATE CLASSES ***********************************/
  297. /**
  298. * This thread runs while listening for incoming connections. It behaves
  299. * like a server-side client. It runs until a connection is accepted (or
  300. * until cancelled).
  301. */
  302. private class AcceptThread extends Thread {
  303. // The local server socket
  304. private final BluetoothServerSocket mmServerSocket;
  305. public AcceptThread() {
  306. BluetoothServerSocket tmp = null;
  307. // Create a new listening server socket
  308. try {
  309. tmp = mAdapter
  310. .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
  311. } catch (IOException e) {
  312. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE,"listen() failed", e);
  313. }
  314. mmServerSocket = tmp;
  315. }
  316. public void run() {
  317. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "BEGIN mAcceptThread" + this);
  318. setName("AcceptThread");
  319. BluetoothSocket socket = null;
  320. // Listen to the server socket if we're not connected
  321. while (mState != STATE_CONNECTED) {
  322. try {
  323. // This is a blocking call and will only return on a
  324. // successful connection or an exception
  325. socket = mmServerSocket.accept();
  326. } catch (IOException e) {
  327. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE,"accept() failed" , e);
  328. break;
  329. }
  330. // If a connection was accepted
  331. if (socket != null) {
  332. synchronized (BluetoothService.this) {
  333. switch (mState) {
  334. case STATE_LISTEN:
  335. case STATE_CONNECTING:
  336. // Situation normal. Start the connected thread.
  337. connected(socket, socket.getRemoteDevice());
  338. break;
  339. case STATE_NONE:
  340. case STATE_CONNECTED:
  341. // Either not ready or already connected. Terminate
  342. // new socket.
  343. try {
  344. socket.close();
  345. } catch (IOException e) {
  346. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE,"Could not close unwanted socket", e);
  347. }
  348. break;
  349. }
  350. }
  351. }
  352. }
  353. if (DEBUG)
  354. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE,"END mAcceptThread" );
  355. }
  356. public void cancel() {
  357. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE,"Cancel" + this );
  358. try {
  359. mmServerSocket.close();
  360. } catch (IOException e) {
  361. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE,"close() of server failed", e);
  362. }
  363. }
  364. }
  365. /**
  366. * This thread runs while attempting to make an outgoing connection with a
  367. * device. It runs straight through; the connection either succeeds or
  368. * fails.
  369. */
  370. private class ConnectThread extends Thread {
  371. private final BluetoothSocket mmSocket;
  372. private final BluetoothDevice mmDevice;
  373. public ConnectThread(BluetoothDevice device)
  374. throws IllegalArgumentException, IllegalAccessException,
  375. InvocationTargetException {
  376. mmDevice = device;
  377. BluetoothSocket tmp = null;
  378. // Get a BluetoothSocket for a connection with the
  379. // given BluetoothDevice
  380. // try {
  381. // tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
  382. try {
  383. Method m = device.getClass()
  384. .getMethod("createInsecureRfcommSocket",
  385. new Class[] { int.class });
  386. tmp = (BluetoothSocket) m.invoke(device, 1);
  387. } catch (Exception e) {
  388. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "error in connectThread:", e);
  389. }
  390. mmSocket = tmp;
  391. }
  392. public void run() {
  393. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE,"BEGIN mConnectThread");
  394. setName("ConnectThread");
  395. // Always cancel discovery because it will slow down a connection
  396. mAdapter.cancelDiscovery();
  397. // Make a connection to the BluetoothSocket
  398. try {
  399. // This is a blocking call and will only return on a
  400. // successful connection or an exception
  401. mmSocket.connect();
  402. } catch (IOException e) {
  403. connectionFailed();
  404. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "connectionFailed", e);
  405. // Close the socket
  406. try {
  407. mmSocket.close();
  408. } catch (IOException e2) {
  409. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "unable to close() socket during connection failure", e);
  410. }
  411. // Start the service over to restart listening mode
  412. BluetoothService.this.start();
  413. return;
  414. }
  415. // Reset the ConnectThread because we're done
  416. synchronized (BluetoothService.this) {
  417. mConnectThread = null;
  418. }
  419. // Start the connected thread
  420. connected(mmSocket, mmDevice);
  421. }
  422. public void cancel() {
  423. try {
  424. mmSocket.close();
  425. } catch (IOException e) {
  426. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "close() of connect socket failed", e);
  427. }
  428. }
  429. }
  430. /**
  431. * This thread runs during a connection with a remote device. It handles all
  432. * incoming and outgoing transmissions.
  433. */
  434. private class ConnectedThread extends Thread {
  435. private final BluetoothSocket mmSocket;
  436. private final InputStream mmInStream;
  437. public ConnectedThread(BluetoothSocket socket) {
  438. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "create ConnectedThread");
  439. mmSocket = socket;
  440. InputStream tmpIn = null;
  441. // Get the BluetoothSocket input stream
  442. try {
  443. tmpIn = socket.getInputStream();
  444. } catch (IOException e) {
  445. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "temp sockets not created", e);
  446. }
  447. mmInStream = tmpIn;
  448. }
  449. public void run() {
  450. LogFormat.setLogMessage(LogFormat.BLUETOOTHSERVICE, "BEGIN mConnectedThread");
  451. byte[] buffer = new byte[1024];
  452. int bytes;
  453. // Keep listening to the InputStream while connected
  454. while (true) {
  455. try {
  456. // Read from the InputStream
  457. bytes = mmInStream.read(buffer);
  458. String readMessage = new String(buffer, 0, bytes);
  459. currentMessage.append(readMessage);
  460. int firstIndex = currentMessage.toString().indexOf("Peso:");
  461. int lastIndex = currentMessage.toString().indexOf(" Kg");
  462. if (firstIndex != -1 && lastIndex != -1
  463. && lastIndex > firstIndex) {
  464. String message = currentMessage.substring(firstIndex+5,
  465. lastIndex);
  466. Float messageInFloat = Float.valueOf(message);
  467. Intent intent = new Intent(ACTION_DATA_RECEIVED);
  468. intent.putExtra(KEY_DATA_RECEIVED, messageInFloat);
  469. // TODO extract to strings.xml
  470. LocalBroadcastManager
  471. .getInstance(BluetoothService.this)
  472. .sendBroadcast(intent);
  473. currentMessage = new StringBuilder();
  474. }
  475. } catch (IOException e) {
  476. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "disconnected", e);
  477. connectionLost();
  478. break;
  479. }
  480. }
  481. }
  482. public void cancel() {
  483. try {
  484. mmSocket.close();
  485. } catch (IOException e) {
  486. LogFormat.setLogMessageException(LogFormat.BLUETOOTHSERVICE, "close() of connect socket failed", e);
  487. }
  488. }
  489. }
  490. }