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