Skip to content

Most visited

Recently visited

navigation
BluetoothLeGatt / src / com.example.android.bluetoothlegatt /

BluetoothLeService.java

1
/*
2
 * Copyright (C) 2013 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.bluetoothlegatt;
18
 
19
import android.app.Service;
20
import android.bluetooth.BluetoothAdapter;
21
import android.bluetooth.BluetoothDevice;
22
import android.bluetooth.BluetoothGatt;
23
import android.bluetooth.BluetoothGattCallback;
24
import android.bluetooth.BluetoothGattCharacteristic;
25
import android.bluetooth.BluetoothGattDescriptor;
26
import android.bluetooth.BluetoothGattService;
27
import android.bluetooth.BluetoothManager;
28
import android.bluetooth.BluetoothProfile;
29
import android.content.Context;
30
import android.content.Intent;
31
import android.os.Binder;
32
import android.os.IBinder;
33
import android.util.Log;
34
 
35
import java.util.List;
36
import java.util.UUID;
37
 
38
/**
39
 * Service for managing connection and data communication with a GATT server hosted on a
40
 * given Bluetooth LE device.
41
 */
42
public class BluetoothLeService extends Service {
43
    private final static String TAG = BluetoothLeService.class.getSimpleName();
44
 
45
    private BluetoothManager mBluetoothManager;
46
    private BluetoothAdapter mBluetoothAdapter;
47
    private String mBluetoothDeviceAddress;
48
    private BluetoothGatt mBluetoothGatt;
49
    private int mConnectionState = STATE_DISCONNECTED;
50
 
51
    private static final int STATE_DISCONNECTED = 0;
52
    private static final int STATE_CONNECTING = 1;
53
    private static final int STATE_CONNECTED = 2;
54
 
55
    public final static String ACTION_GATT_CONNECTED =
56
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
57
    public final static String ACTION_GATT_DISCONNECTED =
58
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
59
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
60
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
61
    public final static String ACTION_DATA_AVAILABLE =
62
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
63
    public final static String EXTRA_DATA =
64
            "com.example.bluetooth.le.EXTRA_DATA";
65
 
66
    public final static UUID UUID_HEART_RATE_MEASUREMENT =
67
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
68
 
69
    // Implements callback methods for GATT events that the app cares about.  For example,
70
    // connection change and services discovered.
71
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
72
        @Override
73
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
74
            String intentAction;
75
            if (newState == BluetoothProfile.STATE_CONNECTED) {
76
                intentAction = ACTION_GATT_CONNECTED;
77
                mConnectionState = STATE_CONNECTED;
78
                broadcastUpdate(intentAction);
79
                Log.i(TAG, "Connected to GATT server.");
80
                // Attempts to discover services after successful connection.
81
                Log.i(TAG, "Attempting to start service discovery:" +
82
                        mBluetoothGatt.discoverServices());
83
 
84
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
85
                intentAction = ACTION_GATT_DISCONNECTED;
86
                mConnectionState = STATE_DISCONNECTED;
87
                Log.i(TAG, "Disconnected from GATT server.");
88
                broadcastUpdate(intentAction);
89
            }
90
        }
91
 
92
        @Override
93
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
94
            if (status == BluetoothGatt.GATT_SUCCESS) {
95
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
96
            } else {
97
                Log.w(TAG, "onServicesDiscovered received: " + status);
98
            }
99
        }
100
 
101
        @Override
102
        public void onCharacteristicRead(BluetoothGatt gatt,
103
                                         BluetoothGattCharacteristic characteristic,
104
                                         int status) {
105
            if (status == BluetoothGatt.GATT_SUCCESS) {
106
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
107
            }
108
        }
109
 
110
        @Override
111
        public void onCharacteristicChanged(BluetoothGatt gatt,
112
                                            BluetoothGattCharacteristic characteristic) {
113
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
114
        }
115
    };
116
 
117
    private void broadcastUpdate(final String action) {
118
        final Intent intent = new Intent(action);
119
        sendBroadcast(intent);
120
    }
121
 
122
    private void broadcastUpdate(final String action,
123
                                 final BluetoothGattCharacteristic characteristic) {
124
        final Intent intent = new Intent(action);
125
 
126
        // This is special handling for the Heart Rate Measurement profile.  Data parsing is
127
        // carried out as per profile specifications:
128
        // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
129
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
130
            int flag = characteristic.getProperties();
131
            int format = -1;
132
            if ((flag & 0x01) != 0) {
133
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
134
                Log.d(TAG, "Heart rate format UINT16.");
135
            } else {
136
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
137
                Log.d(TAG, "Heart rate format UINT8.");
138
            }
139
            final int heartRate = characteristic.getIntValue(format, 1);
140
            Log.d(TAG, String.format("Received heart rate: %d", heartRate));
141
            intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
142
        } else {
143
            // For all other profiles, writes the data formatted in HEX.
144
            final byte[] data = characteristic.getValue();
145
            if (data != null && data.length > 0) {
146
                final StringBuilder stringBuilder = new StringBuilder(data.length);
147
                for(byte byteChar : data)
148
                    stringBuilder.append(String.format("%02X ", byteChar));
149
                intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
150
            }
151
        }
