Now with licensing information
[pdlib:btmidi.git] / BluetoothMidi / src / com / noisepages / nettoyeur / bluetooth / BluetoothSppManager.java
1 /*
2  * Derived from DeviceListActivity.java in android-7/samples/BluetoothChat
3  *
4  * Modifications
5  * Copyright (C) 2011 Peter Brinkmann (peter.brinkmann@gmail.com)
6  *
7  * Copyright (C) 2009 The Android Open Source Project
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21
22 package com.noisepages.nettoyeur.bluetooth;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.util.UUID;
28
29 import android.bluetooth.BluetoothAdapter;
30 import android.bluetooth.BluetoothDevice;
31 import android.bluetooth.BluetoothSocket;
32 import android.util.Log;
33
34
35 public class BluetoothSppManager {
36
37         private static final String TAG = "BluetoothFoundation";
38         private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  // Don't change this.
39
40         public static enum State {
41                 NONE,
42                 CONNECTING,
43                 CONNECTED
44         }
45
46         private final BluetoothAdapter btAdapter;
47         private final BluetoothSppReceiver receiver;
48         private final int bufferSize;
49         private volatile State connectionState = State.NONE;
50         private ConnectThread connectThread = null;
51         private ConnectedThread connectedThread = null;
52
53         public BluetoothSppManager(BluetoothSppReceiver receiver, int bufferSize) throws IOException {
54                 btAdapter = BluetoothAdapter.getDefaultAdapter();
55                 if (btAdapter == null) {
56                         throw new IOException("Bluetooth unavailable");
57                 }
58                 if (!btAdapter.isEnabled()) {
59                         throw new IOException("Bluetooth disabled");
60                 }
61                 this.receiver = receiver;
62                 this.bufferSize = bufferSize;
63         }
64
65         public State getState() {
66                 return connectionState;
67         }
68
69         public synchronized void stop() {
70                 cancelThreads();
71                 setState(State.NONE);
72         }
73
74         public synchronized void connect(String addr) throws IOException {
75                 cancelThreads();
76                 BluetoothDevice device = btAdapter.getRemoteDevice(addr);
77                 connectThread = new ConnectThread(device);
78                 connectThread.start();
79                 setState(State.CONNECTING);
80         }
81
82         public void write(byte[] out, int offset, int count) throws IOException {
83                 ConnectedThread thread;
84                 synchronized (this) {
85                         if (connectionState != State.CONNECTED) {
86                                 throw new IOException("Bluetooth not connected");
87                         }
88                         thread = connectedThread;
89                 }
90                 thread.write(out, offset, count);
91         }
92
93         private synchronized void connected(BluetoothSocket socket, BluetoothDevice device) throws IOException {
94                 connectThread = null;
95                 cancelConnectedThread();
96                 connectedThread = new ConnectedThread(socket);
97                 connectedThread.start();
98                 receiver.onDeviceConnected(device);
99                 setState(State.CONNECTED);
100         }
101
102         private void connectionFailed() {
103                 setState(State.NONE);
104                 receiver.onConnectionFailed();
105         }
106
107         private void connectionLost() {
108                 setState(State.NONE);
109                 receiver.onConnectionLost();
110         }
111
112         private void cancelThreads() {
113                 if (connectThread != null) {
114                         connectThread.cancel(); 
115                         connectThread = null;
116                 }
117                 cancelConnectedThread();
118         }
119
120         private void cancelConnectedThread() {
121                 if (connectedThread != null) {
122                         connectedThread.cancel(); 
123                         connectedThread = null;
124                 }
125         }
126
127         private void setState(State state) {
128                 connectionState = state;
129         }
130         
131         private class ConnectThread extends Thread {
132                 private final BluetoothSocket socket;
133                 private final BluetoothDevice device;
134
135                 private ConnectThread(BluetoothDevice device) throws IOException {
136                         this.device = device;
137                         this.socket = device.createRfcommSocketToServiceRecord(SPP_UUID);
138                 }
139
140                 @Override
141                 public void run() {
142                         try {
143                                 socket.connect();
144                                 connected(socket, device);
145                         } catch (IOException e1) {
146                                 connectionFailed();
147                                 try {
148                                         socket.close();
149                                 } catch (IOException e2) {
150                                         Log.e(TAG, "Unable to close socket after connection failure", e2);
151                                 }
152                         }
153                 }
154
155                 private void cancel() {
156                         try {
157                                 socket.close();
158                         } catch (IOException e) {
159                                 Log.e(TAG, "Unable to close socket", e);
160                         }
161                 }
162         }
163
164         private class ConnectedThread extends Thread {
165                 private final BluetoothSocket socket;
166                 private final InputStream inStream;
167                 private final OutputStream outStream;
168
169                 private ConnectedThread(BluetoothSocket socket) throws IOException {
170                         this.socket = socket;
171                         inStream = socket.getInputStream();
172                         outStream = socket.getOutputStream();
173                 }
174
175                 @Override
176                 public void run() {
177                         byte[] buffer = new byte[bufferSize];
178                         int nBytes;
179                         while (true) {
180                                 try {
181                                         nBytes = inStream.read(buffer);
182                                         receiver.onBytesReceived(nBytes, buffer);
183                                 } catch (IOException e) {
184                                         connectionLost();
185                                         break;
186                                 }
187                         }
188                 }
189
190                 private void write(byte[] buffer, int offset, int count) throws IOException {
191                         outStream.write(buffer, offset, count);
192                 }
193
194                 private void cancel() {
195                         try {
196                                 socket.close();
197                         } catch (IOException e) {
198                                 Log.e(TAG, "Unable to close socket", e);
199                         }
200                 }
201         }
202 }