blob: 746c8949dca447bdb37999923cec9fa1f96797c1 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.bluetooth;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.android.cts.verifier.R;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class BleClientService extends Service {
public static final boolean DEBUG = true;
public static final String TAG = "BleClientService";
// Android N (2016 July 15, currently) BluetoothGatt#disconnect() does not work correct.
// (termination signal will not be sent)
// This flag switches to turn Bluetooth off instead of BluetoothGatt#disconnect().
// If true, test will turn Bluetooth off. Otherwise, will call BluetoothGatt#disconnect().
public static final boolean DISCONNECT_BY_TURN_BT_OFF_ON = (Build.VERSION.SDK_INT > Build.VERSION_CODES.M);
// for Version 1 test
// private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_AUTO;
// for Version 2 test
private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_LE;
public static final int COMMAND_CONNECT = 0;
public static final int COMMAND_DISCONNECT = 1;
public static final int COMMAND_DISCOVER_SERVICE = 2;
public static final int COMMAND_READ_RSSI = 3;
public static final int COMMAND_WRITE_CHARACTERISTIC = 4;
public static final int COMMAND_WRITE_CHARACTERISTIC_BAD_RESP = 5;
public static final int COMMAND_READ_CHARACTERISTIC = 6;
public static final int COMMAND_WRITE_DESCRIPTOR = 7;
public static final int COMMAND_READ_DESCRIPTOR = 8;
public static final int COMMAND_SET_NOTIFICATION = 9;
public static final int COMMAND_BEGIN_WRITE = 10;
public static final int COMMAND_EXECUTE_WRITE = 11;
public static final int COMMAND_ABORT_RELIABLE = 12;
public static final int COMMAND_SCAN_START = 13;
public static final int COMMAND_SCAN_STOP = 14;
public static final String BLE_BLUETOOTH_MISMATCH_SECURE =
"com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE";
public static final String BLE_BLUETOOTH_MISMATCH_INSECURE =
"com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE";
public static final String BLE_BLUETOOTH_DISABLED =
"com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED";
public static final String BLE_BLUETOOTH_CONNECTED =
"com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_CONNECTED";
public static final String BLE_BLUETOOTH_DISCONNECTED =
"com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISCONNECTED";
public static final String BLE_SERVICES_DISCOVERED =
"com.android.cts.verifier.bluetooth.BLE_SERVICES_DISCOVERED";
public static final String BLE_MTU_CHANGED_23BYTES =
"com.android.cts.verifier.bluetooth.BLE_MTU_CHANGED_23BYTES";
public static final String BLE_MTU_CHANGED_512BYTES =
"com.android.cts.verifier.bluetooth.BLE_MTU_CHANGED_512BYTES";
public static final String BLE_CHARACTERISTIC_READ =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ";
public static final String BLE_CHARACTERISTIC_WRITE =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE";
public static final String BLE_CHARACTERISTIC_CHANGED =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_CHANGED";
public static final String BLE_CHARACTERISTIC_INDICATED =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_INDICATED";
public static final String BLE_DESCRIPTOR_READ =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ";
public static final String BLE_DESCRIPTOR_WRITE =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE";
public static final String BLE_RELIABLE_WRITE_COMPLETED =
"com.android.cts.verifier.bluetooth.BLE_RELIABLE_WRITE_COMPLETED";
public static final String BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED =
"com.android.cts.verifier.bluetooth.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED";
public static final String BLE_READ_REMOTE_RSSI =
"com.android.cts.verifier.bluetooth.BLE_READ_REMOTE_RSSI";
public static final String BLE_PHY_READ =
"com.android.cts.verifier.bluetooth.BLE_PHY_READ";
public static final String BLE_ON_SERVICE_CHANGED =
"com.android.cts.verifier.bluetooth.BLE_ON_SERVICE_CHANGED";
public static final String BLE_CHARACTERISTIC_READ_NOPERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_NOPERMISSION";
public static final String BLE_CHARACTERISTIC_WRITE_NOPERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE_NOPERMISSION";
public static final String BLE_DESCRIPTOR_READ_NOPERMISSION =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ_NOPERMISSION";
public static final String BLE_DESCRIPTOR_WRITE_NOPERMISSION =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE_NOPERMISSION";
public static final String BLE_CHARACTERISTIC_READ_NEED_ENCRYPTED =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_NEED_ENCRYPTED";
public static final String BLE_CHARACTERISTIC_WRITE_NEED_ENCRYPTED =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE_NEED_ENCRYPTED";
public static final String BLE_DESCRIPTOR_READ_NEED_ENCRYPTED =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ_NEED_ENCRYPTED";
public static final String BLE_DESCRIPTOR_WRITE_NEED_ENCRYPTED =
"com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE_NEED_ENCRYPTED";
public static final String BLE_CLIENT_ERROR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ERROR";
public static final String EXTRA_COMMAND =
"com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
public static final String EXTRA_WRITE_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_WRITE_VALUE";
public static final String EXTRA_BOOL =
"com.android.cts.verifier.bluetooth.EXTRA_BOOL";
// Literal for Client Action
public static final String BLE_CLIENT_ACTION_CLIENT_CONNECT =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_CONNECT";
public static final String BLE_CLIENT_ACTION_CLIENT_CONNECT_SECURE =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_CONNECT_SECURE";
public static final String BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE";
public static final String BLE_CLIENT_ACTION_REQUEST_MTU_23 =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_REQUEST_MTU_23";
public static final String BLE_CLIENT_ACTION_REQUEST_MTU_512 =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_REQUEST_MTU_512";
public static final String BLE_CLIENT_ACTION_READ_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_RELIABLE_WRITE =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_RELIABLE_WRITE";
public static final String BLE_CLIENT_ACTION_RELIABLE_WRITE_BAD_RESP =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_RELIABLE_WRITE_BAD_RESP";
public static final String BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_READ_DESCRIPTOR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_DESCRIPTOR";
public static final String BLE_CLIENT_ACTION_WRITE_DESCRIPTOR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR";
public static final String BLE_CLIENT_ACTION_READ_RSSI =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_RSSI";
public static final String BLE_CLIENT_ACTION_READ_PHY =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_PHY";
public static final String BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED";
public static final String BLE_CLIENT_ACTION_CLIENT_DISCONNECT =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_DISCONNECT";
public static final String BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_READ_AUTHENTICATED_DESCRIPTOR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_AUTHENTICATED_DESCRIPTOR";
public static final String BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_DESCRIPTOR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_DESCRIPTOR";
public static final String EXTRA_CHARACTERISTIC_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_CHARACTERISTIC_VALUE";
public static final String EXTRA_DESCRIPTOR_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_DESCRIPTOR_VALUE";
public static final String EXTRA_RSSI_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_RSSI_VALUE";
public static final String EXTRA_TX_PHY_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_TX_PHY_VALUE";
public static final String EXTRA_RX_PHY_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_RX_PHY_VALUE";
public static final String EXTRA_ERROR_MESSAGE =
"com.android.cts.verifier.bluetooth.EXTRA_ERROR_MESSAGE";
public static final String WRITE_VALUE_512BYTES_FOR_MTU = createTestData(512);
public static final String WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE = createTestData(507);
private static final UUID SERVICE_UUID =
UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_UUID =
UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_RESULT_UUID =
UUID.fromString("00009974-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID =
UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_UUID =
UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
private static final UUID SERVICE_UUID_ADDITIONAL =
UUID.fromString("00009995-0000-1000-8000-00805f9b34fb");
// Literal for registration permission of Characteristic
private static final UUID CHARACTERISTIC_NO_READ_UUID =
UUID.fromString("00009984-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_NO_WRITE_UUID =
UUID.fromString("00009983-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID =
UUID.fromString("00009982-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID =
UUID.fromString("00009981-0000-1000-8000-00805f9b34fb");
// Literal for registration permission of Descriptor
private static final UUID DESCRIPTOR_NO_READ_UUID =
UUID.fromString("00009973-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_NO_WRITE_UUID =
UUID.fromString("00009972-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_NEED_ENCRYPTED_READ_UUID =
UUID.fromString("00009969-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID =
UUID.fromString("00009968-0000-1000-8000-00805f9b34fb");
// Literal for registration upper limit confirmation of Characteristic
private static final UUID UPDATE_CHARACTERISTIC_UUID_1 =
UUID.fromString("00009989-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_2 =
UUID.fromString("00009988-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_3 =
UUID.fromString("00009987-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_4 =
UUID.fromString("00009986-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_5 =
UUID.fromString("00009985-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_6 =
UUID.fromString("00009979-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_7 =
UUID.fromString("00009978-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_8 =
UUID.fromString("00009977-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_9 =
UUID.fromString("00009976-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_10 =
UUID.fromString("00009975-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_11 =
UUID.fromString("00009959-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_12 =
UUID.fromString("00009958-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_13 =
UUID.fromString("00009957-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_14 =
UUID.fromString("00009956-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_CHARACTERISTIC_UUID_15 =
UUID.fromString("00009955-0000-1000-8000-00805f9b34fb");
private static final UUID SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID =
UUID.fromString("00009949-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_DESCRIPTOR_UUID =
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static final UUID INDICATE_CHARACTERISTIC_UUID =
UUID.fromString("00009971-0000-1000-8000-00805f9b34fb");
public static final String WRITE_VALUE = "CLIENT_TEST";
private static final String NOTIFY_VALUE = "NOTIFY_TEST";
public static final String WRITE_VALUE_BAD_RESP = "BAD_RESP_TEST";
public static final String SERVICE_CHANGED_VALUE = "CLIENT_SVC_CHG";
// current test category
private String mCurrentAction;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mBluetoothGatt;
private BluetoothLeScanner mScanner;
private Handler mHandler;
private Context mContext;
private boolean mSecure;
private int mNotifyCount;
private boolean mValidityService;
private ReliableWriteState mExecReliableWrite;
private byte[] mBuffer;
// Handler for communicating task with peer.
private TestTaskQueue mTaskQueue;
// Lock for synchronization during notification request test.
private final Object mRequestNotificationLock = new Object();
// Lock for triggering service changed event on server side.
private final Object mServiceChangedLock = new Object();
private static final int SERVICE_CHANGED_FLAG_INIT = 0x00;
private static final int SERVICE_CHANGED_FLAG_TRIGGER_ACTION = 0x01;
private static final int SERVICE_CHANGED_FLAG_ON_SERVICE_CHANGED = 0x02;
private static final int SERVICE_CHANGED_FLAG_ALL = 0x03;
private static final int SERVICE_CHANGED_FLAG_IGNORE = 0xFF;
private int mServiceChangedFlag;
private enum ReliableWriteState {
RELIABLE_WRITE_NONE,
RELIABLE_WRITE_WRITE_1ST_DATA,
RELIABLE_WRITE_WRITE_2ND_DATA,
RELIABLE_WRITE_EXECUTE,
RELIABLE_WRITE_BAD_RESP
}
@Override
public void onCreate() {
super.onCreate();
registerReceiver(mBondStatusReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mHandler = new Handler();
mContext = this;
mNotifyCount = 0;
mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread");
}
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
if (!mBluetoothAdapter.isEnabled()) {
notifyBluetoothDisabled();
} else {
mTaskQueue.addTask(new Runnable() {
@Override
public void run() {
onTestFinish(intent.getAction());
}
}, 1500);
}
return START_NOT_STICKY;
}
private void onTestFinish(String action) {
mCurrentAction = action;
if (mCurrentAction != null) {
switch (mCurrentAction) {
case BLE_CLIENT_ACTION_CLIENT_CONNECT_SECURE:
mSecure = true;
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_NONE;
startScan();
break;
case BLE_CLIENT_ACTION_CLIENT_CONNECT:
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_NONE;
startScan();
break;
case BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE:
if (mBluetoothGatt != null && mBleState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
} else {
showMessage("Bluetooth LE not cnnected.");
}
break;
case BLE_CLIENT_ACTION_REQUEST_MTU_23:
case BLE_CLIENT_ACTION_REQUEST_MTU_512: // fall through
requestMtu();
break;
case BLE_CLIENT_ACTION_READ_CHARACTERISTIC:
readCharacteristic(CHARACTERISTIC_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC:
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_RELIABLE_WRITE:
case BLE_CLIENT_ACTION_RELIABLE_WRITE_BAD_RESP: // fall through
mTaskQueue.addTask(new Runnable() {
@Override
public void run() {
reliableWrite();
}
});
break;
case BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC:
setNotification(INDICATE_CHARACTERISTIC_UUID, true);
break;
case BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC:
// Registered the notify to characteristics in the service
mTaskQueue.addTask(new Runnable() {
@Override
public void run() {
mNotifyCount = 16;
setNotification(UPDATE_CHARACTERISTIC_UUID, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_1, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_2, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_3, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_4, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_5, true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_6, true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_7, true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_8, true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_9, true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_10, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_11, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_12, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_13, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_14, true);
waitForDisableNotificationCompletion();
setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_15, true);
waitForDisableNotificationCompletion();
}
});
break;
case BLE_CLIENT_ACTION_READ_DESCRIPTOR:
readDescriptor(DESCRIPTOR_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_DESCRIPTOR:
writeDescriptor(DESCRIPTOR_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_RSSI:
if (mBluetoothGatt != null) {
mBluetoothGatt.readRemoteRssi();
}
break;
case BLE_CLIENT_ACTION_CLIENT_DISCONNECT:
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
}
break;
case BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION:
readCharacteristic(CHARACTERISTIC_NO_READ_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION:
writeCharacteristic(CHARACTERISTIC_NO_WRITE_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION:
readDescriptor(DESCRIPTOR_NO_READ_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION:
writeDescriptor(DESCRIPTOR_NO_WRITE_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC:
readCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC:
writeCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_AUTHENTICATED_DESCRIPTOR:
readDescriptor(CHARACTERISTIC_RESULT_UUID, DESCRIPTOR_NEED_ENCRYPTED_READ_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_DESCRIPTOR:
writeDescriptor(CHARACTERISTIC_RESULT_UUID, DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_PHY:
if (mBluetoothGatt != null) {
mBluetoothGatt.readPhy();
}
break;
case BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED:
initializeServiceChangedEvent();
writeCharacteristic(SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID, WRITE_VALUE);
break;
}
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
mBluetoothGatt = null;
}
stopScan();
unregisterReceiver(mBondStatusReceiver);
mTaskQueue.quit();
}
public static BluetoothGatt connectGatt(BluetoothDevice device, Context context, boolean autoConnect, boolean isSecure, BluetoothGattCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isSecure) {
if (TRANSPORT_MODE_FOR_SECURE_CONNECTION == BluetoothDevice.TRANSPORT_AUTO) {
Toast.makeText(context, "connectGatt(transport=AUTO)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
}
return device.connectGatt(context, autoConnect, callback, TRANSPORT_MODE_FOR_SECURE_CONNECTION);
} else {
Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
return device.connectGatt(context, autoConnect, callback, BluetoothDevice.TRANSPORT_LE);
}
} else {
Toast.makeText(context, "connectGatt", Toast.LENGTH_SHORT).show();
return device.connectGatt(context, autoConnect, callback);
}
}
private void requestMtu() {
if (mBluetoothGatt != null) {
// MTU request test does not work on Android 6.0 in secure mode.
// (BluetoothDevice#createBond() does not work on Android 6.0.
// So devices can't establish Bluetooth LE pairing.)
boolean mtuTestExecutable = true;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
mtuTestExecutable = !mSecure;
}
if (mtuTestExecutable) {
int mtu = 0;
if (BLE_CLIENT_ACTION_REQUEST_MTU_23.equals(mCurrentAction)) {
mtu = 23;
} else if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction)) {
mtu = 512;
} else {
throw new IllegalStateException("unexpected action: " + mCurrentAction);
}
mBluetoothGatt.requestMtu(mtu);
} else {
showMessage("Skip MTU test.");
notifyMtuChanged();
}
}
}
private void writeCharacteristic(BluetoothGattCharacteristic characteristic, String writeValue) {
if (characteristic != null) {
// Note: setValue() should not be necessary when using writeCharacteristic(byte[]) which
// is added on Android T, but here we call the method in order to make the test
// easier to read. Otherwise, we should verify the written value by calling
// readCharacteristic() but that makes the whole test hard to read.
characteristic.setValue(writeValue);
mBluetoothGatt.writeCharacteristic(characteristic, writeValue.getBytes(),
characteristic.getWriteType());
}
}
private void writeCharacteristic(UUID uid, String writeValue) {
BluetoothGattCharacteristic characteristic = getCharacteristic(uid);
if (characteristic != null){
writeCharacteristic(characteristic, writeValue);
}
}
private void readCharacteristic(UUID uuid) {
BluetoothGattCharacteristic characteristic = getCharacteristic(uuid);
if (characteristic != null) {
mBluetoothGatt.readCharacteristic(characteristic);
}
}
private void writeDescriptor(UUID uid, String writeValue) {
BluetoothGattDescriptor descriptor = getDescriptor(uid);
if (descriptor != null) {
// Note: setValue() should not be necessary when using writeDescriptor(byte[]) which
// is added on Android T, but here we call the method in order to make the test
// easier to read. Otherwise, we should verify the written value by calling
// readDescriptor() but that makes the whole test hard to read.
descriptor.setValue(writeValue.getBytes());
mBluetoothGatt.writeDescriptor(descriptor, writeValue.getBytes());
}
}
private void readDescriptor(UUID uuid) {
BluetoothGattDescriptor descriptor = getDescriptor(uuid);
if (descriptor != null) {
mBluetoothGatt.readDescriptor(descriptor);
}
}
private void writeDescriptor(UUID cuid, UUID duid, String writeValue) {
BluetoothGattDescriptor descriptor = getDescriptor(cuid, duid);
if (descriptor != null) {
// Note: setValue() should not be necessary when using writeDescriptor(byte[]) which
// is added on Android T, but here we call the method in order to make the test
// easier to read. Otherwise, we should verify the written value by calling
// readDescriptor() but that makes the whole test hard to read.
descriptor.setValue(writeValue.getBytes());
mBluetoothGatt.writeDescriptor(descriptor, writeValue.getBytes());
}
}
private void readDescriptor(UUID cuid, UUID duid) {
BluetoothGattDescriptor descriptor = getDescriptor(cuid, duid);
if (descriptor != null) {
mBluetoothGatt.readDescriptor(descriptor);
}
}
private void notifyDisableNotificationCompletion() {
synchronized (mRequestNotificationLock) {
mRequestNotificationLock.notify();
}
}
private void waitForDisableNotificationCompletion() {
synchronized (mRequestNotificationLock) {
try {
mRequestNotificationLock.wait();
} catch (InterruptedException e) {
Log.e(TAG, "Error in waitForDisableNotificationCompletion" + e);
}
}
}
private void initializeServiceChangedEvent() {
synchronized (mServiceChangedLock) {
mServiceChangedFlag = SERVICE_CHANGED_FLAG_INIT;
}
}
private void sendServiceChangedEventIfReady(int flag) {
boolean shouldSend = false;
synchronized (mServiceChangedLock) {
mServiceChangedFlag |= flag;
if (mServiceChangedFlag == SERVICE_CHANGED_FLAG_ALL) {
mServiceChangedFlag |= SERVICE_CHANGED_FLAG_IGNORE;
shouldSend = true;
}
}
if (shouldSend) {
// This is to send result to the connected GATT server.
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
SERVICE_CHANGED_VALUE);
}
}
private void setNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
if (characteristic != null) {
mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UPDATE_DESCRIPTOR_UUID);
if (enable) {
if (characteristic.getUuid().equals(INDICATE_CHARACTERISTIC_UUID)) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
} else {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
}
} else {
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
mBluetoothGatt.writeDescriptor(descriptor);
}
}
private void setNotification(UUID serviceUid, UUID characteristicUid, boolean enable) {
BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUid, characteristicUid);
if (characteristic != null) {
setNotification(characteristic, enable);
}
}
private void setNotification(UUID uid, boolean enable) {
BluetoothGattCharacteristic characteristic = getCharacteristic(uid);
if (characteristic != null) {
setNotification(characteristic, enable);
}
}
private void notifyError(String message) {
showMessage(message);
Log.i(TAG, message);
Intent intent = new Intent(BLE_CLIENT_ERROR);
sendBroadcast(intent);
}
private void notifyMismatchSecure() {
Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE);
sendBroadcast(intent);
}
private void notifyMismatchInsecure() {
Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_INSECURE);
sendBroadcast(intent);
}
private void notifyBluetoothDisabled() {
Intent intent = new Intent(BLE_BLUETOOTH_DISABLED);
sendBroadcast(intent);
}
private void notifyConnected() {
showMessage("Bluetooth LE connected");
Intent intent = new Intent(BLE_BLUETOOTH_CONNECTED);
sendBroadcast(intent);
}
private void notifyDisconnected() {
showMessage("Bluetooth LE disconnected");
Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED);
sendBroadcast(intent);
}
private void notifyServicesDiscovered() {
showMessage("Service discovered");
Intent intent = new Intent(BLE_SERVICES_DISCOVERED);
sendBroadcast(intent);
}
private void notifyMtuChanged() {
Intent intent;
if (BLE_CLIENT_ACTION_REQUEST_MTU_23.equals(mCurrentAction)) {
intent = new Intent(BLE_MTU_CHANGED_23BYTES);
} else if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction)) {
intent = new Intent(BLE_MTU_CHANGED_512BYTES);
} else {
throw new IllegalStateException("unexpected action: " + mCurrentAction);
}
sendBroadcast(intent);
}
private void notifyCharacteristicRead(String value) {
showMessage("Characteristic read: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_READ);
intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
sendBroadcast(intent);
}
private void notifyCharacteristicWrite(String value) {
showMessage("Characteristic write: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE);
sendBroadcast(intent);
}
private void notifyCharacteristicReadNoPermission() {
showMessage("Characteristic not read: No Permission");
Intent intent = new Intent(BLE_CHARACTERISTIC_READ_NOPERMISSION);
sendBroadcast(intent);
}
private void notifyCharacteristicWriteNoPermission(String value) {
showMessage("Characteristic write: No Permission");
Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE_NOPERMISSION);
sendBroadcast(intent);
}
private void notifyCharacteristicReadNeedEncrypted(String value) {
showMessage("Characteristic read with encrypted: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_READ_NEED_ENCRYPTED);
sendBroadcast(intent);
}
private void notifyCharacteristicWriteNeedEncrypted(String value) {
showMessage("Characteristic write with encrypted: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE_NEED_ENCRYPTED);
sendBroadcast(intent);
}
private void notifyCharacteristicChanged() {
showMessage("Characteristic changed");
Intent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);
sendBroadcast(intent);
}
private void notifyCharacteristicIndicated() {
showMessage("Characteristic Indicated");
Intent intent = new Intent(BLE_CHARACTERISTIC_INDICATED);
sendBroadcast(intent);
}
private void notifyDescriptorRead(String value) {
showMessage("Descriptor read: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_READ);
intent.putExtra(EXTRA_DESCRIPTOR_VALUE, value);
sendBroadcast(intent);
}
private void notifyDescriptorWrite(String value) {
showMessage("Descriptor write: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_WRITE);
sendBroadcast(intent);
}
private void notifyDescriptorReadNoPermission() {
showMessage("Descriptor read: No Permission");
Intent intent = new Intent(BLE_DESCRIPTOR_READ_NOPERMISSION);
sendBroadcast(intent);
}
private void notifyDescriptorWriteNoPermission() {
showMessage("Descriptor write: NoPermission");
Intent intent = new Intent(BLE_DESCRIPTOR_WRITE_NOPERMISSION);
sendBroadcast(intent);
}
private void notifyDescriptorReadNeedEncrypted(String value) {
showMessage("Descriptor read with encrypted: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_READ_NEED_ENCRYPTED);
sendBroadcast(intent);
}
private void notifyDescriptorWriteNeedEncrypted(String value) {
showMessage("Descriptor write with encrypted: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_WRITE_NEED_ENCRYPTED);
sendBroadcast(intent);
}
private void notifyReliableWriteCompleted() {
showMessage("Reliable write complete");
Intent intent = new Intent(BLE_RELIABLE_WRITE_COMPLETED);
sendBroadcast(intent);
}
private void notifyReliableWriteBadRespCompleted(String err) {
showMessage("Reliable write(bad response) complete");
Intent intent = new Intent(BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED);
if (err != null) {
intent.putExtra(EXTRA_ERROR_MESSAGE, err);
}
sendBroadcast(intent);
}
private void notifyReadRemoteRssi(int rssi) {
showMessage("Remote rssi read: " + rssi);
Intent intent = new Intent(BLE_READ_REMOTE_RSSI);
intent.putExtra(EXTRA_RSSI_VALUE, rssi);
sendBroadcast(intent);
}
private void notifyPhyRead(int txPhy, int rxPhy) {
showMessage("Phy read: txPhy=" + txPhy + ", rxPhy=" + rxPhy);
Intent intent = new Intent(BLE_PHY_READ);
intent.putExtra(EXTRA_TX_PHY_VALUE, txPhy);
intent.putExtra(EXTRA_RX_PHY_VALUE, rxPhy);
sendBroadcast(intent);
}
private void notifyServiceChanged() {
showMessage("Remote service changed");
Intent intent = new Intent(BLE_ON_SERVICE_CHANGED);
sendBroadcast(intent);
}
private BluetoothGattService getService(UUID serverUid) {
BluetoothGattService service = null;
if (mBluetoothGatt != null) {
service = mBluetoothGatt.getService(serverUid);
if (service == null) {
showMessage("Service not found");
}
}
return service;
}
private BluetoothGattService getService() {
BluetoothGattService service = null;
if (mBluetoothGatt != null) {
service = mBluetoothGatt.getService(SERVICE_UUID);
if (service == null) {
showMessage("Service not found");
}
}
return service;
}
private BluetoothGattCharacteristic getCharacteristic(UUID serverUid, UUID characteristicUid) {
BluetoothGattCharacteristic characteristic = null;
BluetoothGattService service = getService(serverUid);
if (service != null) {
characteristic = service.getCharacteristic(characteristicUid);
if (characteristic == null) {
showMessage("Characteristic not found");
}
}
return characteristic;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattCharacteristic characteristic = null;
BluetoothGattService service = getService();
if (service != null) {
characteristic = service.getCharacteristic(uuid);
if (characteristic == null) {
showMessage("Characteristic not found");
}
}
return characteristic;
}
private BluetoothGattDescriptor getDescriptor(UUID uid) {
BluetoothGattDescriptor descriptor = null;
BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
if (characteristic != null) {
descriptor = characteristic.getDescriptor(uid);
if (descriptor == null) {
showMessage("Descriptor not found");
}
}
return descriptor;
}
private BluetoothGattDescriptor getDescriptor(UUID cuid, UUID duid) {
BluetoothGattDescriptor descriptor = null;
BluetoothGattCharacteristic characteristic = getCharacteristic(cuid);
if (characteristic != null) {
descriptor = characteristic.getDescriptor(duid);
if (descriptor == null) {
showMessage("Descriptor not found");
}
}
return descriptor;
}
private void showMessage(final String msg) {
mHandler.post(new Runnable() {
public void run() {
Toast.makeText(BleClientService.this, msg, Toast.LENGTH_SHORT).show();
}
});
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Log.e(TAG, "Error in thread sleep", e);
}
}
private void reliableWrite() {
// aborting test
mBluetoothGatt.beginReliableWrite();
sleep(1000);
mBluetoothGatt.abortReliableWrite();
// writing test
sleep(2000);
mBluetoothGatt.beginReliableWrite();
sleep(1000);
if (BLE_CLIENT_ACTION_RELIABLE_WRITE.equals(mCurrentAction)) {
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_WRITE_1ST_DATA;
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE);
} else {
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_BAD_RESP;
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_BAD_RESP);
}
}
private int mBleState = BluetoothProfile.STATE_DISCONNECTED;
private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (DEBUG) Log.d(TAG, "onConnectionStateChange: status= " + status + ", newState= " + newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBleState = newState;
int bond = gatt.getDevice().getBondState();
boolean bonded = false;
BluetoothDevice target = gatt.getDevice();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
if (device.getAddress().equals(target.getAddress())) {
bonded = true;
break;
}
}
}
if (mSecure && ((bond == BluetoothDevice.BOND_NONE) || !bonded)) {
// not pairing and execute Secure Test
mBluetoothGatt.disconnect();
notifyMismatchSecure();
} else if (!mSecure && ((bond != BluetoothDevice.BOND_NONE) || bonded)) {
// already pairing nad execute Insecure Test
mBluetoothGatt.disconnect();
notifyMismatchInsecure();
} else {
notifyConnected();
}
} else if (status == BluetoothProfile.STATE_DISCONNECTED) {
mBleState = newState;
mSecure = false;
mBluetoothGatt.close();
notifyDisconnected();
}
} else {
showMessage("Failed to connect: " + status + " , newState = " + newState);
mBluetoothGatt.close();
mBluetoothGatt = null;
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (DEBUG){
Log.d(TAG, "onServiceDiscovered");
}
if ((status == BluetoothGatt.GATT_SUCCESS) && (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
notifyServicesDiscovered();
}
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
if (DEBUG){
Log.d(TAG, "onMtuChanged");
}
if (status == BluetoothGatt.GATT_SUCCESS) {
// verify MTU size
int requestedMtu;
if (BLE_CLIENT_ACTION_REQUEST_MTU_23.equals(mCurrentAction)) {
requestedMtu = 23;
} else if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction)) {
requestedMtu = 512;
} else {
throw new IllegalStateException("unexpected action: " + mCurrentAction);
}
if (mtu != requestedMtu) {
String msg = String.format(getString(R.string.ble_mtu_mismatch_message),
requestedMtu, mtu);
showMessage(msg);
}
// test writing characteristic
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_512BYTES_FOR_MTU);
} else {
notifyError("Failed to request mtu: " + status);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, final int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
String value = characteristic.getStringValue(0);
final UUID uid = characteristic.getUuid();
if (DEBUG) {
Log.d(TAG, "onCharacteristicWrite: characteristic.val=" + value + " status=" + status);
}
if (BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED.equals(mCurrentAction)) {
if (SERVICE_CHANGED_VALUE.equals(value)) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Waits until the GATT server sends the response, we can then do notify.
notifyServiceChanged();
} else {
notifyError("Failed to send result for service changed event");
}
} else {
// The reason not to check the status code is that we know there is a service
// changed event coming later, sometimes the status code will be modified by
// bt stack (133), but it's ok, as long as onServiceChanged is called, we then
// know the request is successfully sent during this test session.
sendServiceChangedEventIfReady(SERVICE_CHANGED_FLAG_TRIGGER_ACTION);
}
} else if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction) ||
BLE_CLIENT_ACTION_REQUEST_MTU_23.equals(mCurrentAction)) {
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyMtuChanged();
} else {
notifyError("Failed to write characteristic: " + status + " : " + uid);
}
} else {
switch (mExecReliableWrite) {
case RELIABLE_WRITE_NONE:
if (status == BluetoothGatt.GATT_SUCCESS) {
if (characteristic.getUuid().equals(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID)) {
notifyCharacteristicWriteNeedEncrypted(value);
} else if (!characteristic.getUuid().equals(CHARACTERISTIC_RESULT_UUID)) {
// verify
if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), characteristic.getValue())) {
notifyCharacteristicWrite(value);
} else {
notifyError("Written data is not correct");
}
}
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
if (uid.equals(CHARACTERISTIC_NO_WRITE_UUID)) {
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.WRITE_NO_PERMISSION);
notifyCharacteristicWriteNoPermission(value);
} else {
notifyError("Not Permission Write: " + status + " : " + uid);
}
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
notifyError("Not Authentication Write: " + status + " : " + uid);
} else {
notifyError("Failed to write characteristic: " + status + " : " + uid);
}
break;
case RELIABLE_WRITE_WRITE_1ST_DATA:
// verify
if (WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE.equals(value)) {
// write next data
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_WRITE_2ND_DATA;
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE);
} else {
notifyError("Failed to write characteristic: " + status + " : " + uid);
}
break;
case RELIABLE_WRITE_WRITE_2ND_DATA:
// verify
if (WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE.equals(value)) {
// execute write
mTaskQueue.addTask(new Runnable() {
@Override
public void run() {
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_EXECUTE;
if (!mBluetoothGatt.executeReliableWrite()) {
notifyError("reliable write failed");
}
}
}, 1000);
} else {
notifyError("Failed to write characteristic: " + status + " : " + uid);
}
break;
case RELIABLE_WRITE_BAD_RESP:
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_NONE;
// verify response
// Server sends empty response for this test.
// Response must be empty.
String err = null;
if (!TextUtils.isEmpty(value)) {
err = "response is not empty";
showMessage(err);
}
// finish reliable write
final String errValue = err;
mTaskQueue.addTask(new Runnable() {
@Override
public void run() {
mBluetoothGatt.abortReliableWrite();
notifyReliableWriteBadRespCompleted(errValue);
}
}, 1000);
break;
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
// Note: Both this method and onCharacteristicRead(byte[]) will be called.
UUID uid = characteristic.getUuid();
if (DEBUG) {
Log.d(TAG, "onCharacteristicRead (deprecated): status=" + status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, byte[] value, int status) {
super.onCharacteristicRead(gatt, characteristic, value, status);
UUID uid = characteristic.getUuid();
if (DEBUG) {
Log.d(TAG, "onCharacteristicRead (memory safe version): status=" + status);
}
if (status == BluetoothGatt.GATT_SUCCESS) {
String stringValue = new String(value);
if (characteristic.getUuid().equals(CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID)) {
notifyCharacteristicReadNeedEncrypted(stringValue);
} else {
// verify
if (BleServerService.WRITE_VALUE.equals(stringValue)) {
notifyCharacteristicRead(stringValue);
} else {
notifyError("Read data is not correct");
}
}
} else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
if (uid.equals(CHARACTERISTIC_NO_READ_UUID)) {
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.READ_NO_PERMISSION);
notifyCharacteristicReadNoPermission();
} else {
notifyError("Not Permission Read: " + status + " : " + uid);
}
} else if(status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
notifyError("Not Authentication Read: " + status + " : " + uid);
} else {
notifyError("Failed to read characteristic: " + status + " : " + uid);
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
if (DEBUG) {
Log.d(TAG, "onDescriptorWrite");
}
UUID uid = descriptor.getUuid();
if ((status == BluetoothGatt.GATT_SUCCESS)) {
if (uid.equals(UPDATE_DESCRIPTOR_UUID)) {
Log.d(TAG, "write in update descriptor.");
if (descriptor.getValue() == BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
notifyDisableNotificationCompletion();
}
} else if (uid.equals(DESCRIPTOR_UUID)) {
// verify
if (Arrays.equals(WRITE_VALUE.getBytes(), descriptor.getValue())) {
notifyDescriptorWrite(new String(descriptor.getValue()));
} else {
notifyError("Written data is not correct");
}
} else if (uid.equals(DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID)) {
notifyDescriptorWriteNeedEncrypted(new String(descriptor.getValue()));
}
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
if (uid.equals(DESCRIPTOR_NO_WRITE_UUID)) {
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.DESCRIPTOR_WRITE_NO_PERMISSION);
notifyDescriptorWriteNoPermission();
} else {
notifyError("Not Permission Write: " + status + " : " + descriptor.getUuid());
}
} else {
notifyError("Failed to write descriptor");
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
// Note: Both this method and onDescriptorRead(byte[]) will be called.
if (DEBUG) {
Log.d(TAG, "onDescriptorRead (deprecated)");
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status, byte[] value) {
super.onDescriptorRead(gatt, descriptor, status, value);
if (DEBUG) {
Log.d(TAG, "onDescriptorRead (memory safe version)");
}
UUID uid = descriptor.getUuid();
String stringValue = new String(value);
if ((status == BluetoothGatt.GATT_SUCCESS)) {
if ((uid != null) && (uid.equals(DESCRIPTOR_UUID))) {
// verify
if (BleServerService.WRITE_VALUE.equals(stringValue)) {
notifyDescriptorRead(stringValue);
} else {
notifyError("Read data is not correct");
}
} else if (uid.equals(DESCRIPTOR_NEED_ENCRYPTED_READ_UUID)) {
notifyDescriptorReadNeedEncrypted(stringValue);
}
} else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
if (uid.equals(DESCRIPTOR_NO_READ_UUID)) {
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.DESCRIPTOR_READ_NO_PERMISSION);
notifyDescriptorReadNoPermission();
} else {
notifyError("Not Permission Read: " + status + " : " + descriptor.getUuid());
}
} else {
notifyError("Failed to read descriptor: " + status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
UUID uid = characteristic.getUuid();
// Note: Both this method and onCharacteristicChanged(byte[]) will be called.
if (DEBUG) {
Log.d(TAG, "onCharacteristicChanged (deprecated): uid=" + uid);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, byte[] value) {
super.onCharacteristicChanged(gatt, characteristic, value);
UUID uid = characteristic.getUuid();
if (DEBUG) {
Log.d(TAG, "onCharacteristicChanged (memory safe version): uid=" + uid);
}
String stringValue = new String(value);
if (uid != null) {
if (uid.equals(INDICATE_CHARACTERISTIC_UUID)
&& BleServerService.INDICATE_VALUE.equals(stringValue)) {
setNotification(characteristic, false);
notifyCharacteristicIndicated();
} else if (BleServerService.NOTIFY_VALUE.equals(stringValue)) {
mNotifyCount--;
setNotification(characteristic, false);
if (mNotifyCount == 0) {
notifyCharacteristicChanged();
}
}
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
if (DEBUG) {
Log.d(TAG, "onReliableWriteComplete: " + status);
}
if (mExecReliableWrite != ReliableWriteState.RELIABLE_WRITE_NONE) {
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyReliableWriteCompleted();
} else {
notifyError("Failed to complete reliable write: " + status);
}
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_NONE;
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
if (DEBUG) {
Log.d(TAG, "onReadRemoteRssi");
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyReadRemoteRssi(rssi);
} else {
notifyError("Failed to read remote rssi");
}
}
@Override
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyRead(gatt, txPhy, rxPhy, status);
if (DEBUG) {
Log.d(TAG, "onPhyRead");
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyPhyRead(txPhy, rxPhy);
} else {
notifyError("Failed to read phy");
}
}
@Override
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
// TODO: Currently this is not called when BluetoothGatt.setPreferredPhy() is called.
// It is because the path is not wired in native code. (acl_legacy_interface.cc)
// Add a proper implementation and related test.
super.onPhyUpdate(gatt, txPhy, rxPhy, status);
if (DEBUG) {
Log.d(TAG, "onPhyUpdate");
}
}
@Override
public void onServiceChanged(BluetoothGatt gatt) {
super.onServiceChanged(gatt);
if (DEBUG) {
Log.d(TAG, "onServiceChanged");
}
if (BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED.equals(mCurrentAction)) {
sendServiceChangedEventIfReady(SERVICE_CHANGED_FLAG_ON_SERVICE_CHANGED);
}
}
};
private final ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
if (mBluetoothGatt == null) {
// verify the validity of the advertisement packet.
mValidityService = false;
List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
for (ParcelUuid uuid : uuids) {
if (uuid.getUuid().equals(BleServerService.ADV_SERVICE_UUID)) {
mValidityService = true;
break;
}
}
if (mValidityService) {
stopScan();
BluetoothDevice device = result.getDevice();
if (mSecure) {
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
if (!device.createBond()) {
notifyError("Failed to call create bond");
}
} else {
mBluetoothGatt = connectGatt(result.getDevice(), mContext, false, mSecure, mGattCallbacks);
}
} else {
mBluetoothGatt = connectGatt(result.getDevice(), mContext, false, mSecure, mGattCallbacks);
}
} else {
notifyError("There is no validity to Advertise servie.");
}
}
}
};
private void startScan() {
if (DEBUG) Log.d(TAG, "startScan");
List<ScanFilter> filter = Arrays.asList(new ScanFilter.Builder().setServiceUuid(
new ParcelUuid(BleServerService.ADV_SERVICE_UUID)).build());
ScanSettings setting = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
mScanner.startScan(filter, setting, mScanCallback);
}
private void stopScan() {
if (DEBUG) Log.d(TAG, "stopScan");
if (mScanner != null) {
mScanner.stopScan(mScanCallback);
}
}
private static String createTestData(int length) {
StringBuilder builder = new StringBuilder();
builder.append("REQUEST_MTU");
int len = length - builder.length();
for (int i = 0; i < len; ++i) {
builder.append(""+(i%10));
}
return builder.toString();
}
private final BroadcastReceiver mBondStatusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
switch (state) {
case BluetoothDevice.BOND_BONDED:
if ((mBluetoothGatt == null) &&
(device.getType() != BluetoothDevice.DEVICE_TYPE_CLASSIC)) {
if (DEBUG) {
Log.d(TAG, "onReceive:BOND_BONDED: calling connectGatt device="
+ device + ", mSecure=" + mSecure);
}
mBluetoothGatt = connectGatt(device, mContext, false, mSecure,
mGattCallbacks);
}
break;
case BluetoothDevice.BOND_NONE:
notifyError("Failed to create bond.");
break;
case BluetoothDevice.BOND_BONDING:
default: // fall through
// wait for next state
break;
}
}
}
};
}