152
        sendBroadcast(intent);
153
    }
154
 
155
    public class LocalBinder extends Binder {
156
        BluetoothLeService getService() {
157
            return BluetoothLeService.this;
158
        }
159
    }
160
 
161
    @Override
162
    public IBinder onBind(Intent intent) {
163
        return mBinder;
164
    }
165
 
166
    @Override
167
    public boolean onUnbind(Intent intent) {
168
        // After using a given device, you should make sure that BluetoothGatt.close() is called
169
        // such that resources are cleaned up properly.  In this particular example, close() is
170
        // invoked when the UI is disconnected from the Service.
171
        close();
172
        return super.onUnbind(intent);
173
    }
174
 
175
    private final IBinder mBinder = new LocalBinder();
176
 
177
    /**
178
     * Initializes a reference to the local Bluetooth adapter.
179
     *
180
     * @return Return true if the initialization is successful.
181
     */
182
    public boolean initialize() {
183
        // For API level 18 and above, get a reference to BluetoothAdapter through
184
        // BluetoothManager.
185
        if (mBluetoothManager == null) {
186
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
187
            if (mBluetoothManager == null) {
188
                Log.e(TAG, "Unable to initialize BluetoothManager.");
189
                return false;
190
            }
191
        }
192
 
193
        mBluetoothAdapter = mBluetoothManager.getAdapter();
194
        if (mBluetoothAdapter == null) {
195
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
196
            return false;
197
        }
198
 
199
        return true;
200
    }
201
 
202
    /**
203
     * Connects to the GATT server hosted on the Bluetooth LE device.
204
     *
205
     * @param address The device address of the destination device.
206
     *
207
     * @return Return true if the connection is initiated successfully. The connection result
208
     *         is reported asynchronously through the
209
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
210
     *         callback.
211
     */
212
    public boolean connect(final String address) {
213
        if (mBluetoothAdapter == null || address == null) {
214
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
215
            return false;
216
        }
217
 
218
        // Previously connected device.  Try to reconnect.
219
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
220
                && mBluetoothGatt != null) {
221
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
222
            if (mBluetoothGatt.connect()) {
223
                mConnectionState = STATE_CONNECTING;
224
                return true;
225
            } else {
226
                return false;
227
            }
228
        }
229
 
230
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
231
        if (device == null) {
232
            Log.w(TAG, "Device not found.  Unable to connect.");
233
            return false;
234
        }
235
        // We want to directly connect to the device, so we are setting the autoConnect
236
        // parameter to false.
237
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
238
        Log.d(TAG, "Trying to create a new connection.");
239
        mBluetoothDeviceAddress = address;
240
        mConnectionState = STATE_CONNECTING;
241
        return true;
242
    }
243
 
244
    /**
245
     * Disconnects an existing connection or cancel a pending connection. The disconnection result
246
     * is reported asynchronously through the
247
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
248
     * callback.
249
     */
250
    public void disconnect() {
251
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
252
            Log.w(TAG, "BluetoothAdapter not initialized");
253
            return;
254
        }
255
        mBluetoothGatt.disconnect();
256
    }
257
 
258
    /**
259
     * After using a given BLE device, the app must call this method to ensure resources are
260
     * released properly.
261
     */
262
    public void close() {
263
        if (mBluetoothGatt == null) {
264
            return;
265
        }
266
        mBluetoothGatt.close();
267
        mBluetoothGatt = null;
268
    }
269
 
270
    /**
271
     * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
272
     * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
273
     * callback.
274
     *
275
     * @param characteristic The characteristic to read from.
276
     */
277
    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
278
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
279
            Log.w(TAG, "BluetoothAdapter not initialized");
280
            return;
281
        }
282
        mBluetoothGatt.readCharacteristic(characteristic);
283
    }
284
 
285
    /**
286
     * Enables or disables notification on a give characteristic.
287
     *
288
     * @param characteristic Characteristic to act on.
289
     * @param enabled If true, enable notification.  False otherwise.
290
     */
291
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
292
                                              boolean enabled) {
293
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
294
            Log.w(TAG, "BluetoothAdapter not initialized");
295
            return;
296
        }
297
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
298
 
299
        // This is specific to Heart Rate Measurement.
300
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
301
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
302
                    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
303
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
304
            mBluetoothGatt.writeDescriptor(descriptor);
305
        }
306
    }
307
 
308
    /**
309
     * Retrieves a list of supported GATT services on the connected device. This should be
310
     * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
311
     *
312
     * @return A {@code List} of supported services.
313
     */
314
    public List<BluetoothGattService> getSupportedGattServices() {
315
        if (mBluetoothGatt == null) return null;
316
 
317
        return mBluetoothGatt.getServices();
318
    }
319
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

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 one-minute survey?
Help us improve Android tools and documentation.