Skip to content

Most visited

Recently visited

navigation
BluetoothChat / src / com.example.android.bluetoothchat /

BluetoothChatService.java

1
/*
2
 * Copyright (C) 2014 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
 
17
package com.example.android.bluetoothchat;
18
 
19
import android.bluetooth.BluetoothAdapter;
20
import android.bluetooth.BluetoothDevice;
21
import android.bluetooth.BluetoothServerSocket;
22
import android.bluetooth.BluetoothSocket;
23
import android.content.Context;
24
import android.os.Bundle;
25
import android.os.Handler;
26
import android.os.Message;
27
 
28
import com.example.android.common.logger.Log;
29
 
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.OutputStream;
33
import java.util.UUID;
34
 
35
/**
36
 * This class does all the work for setting up and managing Bluetooth
37
 * connections with other devices. It has a thread that listens for
38
 * incoming connections, a thread for connecting with a device, and a
39
 * thread for performing data transmissions when connected.
40
 */
41
public class BluetoothChatService {
42
    // Debugging
43
    private static final String TAG = "BluetoothChatService";
44
 
45
    // Name for the SDP record when creating server socket
46
    private static final String NAME_SECURE = "BluetoothChatSecure";
47
    private static final String NAME_INSECURE = "BluetoothChatInsecure";
48
 
49
    // Unique UUID for this application
50
    private static final UUID MY_UUID_SECURE =
51
            UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
52
    private static final UUID MY_UUID_INSECURE =
53
            UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
54
 
55
    // Member fields
56
    private final BluetoothAdapter mAdapter;
57
    private final Handler mHandler;
58
    private AcceptThread mSecureAcceptThread;
59
    private AcceptThread mInsecureAcceptThread;
60
    private ConnectThread mConnectThread;
61
    private ConnectedThread mConnectedThread;
62
    private int mState;
63
    private int mNewState;
64
 
65
    // Constants that indicate the current connection state
66
    public static final int STATE_NONE = 0;       // we're doing nothing
67
    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
68
    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
69
    public static final int STATE_CONNECTED = 3;  // now connected to a remote device
70
 
71
    /**
72
     * Constructor. Prepares a new BluetoothChat session.
73
     *
74
     * @param context The UI Activity Context
75
     * @param handler A Handler to send messages back to the UI Activity
76
     */
77
    public BluetoothChatService(Context context, Handler handler) {
78
        mAdapter = BluetoothAdapter.getDefaultAdapter();
79
        mState = STATE_NONE;
80
        mNewState = mState;
81
        mHandler = handler;
82
    }
83
 
84
    /**
85
     * Update UI title according to the current state of the chat connection
86
     */
87
    private synchronized void updateUserInterfaceTitle() {
88
        mState = getState();
89
        Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState);
90
        mNewState = mState;
91
 
92
        // Give the new state to the Handler so the UI Activity can update
93
        mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget();
94
    }
95
 
96
    /**
97
     * Return the current connection state.
98
     */
99
    public synchronized int getState() {
100
        return mState;
101
    }
102
 
103
    /**
104
     * Start the chat service. Specifically start AcceptThread to begin a
105
     * session in listening (server) mode. Called by the Activity onResume()
106
     */
107
    public synchronized void start() {
108
        Log.d(TAG, "start");
109
 
110
        // Cancel any thread attempting to make a connection
111
        if (mConnectThread != null) {
112
            mConnectThread.cancel();
113
            mConnectThread = null;
114
        }
115
 
116
        // Cancel any thread currently running a connection
117
        if (mConnectedThread != null) {
118
            mConnectedThread.cancel();
119
            mConnectedThread = null;
120
        }
121
 
122
        // Start the thread to listen on a BluetoothServerSocket
123
        if (mSecureAcceptThread == null) {
124
            mSecureAcceptThread = new AcceptThread(true);
125
            mSecureAcceptThread.start();
126
        }
127
        if (mInsecureAcceptThread == null) {
128
            mInsecureAcceptThread = new AcceptThread(false);
129
            mInsecureAcceptThread.start();
130
        }
131
        // Update UI title
132
        updateUserInterfaceTitle();
133
    }
134
 
135
    /**
136
     * Start the ConnectThread to initiate a connection to a remote device.
137
     *
138
     * @param device The BluetoothDevice to connect
139
     * @param secure Socket Security type - Secure (true) , Insecure (false)
140
     */
141
    public synchronized void connect(BluetoothDevice device, boolean secure) {
142
        Log.d(TAG, "connect to: " + device);
143
 
144
        // Cancel any thread attempting to make a connection
145
        if (mState == STATE_CONNECTING) {
146
            if (mConnectThread != null) {
147
                mConnectThread.cancel();
148
                mConnectThread = null;
149
            }
150
        }
151
 
152
        // Cancel any thread currently running a connection
153
        if (mConnectedThread != null) {
154
            mConnectedThread.cancel();
155
            mConnectedThread = null;
156
        }
157
 
158
        // Start the thread to connect with the given device
159
        mConnectThread = new ConnectThread(device, secure);
160
        mConnectThread.start();
161
        // Update UI title
162
        updateUserInterfaceTitle();
163
    }
