1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.bluetooth; 18 19 import java.util.Arrays; 20 import java.util.List; 21 import java.util.Set; 22 import java.util.UUID; 23 24 import java.io.ByteArrayOutputStream; 25 26 import com.android.cts.verifier.R; 27 28 import android.app.Service; 29 import android.bluetooth.BluetoothAdapter; 30 import android.bluetooth.BluetoothDevice; 31 import android.bluetooth.BluetoothGatt; 32 import android.bluetooth.BluetoothGattCallback; 33 import android.bluetooth.BluetoothGattCharacteristic; 34 import android.bluetooth.BluetoothGattDescriptor; 35 import android.bluetooth.BluetoothGattService; 36 import android.bluetooth.BluetoothManager; 37 import android.bluetooth.BluetoothProfile; 38 import android.bluetooth.BluetoothSocket; 39 import android.bluetooth.le.BluetoothLeScanner; 40 import android.bluetooth.le.ScanCallback; 41 import android.bluetooth.le.ScanFilter; 42 import android.bluetooth.le.ScanResult; 43 import android.bluetooth.le.ScanSettings; 44 import android.content.BroadcastReceiver; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.content.IntentFilter; 48 import android.os.Build; 49 import android.os.Handler; 50 import android.os.IBinder; 51 import android.os.Message; 52 import android.os.ParcelUuid; 53 import android.text.TextUtils; 54 import android.util.Log; 55 import android.widget.Toast; 56 57 public class BleCocClientService extends Service { 58 59 public static final boolean DEBUG = true; 60 public static final String TAG = "BleCocClientService"; 61 62 private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_LE; 63 64 public static final String BLE_LE_CONNECTED = 65 "com.android.cts.verifier.bluetooth.BLE_LE_CONNECTED"; 66 public static final String BLE_GOT_PSM = 67 "com.android.cts.verifier.bluetooth.BLE_GOT_PSM"; 68 public static final String BLE_COC_CONNECTED = 69 "com.android.cts.verifier.bluetooth.BLE_COC_CONNECTED"; 70 public static final String BLE_CONNECTION_TYPE_CHECKED = 71 "com.android.cts.verifier.bluetooth.BLE_CONNECTION_TYPE_CHECKED"; 72 public static final String BLE_DATA_8BYTES_SENT = 73 "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_SENT"; 74 public static final String BLE_DATA_8BYTES_READ = 75 "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_READ"; 76 public static final String BLE_DATA_LARGEBUF_READ = 77 "com.android.cts.verifier.bluetooth.BLE_DATA_LARGEBUF_READ"; 78 public static final String BLE_LE_DISCONNECTED = 79 "com.android.cts.verifier.bluetooth.BLE_LE_DISCONNECTED"; 80 81 public static final String BLE_BLUETOOTH_MISMATCH_SECURE = 82 "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE"; 83 public static final String BLE_BLUETOOTH_MISMATCH_INSECURE = 84 "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE"; 85 public static final String BLE_BLUETOOTH_DISABLED = 86 "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED"; 87 public static final String BLE_GATT_CONNECTED = 88 "com.android.cts.verifier.bluetooth.BLE_GATT_CONNECTED"; 89 public static final String BLE_BLUETOOTH_DISCONNECTED = 90 "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISCONNECTED"; 91 public static final String BLE_CLIENT_ERROR = 92 "com.android.cts.verifier.bluetooth.BLE_CLIENT_ERROR"; 93 public static final String EXTRA_COMMAND = 94 "com.android.cts.verifier.bluetooth.EXTRA_COMMAND"; 95 public static final String EXTRA_WRITE_VALUE = 96 "com.android.cts.verifier.bluetooth.EXTRA_WRITE_VALUE"; 97 public static final String EXTRA_BOOL = 98 "com.android.cts.verifier.bluetooth.EXTRA_BOOL"; 99 100 // Literal for Client Action 101 public static final String BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT = 102 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT"; 103 public static final String BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT = 104 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT"; 105 public static final String BLE_COC_CLIENT_ACTION_GET_PSM = 106 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_GET_PSM"; 107 public static final String BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT = 108 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT"; 109 public static final String BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE = 110 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE"; 111 public static final String BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES = 112 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES"; 113 public static final String BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES = 114 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES"; 115 public static final String BLE_COC_CLIENT_ACTION_EXCHANGE_DATA = 116 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_EXCHANGE_DATA"; 117 public static final String BLE_COC_CLIENT_ACTION_CLIENT_CONNECT = 118 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CLIENT_CONNECT"; 119 public static final String BLE_COC_CLIENT_ACTION_CLIENT_CONNECT_SECURE = 120 "com.android.cts.verifier.bluetooth.BLE_COC_CLIENT_ACTION_CLIENT_CONNECT_SECURE"; 121 public static final String BLE_CLIENT_ACTION_CLIENT_DISCONNECT = 122 "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_DISCONNECT"; 123 124 private static final UUID SERVICE_UUID = 125 UUID.fromString("00009999-0000-1000-8000-00805f9b34fb"); 126 127 /** 128 * UUID of the GATT Read Characteristics for LE_PSM value. 129 */ 130 public static final UUID LE_PSM_CHARACTERISTIC_UUID = 131 UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); 132 133 public static final String WRITE_VALUE = "CLIENT_TEST"; 134 private static final String NOTIFY_VALUE = "NOTIFY_TEST"; 135 private int mBleState = BluetoothProfile.STATE_DISCONNECTED; 136 private static final int EXECUTION_DELAY = 1500; 137 138 // current test category 139 private String mCurrentAction; 140 141 private BluetoothManager mBluetoothManager; 142 private BluetoothAdapter mBluetoothAdapter; 143 private BluetoothDevice mDevice; 144 private BluetoothGatt mBluetoothGatt; 145 private BluetoothLeScanner mScanner; 146 private Handler mHandler; 147 private boolean mSecure; 148 private boolean mValidityService; 149 private int mPsm; 150 private BluetoothChatService mChatService; 151 private int mNextReadExpectedLen = -1; 152 private String mNextReadCompletionIntent; 153 private int mTotalReadLen = 0; 154 private byte mNextReadByte; 155 private int mNextWriteExpectedLen = -1; 156 private String mNextWriteCompletionIntent = null; 157 158 // Handler for communicating task with peer. 159 private TestTaskQueue mTaskQueue; 160 161 @Override onCreate()162 public void onCreate() { 163 super.onCreate(); 164 165 registerReceiver(mBondStatusReceiver, 166 new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)); 167 168 mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 169 mBluetoothAdapter = mBluetoothManager.getAdapter(); 170 mScanner = mBluetoothAdapter.getBluetoothLeScanner(); 171 mHandler = new Handler(); 172 173 mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread"); 174 } 175 176 @Override onStartCommand(final Intent intent, int flags, int startId)177 public int onStartCommand(final Intent intent, int flags, int startId) { 178 if (!mBluetoothAdapter.isEnabled()) { 179 notifyBluetoothDisabled(); 180 } else { 181 mTaskQueue.addTask(new Runnable() { 182 @Override 183 public void run() { 184 onTestFinish(intent.getAction()); 185 } 186 }, EXECUTION_DELAY); 187 } 188 return START_NOT_STICKY; 189 } 190 onTestFinish(String action)191 private void onTestFinish(String action) { 192 mCurrentAction = action; 193 if (mCurrentAction != null) { 194 switch (mCurrentAction) { 195 case BLE_COC_CLIENT_ACTION_LE_INSECURE_CONNECT: 196 mSecure = false; 197 startScan(); 198 break; 199 case BLE_COC_CLIENT_ACTION_LE_SECURE_CONNECT: 200 mSecure = true; 201 startScan(); 202 break; 203 case BLE_COC_CLIENT_ACTION_GET_PSM: 204 startLeDiscovery(); 205 break; 206 case BLE_COC_CLIENT_ACTION_COC_CLIENT_CONNECT: 207 leCocClientConnect(); 208 break; 209 case BLE_COC_CLIENT_ACTION_CHECK_CONNECTION_TYPE: 210 leCheckConnectionType(); 211 break; 212 case BLE_COC_CLIENT_ACTION_SEND_DATA_8BYTES: 213 sendData8bytes(); 214 break; 215 case BLE_COC_CLIENT_ACTION_READ_DATA_8BYTES: 216 readData8bytes(); 217 break; 218 case BLE_COC_CLIENT_ACTION_EXCHANGE_DATA: 219 sendDataLargeBuf(); 220 break; 221 case BLE_CLIENT_ACTION_CLIENT_DISCONNECT: 222 if (mBluetoothGatt != null) { 223 mBluetoothGatt.disconnect(); 224 } 225 if (mChatService != null) { 226 mChatService.stop(); 227 } 228 break; 229 default: 230 Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction); 231 } 232 } 233 } 234 235 @Override onBind(Intent intent)236 public IBinder onBind(Intent intent) { 237 return null; 238 } 239 240 @Override onDestroy()241 public void onDestroy() { 242 super.onDestroy(); 243 if (mBluetoothGatt != null) { 244 mBluetoothGatt.disconnect(); 245 mBluetoothGatt.close(); 246 mBluetoothGatt = null; 247 } 248 stopScan(); 249 unregisterReceiver(mBondStatusReceiver); 250 251 if (mChatService != null) { 252 mChatService.stop(); 253 } 254 255 mTaskQueue.quit(); 256 } 257 connectGatt(BluetoothDevice device, Context context, boolean autoConnect, boolean isSecure, BluetoothGattCallback callback)258 public static BluetoothGatt connectGatt(BluetoothDevice device, Context context, 259 boolean autoConnect, boolean isSecure, 260 BluetoothGattCallback callback) { 261 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 262 if (isSecure) { 263 if (TRANSPORT_MODE_FOR_SECURE_CONNECTION == BluetoothDevice.TRANSPORT_AUTO) { 264 Toast.makeText(context, "connectGatt(transport=AUTO)", Toast.LENGTH_SHORT) 265 .show(); 266 } else { 267 Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show(); 268 } 269 return device.connectGatt(context, autoConnect, callback, 270 TRANSPORT_MODE_FOR_SECURE_CONNECTION); 271 } else { 272 Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show(); 273 return device.connectGatt(context, autoConnect, callback, 274 BluetoothDevice.TRANSPORT_LE); 275 } 276 } else { 277 Toast.makeText(context, "connectGatt", Toast.LENGTH_SHORT).show(); 278 return device.connectGatt(context, autoConnect, callback); 279 } 280 } 281 readCharacteristic(UUID uuid)282 private void readCharacteristic(UUID uuid) { 283 BluetoothGattCharacteristic characteristic = getCharacteristic(uuid); 284 if (characteristic != null) { 285 mBluetoothGatt.readCharacteristic(characteristic); 286 } 287 } 288 notifyError(String message)289 private void notifyError(String message) { 290 showMessage(message); 291 Log.e(TAG, message); 292 293 Intent intent = new Intent(BLE_CLIENT_ERROR); 294 sendBroadcast(intent); 295 } 296 notifyMismatchSecure()297 private void notifyMismatchSecure() { 298 Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE); 299 sendBroadcast(intent); 300 } 301 notifyMismatchInsecure()302 private void notifyMismatchInsecure() { 303 Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_INSECURE); 304 sendBroadcast(intent); 305 } 306 notifyBluetoothDisabled()307 private void notifyBluetoothDisabled() { 308 Intent intent = new Intent(BLE_BLUETOOTH_DISABLED); 309 sendBroadcast(intent); 310 } 311 notifyConnected()312 private void notifyConnected() { 313 showMessage("Bluetooth LE GATT connected"); 314 Intent intent = new Intent(BLE_LE_CONNECTED); 315 sendBroadcast(intent); 316 } 317 startLeDiscovery()318 private void startLeDiscovery() { 319 // Start Service Discovery 320 if (mBluetoothGatt != null && mBleState == BluetoothProfile.STATE_CONNECTED) { 321 mBluetoothGatt.discoverServices(); 322 } else { 323 showMessage("Bluetooth LE GATT not connected."); 324 } 325 } 326 notifyDisconnected()327 private void notifyDisconnected() { 328 showMessage("Bluetooth LE disconnected"); 329 Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED); 330 sendBroadcast(intent); 331 } 332 notifyServicesDiscovered()333 private void notifyServicesDiscovered() { 334 showMessage("Service discovered"); 335 // Find the LE_COC_PSM characteristics 336 if (DEBUG) { 337 Log.d(TAG, "notifyServicesDiscovered: Next step is to read the PSM char."); 338 } 339 readCharacteristic(LE_PSM_CHARACTERISTIC_UUID); 340 } 341 getService()342 private BluetoothGattService getService() { 343 BluetoothGattService service = null; 344 345 if (mBluetoothGatt != null) { 346 service = mBluetoothGatt.getService(SERVICE_UUID); 347 if (service == null) { 348 showMessage("GATT Service not found"); 349 } 350 } 351 return service; 352 } 353 getCharacteristic(UUID uuid)354 private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { 355 BluetoothGattCharacteristic characteristic = null; 356 357 BluetoothGattService service = getService(); 358 if (service != null) { 359 characteristic = service.getCharacteristic(uuid); 360 if (characteristic == null) { 361 showMessage("Characteristic not found"); 362 } 363 } 364 return characteristic; 365 } 366 showMessage(final String msg)367 private void showMessage(final String msg) { 368 mHandler.post(new Runnable() { 369 public void run() { 370 Toast.makeText(BleCocClientService.this, msg, Toast.LENGTH_SHORT).show(); 371 } 372 }); 373 } 374 375 private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() { 376 @Override 377 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 378 if (DEBUG) { 379 Log.d(TAG, "onConnectionStateChange: status=" + status + ", newState=" + newState); 380 } 381 if (status == BluetoothGatt.GATT_SUCCESS) { 382 if (newState == BluetoothProfile.STATE_CONNECTED) { 383 mBleState = newState; 384 int bondState = gatt.getDevice().getBondState(); 385 boolean bonded = false; 386 BluetoothDevice target = gatt.getDevice(); 387 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); 388 if (!pairedDevices.isEmpty()) { 389 for (BluetoothDevice device : pairedDevices) { 390 if (device.getAddress().equals(target.getAddress())) { 391 bonded = true; 392 break; 393 } 394 } 395 } 396 if (mSecure && ((bondState == BluetoothDevice.BOND_NONE) || !bonded)) { 397 // not pairing and execute Secure Test 398 Log.e(TAG, "BluetoothGattCallback.onConnectionStateChange: " 399 + "Not paired but execute secure test"); 400 mBluetoothGatt.disconnect(); 401 notifyMismatchSecure(); 402 } else if (!mSecure && ((bondState != BluetoothDevice.BOND_NONE) || bonded)) { 403 // already pairing and execute Insecure Test 404 Log.e(TAG, "BluetoothGattCallback.onConnectionStateChange: " 405 + "Paired but execute insecure test"); 406 mBluetoothGatt.disconnect(); 407 notifyMismatchInsecure(); 408 } else { 409 notifyConnected(); 410 } 411 } else if (status == BluetoothProfile.STATE_DISCONNECTED) { 412 mBleState = newState; 413 mSecure = false; 414 mBluetoothGatt.close(); 415 notifyDisconnected(); 416 } 417 } else { 418 showMessage("Failed to connect: " + status + " , newState = " + newState); 419 mBluetoothGatt.close(); 420 mBluetoothGatt = null; 421 } 422 } 423 424 @Override 425 public void onServicesDiscovered(BluetoothGatt gatt, int status) { 426 if (DEBUG) { 427 Log.d(TAG, "onServicesDiscovered: status=" + status); 428 } 429 if ((status == BluetoothGatt.GATT_SUCCESS) && 430 (mBluetoothGatt.getService(SERVICE_UUID) != null)) { 431 notifyServicesDiscovered(); 432 } 433 } 434 435 @Override 436 public void onCharacteristicRead(BluetoothGatt gatt, 437 BluetoothGattCharacteristic characteristic, int status) { 438 UUID uid = characteristic.getUuid(); 439 if (DEBUG) { 440 Log.d(TAG, "onCharacteristicRead: status=" + status + ", uuid=" + uid); 441 } 442 if (status == BluetoothGatt.GATT_SUCCESS) { 443 String value = characteristic.getStringValue(0); 444 if (characteristic.getUuid().equals(LE_PSM_CHARACTERISTIC_UUID)) { 445 mPsm = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); 446 if (DEBUG) { 447 Log.d(TAG, "onCharacteristicRead: reading PSM=" + mPsm); 448 } 449 Intent intent = new Intent(BLE_GOT_PSM); 450 sendBroadcast(intent); 451 } else { 452 if (DEBUG) { 453 Log.d(TAG, "onCharacteristicRead: Note: unknown uuid=" + uid); 454 } 455 } 456 } else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) { 457 notifyError("Not Permission Read: " + status + " : " + uid); 458 } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { 459 notifyError("Not Authentication Read: " + status + " : " + uid); 460 } else { 461 notifyError("Failed to read characteristic: " + status + " : " + uid); 462 } 463 } 464 }; 465 466 private final ScanCallback mScanCallback = new ScanCallback() { 467 @Override 468 public void onScanResult(int callbackType, ScanResult result) { 469 if (mBluetoothGatt == null) { 470 // verify the validity of the advertisement packet. 471 mValidityService = false; 472 List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids(); 473 for (ParcelUuid uuid : uuids) { 474 if (uuid.getUuid().equals(BleCocServerService.ADV_COC_SERVICE_UUID)) { 475 if (DEBUG) { 476 Log.d(TAG, "onScanResult: Found ADV with LE CoC Service UUID."); 477 } 478 mValidityService = true; 479 break; 480 } 481 } 482 if (mValidityService) { 483 stopScan(); 484 485 BluetoothDevice device = result.getDevice(); 486 if (DEBUG) { 487 Log.d(TAG, "onScanResult: Found ADV with CoC UUID on device=" 488 + device); 489 } 490 if (mSecure) { 491 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { 492 if (!device.createBond()) { 493 notifyError("Failed to call create bond"); 494 } 495 } else { 496 mDevice = device; 497 mBluetoothGatt = connectGatt(result.getDevice(), BleCocClientService.this, false, 498 mSecure, mGattCallbacks); 499 } 500 } else { 501 mDevice = device; 502 mBluetoothGatt = connectGatt(result.getDevice(), BleCocClientService.this, false, mSecure, 503 mGattCallbacks); 504 } 505 } else { 506 notifyError("No valid service in Advertisement"); 507 } 508 } 509 } 510 }; 511 checkReadBufContent(byte[] buf, int len)512 private boolean checkReadBufContent(byte[] buf, int len) { 513 // Check that the content is correct 514 for (int i = 0; i < len; i++) { 515 if (buf[i] != mNextReadByte) { 516 Log.e(TAG, "handleMessageRead: Error: wrong byte content. buf[" 517 + i + "]=" + buf[i] + " not equal to " + mNextReadByte); 518 return false; 519 } 520 mNextReadByte++; 521 } 522 return true; 523 } 524 handleMessageRead(Message msg)525 private void handleMessageRead(Message msg) { 526 byte[] buf = (byte[])msg.obj; 527 int len = msg.arg1; 528 if (len <= 0) { 529 return; 530 } 531 mTotalReadLen += len; 532 if (DEBUG) { 533 Log.d(TAG, "handleMessageRead: receive buffer of length=" + len + ", mTotalReadLen=" 534 + mTotalReadLen + ", mNextReadExpectedLen=" + mNextReadExpectedLen); 535 } 536 537 if (mNextReadExpectedLen == mTotalReadLen) { 538 if (!checkReadBufContent(buf, len)) { 539 mNextReadExpectedLen = -1; 540 return; 541 } 542 showMessage("Read " + len + " bytes"); 543 if (DEBUG) { 544 Log.d(TAG, "handleMessageRead: broadcast intent " + mNextReadCompletionIntent); 545 } 546 Intent intent = new Intent(mNextReadCompletionIntent); 547 sendBroadcast(intent); 548 mTotalReadLen = 0; 549 if (mNextReadCompletionIntent.equals(BLE_DATA_8BYTES_READ)) { 550 // The server will not wait for any signal to send out the next bunch of data, after 551 // it finishes sending the first 8 bytes. That means if we set the expectation 552 // asynchronously, the data could come before that, so we have to do that here. 553 readDataLargeBuf(); 554 } else { 555 mNextReadExpectedLen = -1; 556 mNextReadCompletionIntent = null; 557 } 558 } else if (mNextReadExpectedLen > mTotalReadLen) { 559 if (!checkReadBufContent(buf, len)) { 560 mNextReadExpectedLen = -1; 561 return; 562 } 563 } else if (mNextReadExpectedLen < mTotalReadLen) { 564 Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len 565 + ", expected len=" + mNextReadExpectedLen); 566 } 567 } 568 sendMessage(byte[] buf)569 private void sendMessage(byte[] buf) { 570 mChatService.write(buf); 571 } 572 handleMessageWrite(Message msg)573 private void handleMessageWrite(Message msg) { 574 byte[] buffer = (byte[]) msg.obj; 575 int len = buffer.length; 576 577 showMessage("LE Coc Client wrote " + len + " bytes"); 578 if (len == mNextWriteExpectedLen) { 579 if (mNextWriteCompletionIntent != null) { 580 Intent intent = new Intent(mNextWriteCompletionIntent); 581 sendBroadcast(intent); 582 } 583 } else { 584 Log.d(TAG, "handleMessageWrite: unrecognized length=" + len); 585 } 586 } 587 588 private class ChatHandler extends Handler { 589 @Override handleMessage(Message msg)590 public void handleMessage(Message msg) { 591 super.handleMessage(msg); 592 if (DEBUG) { 593 Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg); 594 } 595 int state = msg.arg1; 596 switch (msg.what) { 597 case BluetoothChatService.MESSAGE_STATE_CHANGE: 598 if (state == BluetoothChatService.STATE_CONNECTED) { 599 // LE CoC is established 600 notifyLeCocClientConnected(); 601 } 602 break; 603 case BluetoothChatService.MESSAGE_READ: 604 handleMessageRead(msg); 605 break; 606 case BluetoothChatService.MESSAGE_WRITE: 607 handleMessageWrite(msg); 608 break; 609 } 610 } 611 } 612 notifyLeCocClientConnected()613 private void notifyLeCocClientConnected() { 614 if (DEBUG) { 615 Log.d(TAG, "notifyLeCocClientConnected: device=" + mDevice + ", mSecure=" + mSecure); 616 } 617 showMessage("Bluetooth LE Coc connected"); 618 Intent intent = new Intent(BLE_COC_CONNECTED); 619 sendBroadcast(intent); 620 } 621 leCocClientConnect()622 private void leCocClientConnect() { 623 if (DEBUG) { 624 Log.d(TAG, "leCocClientConnect: device=" + mDevice + ", mSecure=" + mSecure); 625 } 626 if (mDevice == null) { 627 Log.e(TAG, "leCocClientConnect: mDevice is null"); 628 return; 629 } 630 // Construct BluetoothChatService with useBle=true parameter 631 mChatService = new BluetoothChatService(this, new ChatHandler(), true); 632 mChatService.connect(mDevice, mSecure, mPsm); 633 } 634 leCheckConnectionType()635 private void leCheckConnectionType() { 636 if (mChatService == null) { 637 Log.e(TAG, "leCheckConnectionType: no LE Coc connection"); 638 return; 639 } 640 int type = mChatService.getSocketConnectionType(); 641 if (type != BluetoothSocket.TYPE_L2CAP) { 642 Log.e(TAG, "leCheckConnectionType: invalid connection type=" + type); 643 return; 644 } 645 showMessage("LE Coc Connection Type Checked"); 646 Intent intent = new Intent(BLE_CONNECTION_TYPE_CHECKED); 647 sendBroadcast(intent); 648 } 649 sendData8bytes()650 private void sendData8bytes() { 651 if (DEBUG) Log.d(TAG, "sendData8bytes"); 652 653 final byte[] buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; 654 mNextWriteExpectedLen = 8; 655 mNextWriteCompletionIntent = BLE_DATA_8BYTES_SENT; 656 sendMessage(buf); 657 } 658 sendDataLargeBuf()659 private void sendDataLargeBuf() { 660 final int len = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE; 661 if (DEBUG) Log.d(TAG, "sendDataLargeBuf of size=" + len); 662 663 byte[] buf = new byte[len]; 664 for (int i = 0; i < len; i++) { 665 buf[i] = (byte)(i + 1); 666 } 667 mNextWriteExpectedLen = len; 668 mNextWriteCompletionIntent = null; 669 sendMessage(buf); 670 } 671 readData8bytes()672 private void readData8bytes() { 673 mNextReadExpectedLen = 8; 674 mNextReadCompletionIntent = BLE_DATA_8BYTES_READ; 675 mNextReadByte = 1; 676 } 677 readDataLargeBuf()678 private void readDataLargeBuf() { 679 mNextReadExpectedLen = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE; 680 mNextReadCompletionIntent = BLE_DATA_LARGEBUF_READ; 681 mNextReadByte = 1; 682 } 683 startScan()684 private void startScan() { 685 if (DEBUG) Log.d(TAG, "startScan"); 686 List<ScanFilter> filter = Arrays.asList(new ScanFilter.Builder().setServiceUuid( 687 new ParcelUuid(BleCocServerService.ADV_COC_SERVICE_UUID)).build()); 688 ScanSettings setting = new ScanSettings.Builder() 689 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); 690 mScanner.startScan(filter, setting, mScanCallback); 691 } 692 stopScan()693 private void stopScan() { 694 if (DEBUG) Log.d(TAG, "stopScan"); 695 if (mScanner != null) { 696 mScanner.stopScan(mScanCallback); 697 } 698 } 699 700 private final BroadcastReceiver mBondStatusReceiver = new BroadcastReceiver() { 701 @Override 702 public void onReceive(Context context, Intent intent) { 703 if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { 704 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 705 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 706 BluetoothDevice.BOND_NONE); 707 switch (state) { 708 case BluetoothDevice.BOND_BONDED: 709 if (mBluetoothGatt == null) { 710 if (DEBUG) { 711 Log.d(TAG, "onReceive:BOND_BONDED: calling connectGatt. device=" 712 + device + ", mSecure=" + mSecure); 713 } 714 mDevice = device; 715 mBluetoothGatt = connectGatt(device, BleCocClientService.this, false, mSecure, 716 mGattCallbacks); 717 } 718 break; 719 case BluetoothDevice.BOND_NONE: 720 notifyError("Failed to create bond"); 721 break; 722 case BluetoothDevice.BOND_BONDING: 723 // fall through 724 default: 725 // wait for next state 726 break; 727 } 728 } 729 } 730 }; 731 } 732