64fedb7725
Change-Id: Ie1f1875d041ff662aee709cd5f7d3110e438d0d5
618 lines
26 KiB
Plaintext
618 lines
26 KiB
Plaintext
page.title=Bluetooth Low Energy
|
||
page.tags="wireless","bluetoothadapter","bluetoothdevice","BLE","BTLE"
|
||
@jd:body
|
||
|
||
<div id="qv-wrapper">
|
||
<div id="qv">
|
||
|
||
<h2>In this document</h2>
|
||
<ol>
|
||
<li><a href="#terms">Key Terms and Concepts</a>
|
||
<ol>
|
||
<li><a href="#roles">Roles and Responsibilities</a></li>
|
||
</ol>
|
||
</li>
|
||
<li><a href="#permissions">BLE Permissions</a></li>
|
||
<li><a href="#setup">Setting Up BLE</a></li>
|
||
<li><a href="#find">Finding BLE Devices</a></li>
|
||
<li><a href="#connect">Connecting to a GATT Server</a></li>
|
||
<li><a href="#read">Reading BLE Attributes</a></li>
|
||
<li><a href="#notification">Receiving GATT Notifications</a></li>
|
||
<li><a href="#close">Closing the Client App</a></li>
|
||
</ol>
|
||
|
||
<h2>Key classes</h2>
|
||
<ol>
|
||
<li>{@link android.bluetooth.BluetoothGatt}</li>
|
||
<li>{@link android.bluetooth.BluetoothGattCallback}</li>
|
||
<li>{@link android.bluetooth.BluetoothGattCharacteristic}</li>
|
||
<li>{@link android.bluetooth.BluetoothGattService}</li>
|
||
</ol>
|
||
|
||
<h2>Related samples</h2>
|
||
<ol>
|
||
<li><a href="{@docRoot}tools/samples/index.html">Bluetooth LE sample</a></li>
|
||
</ol>
|
||
|
||
<h2>See Also</h2>
|
||
<ol>
|
||
<li><a href="http://developers.google.com/events/io/sessions/326240948">
|
||
Best Practices for Bluetooth Development</a> (video)</li>
|
||
|
||
</ol>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<a class="notice-developers-video" href="http://www.youtube.com/watch?v=vUbFB1Qypg8">
|
||
<div>
|
||
<h3>Video</h3>
|
||
<p>DevBytes: Bluetooth Low Energy API</p>
|
||
</div>
|
||
</a>
|
||
|
||
|
||
|
||
|
||
<p>
|
||
Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low
|
||
Energy in the <em>central role</em> and provides APIs that apps can use to discover
|
||
devices, query for services, and read/write characteristics.
|
||
In contrast to
|
||
<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Classic Bluetooth</a>,
|
||
Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption.
|
||
This allows Android apps to communicate with BLE devices that have low power requirements,
|
||
such as proximity sensors, heart rate monitors, fitness devices, and so on.</p>
|
||
|
||
<h2 id="terms">Key Terms and Concepts</h2>
|
||
<p>Here is a summary of key BLE terms and concepts:</p>
|
||
<ul>
|
||
<li><strong>Generic Attribute Profile (GATT)</strong>—The GATT profile
|
||
is a general specification for sending and receiving short pieces of data known
|
||
as "attributes" over a BLE link. All current Low Energy application profiles are
|
||
based on GATT.
|
||
<ul>
|
||
<li>The Bluetooth SIG defines many
|
||
<a href="https://www.bluetooth.org/en-us/specification/adopted-specifications">profiles</a>
|
||
for Low Energy devices. A profile is a specification for how a device works in a
|
||
particular application. Note that a device can implement more than one profile.
|
||
For example, a device could contain a heart rate monitor and a battery level
|
||
detector.</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Attribute Protocol (ATT)</strong>—GATT is built on top of
|
||
the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is
|
||
optimized to run on BLE devices. To this end, it uses as few bytes as possible.
|
||
Each attribute is uniquely identified by a Universally Unique Identifier (UUID),
|
||
which is a standardized 128-bit format for a string ID used to uniquely
|
||
identify information. The <em>attributes</em> transported by ATT are formatted
|
||
as <em>characteristics</em> and <em>services</em>. </li>
|
||
|
||
<li><strong>Characteristic</strong>—A characteristic contains a single
|
||
value and 0-n descriptors that describe the characteristic's value. A
|
||
characteristic can be thought of as a type, analogous to a class. </li>
|
||
<li><strong>Descriptor</strong>—Descriptors are defined attributes that
|
||
describe a characteristic value. For example, a descriptor might specify a
|
||
human-readable description, an acceptable range for a characteristic's value, or
|
||
a unit of measure that is specific to a characteristic's value.</li>
|
||
|
||
<li><strong>Service</strong>—A service is a collection of
|
||
characteristics. For example, you could have a service called
|
||
"Heart Rate Monitor" that includes characteristics such as
|
||
"heart rate measurement." You can find a list of existing GATT-based
|
||
profiles and services on
|
||
<a href="https://www.bluetooth.org/en-us/specification/adopted-specifications">
|
||
bluetooth.org</a>.</li>
|
||
|
||
</ul>
|
||
|
||
<h3 id="roles">Roles and Responsibilities</h3>
|
||
|
||
<p>Here are the roles and responsibilities that apply when
|
||
an Android device interacts with a BLE device:</p>
|
||
|
||
<ul>
|
||
<li>Central vs. peripheral. This applies to the BLE connection itself. The
|
||
device in the central role scans, looking for advertisement, and the device in
|
||
the peripheral role makes the advertisement.</li>
|
||
<li>GATT server vs. GATT client. This determines how two devices talk to each
|
||
other once they've established the connection.</li>
|
||
</ul>
|
||
|
||
<p>To understand the distinction, imagine that you have an Android phone and
|
||
an activity tracker that is a BLE device. The phone supports the
|
||
central role; the activity tracker supports the peripheral role (to
|
||
establish a BLE connection you need one of each—two things that only support
|
||
peripheral couldn't talk to each other, nor could two things that only support
|
||
central).</p>
|
||
|
||
<p>Once the phone and the activity tracker have established a connection, they
|
||
start transferring GATT metadata to one another. Depending on the kind of data they transfer,
|
||
one or the other might act as the server. For example, if the activity tracker
|
||
wants to report sensor data to the phone, it might make sense for the activity
|
||
tracker to act as the server. If the activity tracker wants to receive updates
|
||
from the phone, then it might make sense for the phone to act
|
||
as the server.</p>
|
||
|
||
<p>
|
||
In the example used in this document, the Android app (running on an Android
|
||
device) is the GATT client. The app gets data from the GATT server, which is a
|
||
BLE heart rate monitor that supports the
|
||
<a href="http://developer.bluetooth.org/TechnologyOverview/Pages/HRP.aspx">Heart
|
||
Rate Profile</a>. But you could alternatively design
|
||
your Android app to play the GATT server
|
||
role. See {@link android.bluetooth.BluetoothGattServer} for more
|
||
information.</p>
|
||
|
||
<h2 id="permissions">BLE Permissions</h2>
|
||
|
||
<p>In order to use Bluetooth features in your application, you must declare
|
||
the Bluetooth permission {@link android.Manifest.permission#BLUETOOTH}.
|
||
You need this permission to perform any Bluetooth communication,
|
||
such as requesting a connection, accepting a connection, and transferring data.</p>
|
||
|
||
<p>If you want your app to initiate device discovery or manipulate Bluetooth
|
||
settings, you must also declare the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
|
||
permission. <strong>Note:</strong> If you use the
|
||
{@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then you must
|
||
also have the {@link android.Manifest.permission#BLUETOOTH} permission.</p>
|
||
|
||
<p>Declare the Bluetooth permission(s) in your application manifest file. For
|
||
example:</p>
|
||
|
||
<pre>
|
||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/></pre>
|
||
|
||
<p>If you want to declare that your app is available to BLE-capable devices only,
|
||
include the following in your app's manifest:</p>
|
||
|
||
<pre><uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||
</pre>
|
||
|
||
<p>However, if you want to make your app available to devices that don't support BLE,
|
||
you should still include this element in your app's manifest, but set {@code required="false"}.
|
||
Then at run-time you can determine BLE availability by using
|
||
{@link android.content.pm.PackageManager#hasSystemFeature PackageManager.hasSystemFeature()}:
|
||
|
||
<pre>// Use this check to determine whether BLE is supported on the device. Then
|
||
// you can selectively disable BLE-related features.
|
||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
|
||
finish();
|
||
}</pre>
|
||
|
||
<h2 id="setup">Setting Up BLE</h2>
|
||
|
||
<p>Before your application can communicate over BLE, you need
|
||
to verify that BLE is supported on the device, and if so, ensure that it is enabled.
|
||
Note that this check is only necessary if {@code <uses-feature.../>}
|
||
is set to false.</p>
|
||
|
||
<p>If BLE is not supported, then you should gracefully disable any
|
||
BLE features. If BLE is supported, but disabled, then you can request that the
|
||
user enable Bluetooth without leaving your application. This setup is
|
||
accomplished in two steps, using the {@link android.bluetooth.BluetoothAdapter}.
|
||
</p>
|
||
|
||
|
||
<ol>
|
||
<li>Get the {@link android.bluetooth.BluetoothAdapter}
|
||
<p>The {@link android.bluetooth.BluetoothAdapter} is required for any and all
|
||
Bluetooth activity. The {@link android.bluetooth.BluetoothAdapter} represents
|
||
the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth
|
||
adapter for the entire system, and your application can interact with it using
|
||
this object. The snippet below shows how to get the adapter. Note that this approach
|
||
uses {@link android.content.Context#getSystemService getSystemService()} to return
|
||
an instance of {@link android.bluetooth.BluetoothManager}, which is then
|
||
used to get the adapter. Android 4.3 (API Level 18) introduces
|
||
{@link android.bluetooth.BluetoothManager}:</p>
|
||
|
||
<pre>// Initializes Bluetooth adapter.
|
||
final BluetoothManager bluetoothManager =
|
||
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
|
||
mBluetoothAdapter = bluetoothManager.getAdapter();
|
||
</pre>
|
||
</li>
|
||
|
||
<li>Enable Bluetooth
|
||
<p>Next, you need to ensure that Bluetooth is enabled. Call {@link
|
||
android.bluetooth.BluetoothAdapter#isEnabled()} to check whether Bluetooth is
|
||
currently enabled. If this method returns false, then Bluetooth is disabled.
|
||
The following snippet checks whether Bluetooth is enabled. If it isn't, the
|
||
snippet displays an error prompting the user to go to Settings to enable
|
||
Bluetooth:</p>
|
||
<pre>private BluetoothAdapter mBluetoothAdapter;
|
||
...
|
||
// Ensures Bluetooth is available on the device and it is enabled. If not,
|
||
// displays a dialog requesting user permission to enable Bluetooth.
|
||
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
|
||
}
|
||
</li>
|
||
</ol>
|
||
|
||
|
||
|
||
<h2 id="find">Finding BLE Devices</h2>
|
||
|
||
<p>To find BLE devices, you use the
|
||
{@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} method.
|
||
This method takes a {@link android.bluetooth.BluetoothAdapter.LeScanCallback}
|
||
as a parameter. You must implement this callback, because that is how scan
|
||
results are returned. Because scanning is battery-intensive, you should observe
|
||
the following guidelines:</p>
|
||
<ul>
|
||
<li>As soon as you find the desired device, stop scanning.</li>
|
||
<li>Never scan on a loop, and set a time limit on your scan. A device that was
|
||
previously available may have moved out of range, and continuing to scan drains
|
||
the battery.</li>
|
||
</ul>
|
||
|
||
<p>The following snippet shows how to start and stop a scan:</p>
|
||
|
||
<pre>/**
|
||
* Activity for scanning and displaying available BLE devices.
|
||
*/
|
||
public class DeviceScanActivity extends ListActivity {
|
||
|
||
private BluetoothAdapter mBluetoothAdapter;
|
||
private boolean mScanning;
|
||
private Handler mHandler;
|
||
|
||
// Stops scanning after 10 seconds.
|
||
private static final long SCAN_PERIOD = 10000;
|
||
...
|
||
private void scanLeDevice(final boolean enable) {
|
||
if (enable) {
|
||
// Stops scanning after a pre-defined scan period.
|
||
mHandler.postDelayed(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
mScanning = false;
|
||
mBluetoothAdapter.stopLeScan(mLeScanCallback);
|
||
}
|
||
}, SCAN_PERIOD);
|
||
|
||
mScanning = true;
|
||
mBluetoothAdapter.startLeScan(mLeScanCallback);
|
||
} else {
|
||
mScanning = false;
|
||
mBluetoothAdapter.stopLeScan(mLeScanCallback);
|
||
}
|
||
...
|
||
}
|
||
...
|
||
}
|
||
</pre>
|
||
|
||
<p>If you want to scan for only specific types of peripherals, you can instead
|
||
call {@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan(UUID[], BluetoothAdapter.LeScanCallback)},
|
||
providing an array of {@link java.util.UUID} objects that specify the GATT
|
||
services your app supports.</p>
|
||
|
||
<p>Here is an implementation of the
|
||
{@link android.bluetooth.BluetoothAdapter.LeScanCallback},
|
||
which is the interface used to deliver BLE scan results:</p>
|
||
|
||
<pre>
|
||
private LeDeviceListAdapter mLeDeviceListAdapter;
|
||
...
|
||
// Device scan callback.
|
||
private BluetoothAdapter.LeScanCallback mLeScanCallback =
|
||
new BluetoothAdapter.LeScanCallback() {
|
||
@Override
|
||
public void onLeScan(final BluetoothDevice device, int rssi,
|
||
byte[] scanRecord) {
|
||
runOnUiThread(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
mLeDeviceListAdapter.addDevice(device);
|
||
mLeDeviceListAdapter.notifyDataSetChanged();
|
||
}
|
||
});
|
||
}
|
||
};</pre>
|
||
|
||
|
||
<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices
|
||
<em>or</em> scan for Classic Bluetooth devices, as described in
|
||
<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a>. You cannot
|
||
scan for both Bluetooth LE and classic devices at the same time.</p>
|
||
|
||
<h2 id="connect">Connecting to a GATT Server</h2>
|
||
|
||
<p>The first step in interacting with a BLE device is connecting to it—
|
||
more specifically, connecting to the GATT server on the device. To
|
||
connect to a GATT server on a BLE device, you use the
|
||
{@link android.bluetooth.BluetoothDevice#connectGatt connectGatt()} method.
|
||
This method takes three parameters: a {@link android.content.Context} object,
|
||
<code>autoConnect</code> (boolean indicating whether to automatically connect to
|
||
the BLE device as soon as it becomes available), and a reference to a
|
||
{@link android.bluetooth.BluetoothGattCallback}: </p>
|
||
|
||
<pre>mBluetoothGatt = device.connectGatt(this, false, mGattCallback);</pre>
|
||
|
||
<p>This connects to the GATT server hosted by the BLE device, and returns a
|
||
{@link android.bluetooth.BluetoothGatt} instance, which you can then use to
|
||
conduct GATT client operations. The caller (the Android app) is the GATT client.
|
||
The {@link android.bluetooth.BluetoothGattCallback} is used to deliver results
|
||
to the client, such as connection status, as well as any further GATT client
|
||
operations.</p>
|
||
|
||
<p>In this example, the BLE app provides an activity
|
||
(<code>DeviceControlActivity</code>) to connect,
|
||
display data, and display GATT services and characteristics
|
||
supported by the device. Based on user input, this activity communicates with a
|
||
{@link android.app.Service} called {@code BluetoothLeService},
|
||
which interacts with the BLE device via the Android BLE API:</p>
|
||
|
||
<pre>
|
||
// A service that interacts with the BLE device via the Android BLE API.
|
||
public class BluetoothLeService extends Service {
|
||
private final static String TAG = BluetoothLeService.class.getSimpleName();
|
||
|
||
private BluetoothManager mBluetoothManager;
|
||
private BluetoothAdapter mBluetoothAdapter;
|
||
private String mBluetoothDeviceAddress;
|
||
private BluetoothGatt mBluetoothGatt;
|
||
private int mConnectionState = STATE_DISCONNECTED;
|
||
|
||
private static final int STATE_DISCONNECTED = 0;
|
||
private static final int STATE_CONNECTING = 1;
|
||
private static final int STATE_CONNECTED = 2;
|
||
|
||
public final static String ACTION_GATT_CONNECTED =
|
||
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
|
||
public final static String ACTION_GATT_DISCONNECTED =
|
||
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
|
||
public final static String ACTION_GATT_SERVICES_DISCOVERED =
|
||
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
|
||
public final static String ACTION_DATA_AVAILABLE =
|
||
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
|
||
public final static String EXTRA_DATA =
|
||
"com.example.bluetooth.le.EXTRA_DATA";
|
||
|
||
public final static UUID UUID_HEART_RATE_MEASUREMENT =
|
||
UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
|
||
|
||
// Various callback methods defined by the BLE API.
|
||
private final BluetoothGattCallback mGattCallback =
|
||
new BluetoothGattCallback() {
|
||
@Override
|
||
public void onConnectionStateChange(BluetoothGatt gatt, int status,
|
||
int newState) {
|
||
String intentAction;
|
||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||
intentAction = ACTION_GATT_CONNECTED;
|
||
mConnectionState = STATE_CONNECTED;
|
||
broadcastUpdate(intentAction);
|
||
Log.i(TAG, "Connected to GATT server.");
|
||
Log.i(TAG, "Attempting to start service discovery:" +
|
||
mBluetoothGatt.discoverServices());
|
||
|
||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||
intentAction = ACTION_GATT_DISCONNECTED;
|
||
mConnectionState = STATE_DISCONNECTED;
|
||
Log.i(TAG, "Disconnected from GATT server.");
|
||
broadcastUpdate(intentAction);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
// New services discovered
|
||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
|
||
} else {
|
||
Log.w(TAG, "onServicesDiscovered received: " + status);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
// Result of a characteristic read operation
|
||
public void onCharacteristicRead(BluetoothGatt gatt,
|
||
BluetoothGattCharacteristic characteristic,
|
||
int status) {
|
||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
|
||
}
|
||
}
|
||
...
|
||
};
|
||
...
|
||
}</pre>
|
||
|
||
<p>When a particular callback is triggered, it calls the appropriate
|
||
{@code broadcastUpdate()} helper method and passes it an action. Note that the data
|
||
parsing in this section is performed in accordance with the Bluetooth Heart Rate
|
||
Measurement
|
||
<a href="http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml">
|
||
profile specifications</a>:</p>
|
||
|
||
<pre>private void broadcastUpdate(final String action) {
|
||
final Intent intent = new Intent(action);
|
||
sendBroadcast(intent);
|
||
}
|
||
|
||
private void broadcastUpdate(final String action,
|
||
final BluetoothGattCharacteristic characteristic) {
|
||
final Intent intent = new Intent(action);
|
||
|
||
// This is special handling for the Heart Rate Measurement profile. Data
|
||
// parsing is carried out as per profile specifications.
|
||
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
|
||
int flag = characteristic.getProperties();
|
||
int format = -1;
|
||
if ((flag & 0x01) != 0) {
|
||
format = BluetoothGattCharacteristic.FORMAT_UINT16;
|
||
Log.d(TAG, "Heart rate format UINT16.");
|
||
} else {
|
||
format = BluetoothGattCharacteristic.FORMAT_UINT8;
|
||
Log.d(TAG, "Heart rate format UINT8.");
|
||
}
|
||
final int heartRate = characteristic.getIntValue(format, 1);
|
||
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
|
||
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
|
||
} else {
|
||
// For all other profiles, writes the data formatted in HEX.
|
||
final byte[] data = characteristic.getValue();
|
||
if (data != null && data.length > 0) {
|
||
final StringBuilder stringBuilder = new StringBuilder(data.length);
|
||
for(byte byteChar : data)
|
||
stringBuilder.append(String.format("%02X ", byteChar));
|
||
intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
|
||
stringBuilder.toString());
|
||
}
|
||
}
|
||
sendBroadcast(intent);
|
||
}</pre>
|
||
|
||
|
||
|
||
<p>Back in <code>DeviceControlActivity</code>, these events are handled by a
|
||
{@link android.content.BroadcastReceiver}:</p>
|
||
|
||
<pre>// Handles various events fired by the Service.
|
||
// ACTION_GATT_CONNECTED: connected to a GATT server.
|
||
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
|
||
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
|
||
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
|
||
// result of read or notification operations.
|
||
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
|
||
@Override
|
||
public void onReceive(Context context, Intent intent) {
|
||
final String action = intent.getAction();
|
||
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
|
||
mConnected = true;
|
||
updateConnectionState(R.string.connected);
|
||
invalidateOptionsMenu();
|
||
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
|
||
mConnected = false;
|
||
updateConnectionState(R.string.disconnected);
|
||
invalidateOptionsMenu();
|
||
clearUI();
|
||
} else if (BluetoothLeService.
|
||
ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
|
||
// Show all the supported services and characteristics on the
|
||
// user interface.
|
||
displayGattServices(mBluetoothLeService.getSupportedGattServices());
|
||
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
|
||
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
|
||
}
|
||
}
|
||
};
|
||
</pre>
|
||
|
||
<h2 id="read">Reading BLE Attributes</h2>
|
||
|
||
<p>Once your Android app has connected to a GATT server and discovered services,
|
||
it can read and write attributes, where supported. For example, this snippet iterates
|
||
through the server's services and characteristics and displays them in the UI:</p>
|
||
|
||
<pre>
|
||
public class DeviceControlActivity extends Activity {
|
||
...
|
||
// Demonstrates how to iterate through the supported GATT
|
||
// Services/Characteristics.
|
||
// In this sample, we populate the data structure that is bound to the
|
||
// ExpandableListView on the UI.
|
||
private void displayGattServices(List<BluetoothGattService> gattServices) {
|
||
if (gattServices == null) return;
|
||
String uuid = null;
|
||
String unknownServiceString = getResources().
|
||
getString(R.string.unknown_service);
|
||
String unknownCharaString = getResources().
|
||
getString(R.string.unknown_characteristic);
|
||
ArrayList<HashMap<String, String>> gattServiceData =
|
||
new ArrayList<HashMap<String, String>>();
|
||
ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
|
||
= new ArrayList<ArrayList<HashMap<String, String>>>();
|
||
mGattCharacteristics =
|
||
new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
|
||
|
||
// Loops through available GATT Services.
|
||
for (BluetoothGattService gattService : gattServices) {
|
||
HashMap<String, String> currentServiceData =
|
||
new HashMap<String, String>();
|
||
uuid = gattService.getUuid().toString();
|
||
currentServiceData.put(
|
||
LIST_NAME, SampleGattAttributes.
|
||
lookup(uuid, unknownServiceString));
|
||
currentServiceData.put(LIST_UUID, uuid);
|
||
gattServiceData.add(currentServiceData);
|
||
|
||
ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
|
||
new ArrayList<HashMap<String, String>>();
|
||
List<BluetoothGattCharacteristic> gattCharacteristics =
|
||
gattService.getCharacteristics();
|
||
ArrayList<BluetoothGattCharacteristic> charas =
|
||
new ArrayList<BluetoothGattCharacteristic>();
|
||
// Loops through available Characteristics.
|
||
for (BluetoothGattCharacteristic gattCharacteristic :
|
||
gattCharacteristics) {
|
||
charas.add(gattCharacteristic);
|
||
HashMap<String, String> currentCharaData =
|
||
new HashMap<String, String>();
|
||
uuid = gattCharacteristic.getUuid().toString();
|
||
currentCharaData.put(
|
||
LIST_NAME, SampleGattAttributes.lookup(uuid,
|
||
unknownCharaString));
|
||
currentCharaData.put(LIST_UUID, uuid);
|
||
gattCharacteristicGroupData.add(currentCharaData);
|
||
}
|
||
mGattCharacteristics.add(charas);
|
||
gattCharacteristicData.add(gattCharacteristicGroupData);
|
||
}
|
||
...
|
||
}
|
||
...
|
||
}</pre>
|
||
|
||
<h2 id="notification">Receiving GATT Notifications</h2>
|
||
|
||
<p>It's common for BLE apps to ask to be notified when a particular
|
||
characteristic changes on the device. This snippet shows how to set a notification
|
||
for a characteristic, using the
|
||
{@link android.bluetooth.BluetoothGatt#setCharacteristicNotification setCharacteristicNotification()}
|
||
method:</p>
|
||
|
||
<pre>
|
||
private BluetoothGatt mBluetoothGatt;
|
||
BluetoothGattCharacteristic characteristic;
|
||
boolean enabled;
|
||
...
|
||
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
|
||
...
|
||
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
|
||
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
|
||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||
mBluetoothGatt.writeDescriptor(descriptor);</pre>
|
||
|
||
<p>Once notifications are enabled for a characteristic,
|
||
an {@link android.bluetooth.BluetoothGattCallback#onCharacteristicChanged onCharacteristicChanged()}
|
||
callback is triggered if the characteristic changes on the remote device:</p>
|
||
|
||
<pre>@Override
|
||
// Characteristic notification
|
||
public void onCharacteristicChanged(BluetoothGatt gatt,
|
||
BluetoothGattCharacteristic characteristic) {
|
||
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
|
||
}
|
||
</pre>
|
||
|
||
<h2 id="close">Closing the Client App</h2>
|
||
|
||
<p>Once your app has finished using a BLE device, it should call
|
||
{@link android.bluetooth.BluetoothGatt#close close()}
|
||
so the system can release resources appropriately:</p>
|
||
|
||
<pre>public void close() {
|
||
if (mBluetoothGatt == null) {
|
||
return;
|
||
}
|
||
mBluetoothGatt.close();
|
||
mBluetoothGatt = null;
|
||
}</pre>
|