164
 
165
    /**
166
     * Start the ConnectedThread to begin managing a Bluetooth connection
167
     *
168
     * @param socket The BluetoothSocket on which the connection was made
169
     * @param device The BluetoothDevice that has been connected
170
     */
171
    public synchronized void connected(BluetoothSocket socket, BluetoothDevice
172
            device, final String socketType) {
173
        Log.d(TAG, "connected, Socket Type:" + socketType);
174
 
175
        // Cancel the thread that completed the connection
176
        if (mConnectThread != null) {
177
            mConnectThread.cancel();
178
            mConnectThread = null;
179
        }
180
 
181
        // Cancel any thread currently running a connection
182
        if (mConnectedThread != null) {
183
            mConnectedThread.cancel();
184
            mConnectedThread = null;
185
        }
186
 
187
        // Cancel the accept thread because we only want to connect to one device
188
        if (mSecureAcceptThread != null) {
189
            mSecureAcceptThread.cancel();
190
            mSecureAcceptThread = null;
191
        }
192
        if (mInsecureAcceptThread != null) {
193
            mInsecureAcceptThread.cancel();
194
            mInsecureAcceptThread = null;
195
        }
196
 
197
        // Start the thread to manage the connection and perform transmissions
198
        mConnectedThread = new ConnectedThread(socket, socketType);
199
        mConnectedThread.start();
200
 
201
        // Send the name of the connected device back to the UI Activity
202
        Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
203
        Bundle bundle = new Bundle();
204
        bundle.putString(Constants.DEVICE_NAME, device.getName());
205
        msg.setData(bundle);
206
        mHandler.sendMessage(msg);
207
        // Update UI title
208
        updateUserInterfaceTitle();
209
    }
210
 
211
    /**
212
     * Stop all threads
213
     */
214
    public synchronized void stop() {
215
        Log.d(TAG, "stop");
216
 
217
        if (mConnectThread != null) {
218
            mConnectThread.cancel();
219
            mConnectThread = null;
220
        }
221
 
222
        if (mConnectedThread != null) {
223
            mConnectedThread.cancel();
224
            mConnectedThread = null;
225
        }
226
 
227
        if (mSecureAcceptThread != null) {
228
            mSecureAcceptThread.cancel();
229
            mSecureAcceptThread = null;
230
        }
231
 
232
        if (mInsecureAcceptThread != null) {
233
            mInsecureAcceptThread.cancel();
234
            mInsecureAcceptThread = null;
235
        }
236
        mState = STATE_NONE;
237
        // Update UI title
238
        updateUserInterfaceTitle();
239
    }
240
 
241
    /**
242
     * Write to the ConnectedThread in an unsynchronized manner
243
     *
244
     * @param out The bytes to write
245
     * @see ConnectedThread#write(byte[])
246
     */
247
    public void write(byte[] out) {
248
        // Create temporary object
249
        ConnectedThread r;
250
        // Synchronize a copy of the ConnectedThread
251
        synchronized (this) {
252
            if (mState != STATE_CONNECTED) return;
253
            r = mConnectedThread;
254
        }
255
        // Perform the write unsynchronized
256
        r.write(out);
257
    }
258
 
259
    /**
260
     * Indicate that the connection attempt failed and notify the UI Activity.
261
     */
262
    private void connectionFailed() {
263
        // Send a failure message back to the Activity
264
        Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
265
        Bundle bundle = new Bundle();
266
        bundle.putString(Constants.TOAST, "Unable to connect device");
267
        msg.setData(bundle);
268
        mHandler.sendMessage(msg);
269
 
270
        mState = STATE_NONE;
271
        // Update UI title
272
        updateUserInterfaceTitle();
273
 
274
        // Start the service over to restart listening mode
275
        BluetoothChatService.this.start();
276
    }
277
 
278
    /**
279
     * Indicate that the connection was lost and notify the UI Activity.
280
     */
281
    private void connectionLost() {
282
        // Send a failure message back to the Activity
283
        Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
284
        Bundle bundle = new Bundle();
285
        bundle.putString(Constants.TOAST, "Device connection was lost");
286
        msg.setData(bundle);
287
        mHandler.sendMessage(msg);
288
 
289
        mState = STATE_NONE;
290
        // Update UI title
291
        updateUserInterfaceTitle();
292
 
293
        // Start the service over to restart listening mode
294
        BluetoothChatService.this.start();
295
    }
296
 
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
300
     * (or until cancelled).
301
     */
302
    private class AcceptThread extends Thread {
303
        // The local server socket
304
        private final BluetoothServerSocket mmServerSocket;
305
        private String mSocketType;
306
 
307
        public AcceptThread(boolean secure) {
308
            BluetoothServerSocket tmp = null;
309
            mSocketType = secure ? "Secure" : "Insecure";
310
 
311
            // Create a new listening server socket
312
            try {
313
                if (secure) {
314
                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE