close
The Wayback Machine - https://web.archive.org/web/20171110041450/https://developer.android.com/samples/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatService.html
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,
315
                            MY_UUID_SECURE);
316
                } else {
317
                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
318
                            NAME_INSECURE, MY_UUID_INSECURE);
319
                }
320
            } catch (IOException e) {
321
                Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
322
            }
323
            mmServerSocket = tmp;
324
            mState = STATE_LISTEN;
325
        }
326
 
327
        public void run() {
328
            Log.d(TAG, "Socket Type: " + mSocketType +
329
                    "BEGIN mAcceptThread" + this);
330
            setName("AcceptThread" + mSocketType);
331
 
332
            BluetoothSocket socket = null;
333
 
334
            // Listen to the server socket if we're not connected
335
            while (mState != STATE_CONNECTED) {
336
                try {
337
                    // This is a blocking call and will only return on a
338
                    // successful connection or an exception
339
                    socket = mmServerSocket.accept();
340
                } catch (IOException e) {
341
                    Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
342
                    break;
343
                }
344
 
345
                // If a connection was accepted
346
                if (socket != null) {
347
                    synchronized (BluetoothChatService.this) {
348
                        switch (mState) {
349
                            case STATE_LISTEN:
350
                            case STATE_CONNECTING:
351
                                // Situation normal. Start the connected thread.
352
                                connected(socket, socket.getRemoteDevice(),
353
                                        mSocketType);
354
                                break;
355
                            case STATE_NONE:
356
                            case STATE_CONNECTED:
357
                                // Either not ready or already connected. Terminate new socket.
358
                                try {
359
                                    socket.close();
360
                                } catch (IOException e) {
361
                                    Log.e(TAG, "Could not close unwanted socket", e);
362
                                }
363
                                break;
364
                        }
365
                    }
366
                }
367
            }
368
            Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
369
 
370
        }
371
 
372
        public void cancel() {
373
            Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
374
            try {
375
                mmServerSocket.close();
376
            } catch (IOException e) {
377
                Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
378
            }
379
        }
380
    }
381
 
382
 
383
    /**
384
     * This thread runs while attempting to make an outgoing connection
385
     * with a device. It runs straight through; the connection either
386
     * succeeds or fails.
387
     */
388
    private class ConnectThread extends Thread {
389
        private final BluetoothSocket mmSocket;
390
        private final BluetoothDevice mmDevice;
391
        private String mSocketType;
392
 
393
        public ConnectThread(BluetoothDevice device, boolean secure) {
394
            mmDevice = device;
395
            BluetoothSocket tmp = null;
396
            mSocketType = secure ? "Secure" : "Insecure";
397
 
398
            // Get a BluetoothSocket for a connection with the
399
            // given BluetoothDevice
400
            try {
401
                if (secure) {
402
                    tmp = device.createRfcommSocketToServiceRecord(
403
                            MY_UUID_SECURE);
404
                } else {
405
                    tmp = device.createInsecureRfcommSocketToServiceRecord(
406
                            MY_UUID_INSECURE);
407
                }
408
            } catch (IOException e) {
409
                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
410
            }
411
            mmSocket = tmp;
412
            mState = STATE_CONNECTING;
413
        }
414
 
415
        public void run() {
416
            Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
417
            setName("ConnectThread" + mSocketType);
418
 
419
            // Always cancel discovery because it will slow down a connection
420
            mAdapter.cancelDiscovery();
421
 
422
            // Make a connection to the BluetoothSocket
423
            try {
424
                // This is a blocking call and will only return on a
425
                // successful connection or an exception
426
                mmSocket.connect();
427
            } catch (IOException e) {
428
                // Close the socket
429
                try {
430
                    mmSocket.close();
431
                } catch (IOException e2) {
432
                    Log.e(TAG, "unable to close() " + mSocketType +
433
                            " socket during connection failure", e2);
434
                }
435
                connectionFailed();
436
                return;
437
            }
438
 
439
            // Reset the ConnectThread because we're done
440
            synchronized (BluetoothChatService.this) {
441
                mConnectThread = null;
442
            }
443
 
444
            // Start the connected thread
445
            connected(mmSocket, mmDevice, mSocketType);
446
        }
447
 
448
        public void cancel() {
449
            try {
450
                mmSocket.close();
451
            } catch (IOException e) {
452
                Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
453
            }
454
        }
455
    }
456
 
457
    /**
458
     * This thread runs during a connection with a remote device.
459
     * It handles all incoming and outgoing transmissions.
460
     */
461
    private class ConnectedThread extends Thread {
462
        private final BluetoothSocket mmSocket;
463
        private final InputStream mmInStream;
464
        private final OutputStream mmOutStream;
465
 
466
        public ConnectedThread(BluetoothSocket socket, String socketType) {
467
            Log.d(TAG, "create ConnectedThread: " + socketType);
468
            mmSocket = socket;
469
            InputStream tmpIn = null;
470
            OutputStream tmpOut = null;
471
 
472
            // Get the BluetoothSocket input and output streams
473
            try {
474
                tmpIn = socket.getInputStream();
475
                tmpOut = socket.getOutputStream();
476
            } catch (IOException e) {
477
                Log.e(TAG, "temp sockets not created", e);
478
            }
479
 
480
            mmInStream = tmpIn;
481
            mmOutStream = tmpOut;
482
            mState = STATE_CONNECTED;
483
        }
484
 
485
        public void run() {
486
            Log.i(TAG, "BEGIN mConnectedThread");
487
            byte[] buffer = new byte[1024];
488
            int bytes;
489
 
490
            // Keep listening to the InputStream while connected
491
            while (mState == STATE_CONNECTED) {
492
                try {
493
                    // Read from the InputStream
494
                    bytes = mmInStream.read(buffer);
495
 
496
                    // Send the obtained bytes to the UI Activity
497
                    mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
498
                            .sendToTarget();
499
                } catch (IOException e) {
500
                    Log.e(TAG, "disconnected", e);
501
                    connectionLost();
502
                    break;
503
                }
504
            }
505
        }
506
 
507
        /**
508
         * Write to the connected OutStream.
509
         *
510
         * @param buffer The bytes to write
511
         */
512
        public void write(byte[] buffer) {
513
            try {
514
                mmOutStream.write(buffer);
515
 
516
                // Share the sent message back to the UI Activity
517
                mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
518
                        .sendToTarget();
519
            } catch (IOException e) {
520
                Log.e(TAG, "Exception during write", e);
521
            }
522
        }
523
 
524
        public void cancel() {
525
            try {
526
                mmSocket.close();
527
            } catch (IOException e) {
528
                Log.e(TAG, "close() of connect socket failed", e);
529
            }
530
        }
531
    }
532
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

BERJAYA

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)