blob: 54e01933af332db05ed256db5541f95ab2d36c83 [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 static android.content.Context.RECEIVER_EXPORTED;
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_PHY_READ_SKIPPED =
"com.android.cts.verifier.bluetooth.BLE_PHY_READ_SKIPPED";
public static final String BLE_PHY_UPDATE =
"com.android.cts.verifier.bluetooth.BLE_PHY_UPDATE";
public static final String BLE_PHY_UPDATE_SKIPPED =
"com.android.cts.verifier.bluetooth.BLE_PHY_UPDATE_SKIPPED";
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_SET_PREFERRED_PHY =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_SET_PREFERRED_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("REQUEST_MTU", 512);
public static final String WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE =
createTestData("RELIABLE_WRITE", 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;
private boolean mWriteAfterMtuChangeDone = false;
// 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),
RECEIVER_EXPORTED);
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:
/* Test the long write before changing MTU.
* Starting from Android U, MTU is set to 517, no matter what
* user request. To keep same test scope, send first 512 write
* before MTU change, so it make use of default MTU = 23.
* Later, requestMtu is called twice and test if MTU Exchange will
* execute only once over the air.
*/
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_512BYTES_FOR_MTU);
break;
case BLE_CLIENT_ACTION_REQUEST_MTU_512: // fall through
/* Call it twice to verify single MTU Exchange procedure over the link */
requestMtu();
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_SET_PREFERRED_PHY:
if (mBluetoothGatt != null) {
mBluetoothGatt.setPreferredPhy(BluetoothDevice.PHY_LE_1M_MASK,
BluetoothDevice.PHY_LE_1M_MASK,
BluetoothDevice.PHY_OPTION_NO_PREFERRED);
}
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();
}
/**
* Connect to GATT Server hosted by this device. Caller acts as GATT client.
* The callback is used to deliver results to Caller, such as connection status as well
* as any further GATT client operations.
* The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
* GATT client operations.
*
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @param autoConnect Whether to directly connect to the remote device (false) or to
* automatically connect as soon as the remote device becomes available (true).
* @param isSecure Whether to use transport mode for secure connection (true or false)
* @throws IllegalArgumentException if callback is null
*/
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) {
if (BLE_CLIENT_ACTION_READ_PHY.equals(mCurrentAction)) {
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);
} else {
Log.d(TAG, "notifyPhyRead arrived without BLE_CLIENT_ACTION_READ_PHY");
}
}
private void notifyPhyReadSkipped() {
showMessage("Phy read not supported. Skipping the test.");
Intent intent = new Intent(BLE_PHY_READ_SKIPPED);
sendBroadcast(intent);
}
private void notifyPhyUpdate(int txPhy, int rxPhy) {
if (BLE_CLIENT_ACTION_SET_PREFERRED_PHY.equals(mCurrentAction)) {
showMessage("Phy update: txPhy=" + txPhy + ", rxPhy=" + rxPhy);
Intent intent = new Intent(BLE_PHY_UPDATE);
intent.putExtra(EXTRA_TX_PHY_VALUE, txPhy);
intent.putExtra(EXTRA_RX_PHY_VALUE, rxPhy);
sendBroadcast(intent);
} else {
Log.d(TAG, "notifyPhyUpdate arrived without BLE_CLIENT_ACTION_SET_PREFERRED_PHY");
}
}
private void notifyPhyUpdateSkipped() {
showMessage("Phy update not supported. Skipping the test.");
Intent intent = new Intent(BLE_PHY_UPDATE_SKIPPED);
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 (BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE.equals(mCurrentAction)) {
if (DEBUG) {
Log.d(TAG, "onServiceDiscovered");
}
if ((status == BluetoothGatt.GATT_SUCCESS)
&& (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
notifyServicesDiscovered();
}
} else {
Log.d(TAG, "onServicesDiscovered without BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE");
}
}
@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 which now is always equal to max 517
int expectedNewMtu = 517;
if (mtu != expectedNewMtu) {
String msg = String.format(getString(R.string.ble_mtu_mismatch_message),
expectedNewMtu, mtu);
showMessage(msg);
}
if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction)
&& !mWriteAfterMtuChangeDone) {
/* Verify write characteristic on bigger MTU */
writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_512BYTES_FOR_MTU);
mWriteAfterMtuChangeDone = true;
}
} 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 (Arrays.equals(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 status=" + status);
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyPhyRead(txPhy, rxPhy);
} else if (status == BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED) {
notifyPhyReadSkipped();
} else {
notifyError("Failed to read phy");
}
}
@Override
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyUpdate(gatt, txPhy, rxPhy, status);
if (DEBUG) {
Log.d(TAG, "onPhyUpdate status=" + status);
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyPhyUpdate(txPhy, rxPhy);
} else if (status == BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED) {
notifyPhyUpdateSkipped();
} else {
notifyError("Failed to update phy");
}
}
@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(String prefix, int length) {
StringBuilder builder = new StringBuilder();
builder.append(prefix);
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;
}
}
}
};
}