1 /* 2 * Copyright (C) 2017 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.googlecode.android_scripting.facade.bluetooth; 18 19 import android.app.PendingIntent; 20 import android.app.Service; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothAdapter.LeScanCallback; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.le.BluetoothLeScanner; 25 import android.bluetooth.le.ScanCallback; 26 import android.bluetooth.le.ScanFilter; 27 import android.bluetooth.le.ScanFilter.Builder; 28 import android.bluetooth.le.ScanResult; 29 import android.bluetooth.le.ScanSettings; 30 import android.content.BroadcastReceiver; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.os.Bundle; 36 import android.os.ParcelUuid; 37 38 import com.googlecode.android_scripting.Log; 39 import com.googlecode.android_scripting.MainThread; 40 import com.googlecode.android_scripting.facade.EventFacade; 41 import com.googlecode.android_scripting.facade.FacadeManager; 42 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 43 import com.googlecode.android_scripting.rpc.Rpc; 44 import com.googlecode.android_scripting.rpc.RpcOptional; 45 import com.googlecode.android_scripting.rpc.RpcParameter; 46 47 import java.io.UnsupportedEncodingException; 48 import java.util.ArrayList; 49 import java.util.HashMap; 50 import java.util.List; 51 import java.util.UUID; 52 import java.util.concurrent.Callable; 53 54 /** 55 * BluetoothLe Scan functions. 56 */ 57 58 public class BluetoothLeScanFacade extends RpcReceiver { 59 60 private final EventFacade mEventFacade; 61 62 private BluetoothAdapter mBluetoothAdapter; 63 private static int ScanCallbackCount; 64 private static int FilterListCount; 65 private static int LeScanCallbackCount; 66 private static int ScanSettingsCount; 67 private final Service mService; 68 private final BluetoothLeScanner mScanner; 69 private android.bluetooth.le.ScanSettings.Builder mScanSettingsBuilder; 70 private Builder mScanFilterBuilder; 71 private final HashMap<Integer, myScanCallback> mScanCallbackList; 72 private final HashMap<Integer, myLeScanCallback> mLeScanCallbackList; 73 private final HashMap<Integer, ArrayList<ScanFilter>> mScanFilterList; 74 private final HashMap<Integer, ScanSettings> mScanSettingsList; 75 BluetoothLeScanFacade(FacadeManager manager)76 public BluetoothLeScanFacade(FacadeManager manager) { 77 super(manager); 78 mService = manager.getService(); 79 mBluetoothAdapter = MainThread.run(mService, 80 new Callable<BluetoothAdapter>() { 81 @Override 82 public BluetoothAdapter call() throws Exception { 83 return BluetoothAdapter.getDefaultAdapter(); 84 } 85 }); 86 mScanner = mBluetoothAdapter.getBluetoothLeScanner(); 87 mEventFacade = manager.getReceiver(EventFacade.class); 88 mScanFilterList = new HashMap<Integer, ArrayList<ScanFilter>>(); 89 mLeScanCallbackList = new HashMap<Integer, myLeScanCallback>(); 90 mScanSettingsList = new HashMap<Integer, ScanSettings>(); 91 mScanCallbackList = new HashMap<Integer, myScanCallback>(); 92 mScanFilterBuilder = new Builder(); 93 mScanSettingsBuilder = new android.bluetooth.le.ScanSettings.Builder(); 94 } 95 96 /** 97 * Constructs a myScanCallback obj and returns its index 98 * 99 * @return Integer myScanCallback.index 100 */ 101 @Rpc(description = "Generate a new myScanCallback Object") bleGenScanCallback()102 public Integer bleGenScanCallback() { 103 ScanCallbackCount += 1; 104 int index = ScanCallbackCount; 105 myScanCallback mScan = new myScanCallback(index); 106 mScanCallbackList.put(mScan.index, mScan); 107 return mScan.index; 108 } 109 110 /** 111 * Constructs a myLeScanCallback obj and returns its index 112 * 113 * @return Integer myScanCallback.index 114 */ 115 @Rpc(description = "Generate a new myScanCallback Object") bleGenLeScanCallback()116 public Integer bleGenLeScanCallback() { 117 LeScanCallbackCount += 1; 118 int index = LeScanCallbackCount; 119 myLeScanCallback mScan = new myLeScanCallback(index); 120 mLeScanCallbackList.put(mScan.index, mScan); 121 return mScan.index; 122 } 123 124 /** 125 * Constructs a new filter list array and returns its index 126 * 127 * @return Integer index 128 */ 129 @Rpc(description = "Generate a new Filter list") bleGenFilterList()130 public Integer bleGenFilterList() { 131 FilterListCount += 1; 132 int index = FilterListCount; 133 mScanFilterList.put(index, new ArrayList<ScanFilter>()); 134 return index; 135 } 136 137 /** 138 * Constructs a new filter list array and returns its index 139 * 140 * @return Integer index 141 */ 142 @Rpc(description = "Generate a new Filter list") bleBuildScanFilter( @pcParametername = "filterIndex") Integer filterIndex )143 public Integer bleBuildScanFilter( 144 @RpcParameter(name = "filterIndex") 145 Integer filterIndex 146 ) { 147 mScanFilterList.get(filterIndex).add(mScanFilterBuilder.build()); 148 mScanFilterBuilder = new Builder(); 149 return mScanFilterList.get(filterIndex).size()-1; 150 } 151 152 /** 153 * Constructs a new scan setting and returns its index 154 * 155 * @return Integer index 156 */ 157 @Rpc(description = "Generate a new scan settings Object") bleBuildScanSetting()158 public Integer bleBuildScanSetting() { 159 ScanSettingsCount += 1; 160 int index = ScanSettingsCount; 161 mScanSettingsList.put(index, mScanSettingsBuilder.build()); 162 mScanSettingsBuilder = new android.bluetooth.le.ScanSettings.Builder(); 163 return index; 164 } 165 166 /** 167 * Stops a ble scan 168 * 169 * @param index the id of the myScan whose ScanCallback to stop 170 * @throws Exception 171 */ 172 @Rpc(description = "Stops an ongoing ble advertisement scan") bleStopBleScan( @pcParametername = "index") Integer index)173 public void bleStopBleScan( 174 @RpcParameter(name = "index") 175 Integer index) throws Exception { 176 Log.d("bluetooth_le_scan mScanCallback " + index); 177 if (mScanCallbackList.get(index) != null) { 178 myScanCallback mScanCallback = mScanCallbackList.get(index); 179 mScanner.stopScan(mScanCallback); 180 } else { 181 throw new Exception("Invalid index input:" + Integer.toString(index)); 182 } 183 } 184 185 /** 186 * Stops a classic ble scan 187 * 188 * @param index the id of the myScan whose LeScanCallback to stop 189 * @throws Exception 190 */ 191 @Rpc(description = "Stops an ongoing classic ble scan") bleStopClassicBleScan( @pcParametername = "index") Integer index)192 public void bleStopClassicBleScan( 193 @RpcParameter(name = "index") 194 Integer index) throws Exception { 195 Log.d("bluetooth_le_scan mLeScanCallback " + index); 196 if (mLeScanCallbackList.get(index) != null) { 197 myLeScanCallback mLeScanCallback = mLeScanCallbackList.get(index); 198 mBluetoothAdapter.stopLeScan(mLeScanCallback); 199 } else { 200 throw new Exception("Invalid index input:" + Integer.toString(index)); 201 } 202 } 203 204 /** 205 * Starts a ble scan with ScanCallback callbacks. 206 * 207 * @param index the id of the myScan whose ScanCallback to start 208 * @throws Exception 209 */ 210 @Rpc(description = "Starts a ble advertisement scan using scan callback") bleStartBleScan( @pcParametername = "filterListIndex") Integer filterListIndex, @RpcParameter(name = "scanSettingsIndex") Integer scanSettingsIndex, @RpcParameter(name = "callbackIndex") Integer callbackIndex )211 public void bleStartBleScan( 212 @RpcParameter(name = "filterListIndex") 213 Integer filterListIndex, 214 @RpcParameter(name = "scanSettingsIndex") 215 Integer scanSettingsIndex, 216 @RpcParameter(name = "callbackIndex") 217 Integer callbackIndex 218 ) throws Exception { 219 Log.d("bluetooth_le_scan starting a background scan"); 220 ArrayList<ScanFilter> mScanFilters = new ArrayList<ScanFilter>(); 221 mScanFilters.add(new ScanFilter.Builder().build()); 222 ScanSettings mScanSettings = new ScanSettings.Builder().build(); 223 if (mScanFilterList.get(filterListIndex) != null) { 224 mScanFilters = mScanFilterList.get(filterListIndex); 225 } else { 226 throw new Exception("Invalid filterListIndex input:" 227 + Integer.toString(filterListIndex)); 228 } 229 if (mScanSettingsList.get(scanSettingsIndex) != null) { 230 mScanSettings = mScanSettingsList.get(scanSettingsIndex); 231 } else if (!mScanSettingsList.isEmpty()) { 232 throw new Exception("Invalid scanSettingsIndex input:" 233 + Integer.toString(scanSettingsIndex)); 234 } 235 if (mScanCallbackList.get(callbackIndex) != null) { 236 mScanner.startScan(mScanFilters, mScanSettings, 237 mScanCallbackList.get(callbackIndex)); 238 } else { 239 throw new Exception("Invalid filterListIndex input:" 240 + Integer.toString(filterListIndex)); 241 } 242 } 243 createPendingIntent()244 private PendingIntent createPendingIntent() { 245 return PendingIntent 246 .getBroadcast(mService, 0, 247 new Intent(mService, ScanBroadcastReceiver.class) 248 .setAction("com.googlecode.android_scripting.ACTION_FOUND") 249 .setComponent(new ComponentName("com.googlecode.android_scripting", 250 "com.googlecode.android_scripting.facade.bluetooth.ScanBroadcastReceiver")), 251 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE); 252 } 253 254 /** 255 * Starts a ble scan with pending intent callback. 256 * 257 * @param filterListIndex indicate the scan filter list to use. 258 * @param scanSettingsIndex indicate the scan setting to use. 259 */ 260 @Rpc(description = "Starts a ble advertisement scan") bleStartBleScanPendingIntent( @pcParametername = "filterListIndex") Integer filterListIndex, @RpcParameter(name = "scanSettingsIndex") Integer scanSettingsIndex )261 public void bleStartBleScanPendingIntent( 262 @RpcParameter(name = "filterListIndex") 263 Integer filterListIndex, 264 @RpcParameter(name = "scanSettingsIndex") 265 Integer scanSettingsIndex 266 ) throws Exception { 267 Log.d("bluetooth_le_scan starting a background scan using pending intent"); 268 ArrayList<ScanFilter> mScanFilters = new ArrayList<ScanFilter>(); 269 mScanFilters.add(new ScanFilter.Builder().build()); 270 ScanSettings mScanSettings = new ScanSettings.Builder().build(); 271 if (mScanFilterList.get(filterListIndex) != null) { 272 mScanFilters = mScanFilterList.get(filterListIndex); 273 } else { 274 throw new IllegalArgumentException("Invalid filterListIndex input:" 275 + Integer.toString(filterListIndex)); 276 } 277 if (mScanSettingsList.get(scanSettingsIndex) != null) { 278 mScanSettings = mScanSettingsList.get(scanSettingsIndex); 279 } else if (!mScanSettingsList.isEmpty()) { 280 throw new IllegalArgumentException("Invalid scanSettingsIndex input:" 281 + Integer.toString(scanSettingsIndex)); 282 } 283 Log.d("Registering receiver"); 284 mService.registerReceiver(new TestBroadcastReceiver(), 285 new IntentFilter(ScanBroadcastReceiver.ACTION_FOUND_SIDESTEP)); 286 Log.d("Starting Scan"); 287 mScanner.startScan(mScanFilters, mScanSettings, createPendingIntent()); 288 } 289 290 private class TestBroadcastReceiver extends BroadcastReceiver { 291 292 @Override onReceive(Context context, Intent intent)293 public void onReceive(Context context, Intent intent) { 294 Log.d("TEST onReceive( " + context + ", " + intent + ")"); 295 ArrayList<ScanResult> results = intent 296 .getParcelableArrayListExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT); 297 if (results != null && results.size() > 0) { 298 for (final ScanResult result : results) { 299 Log.d( 300 "onScanResult : " + result.getDevice().getName() + " " 301 + result.getDevice().getAddress()); 302 new myScanCallback(1) 303 .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result); 304 } 305 } else { 306 Log.d("Empty results"); 307 } 308 } 309 } 310 311 /** 312 * Starts a classic ble scan 313 * 314 * @param index the id of the myScan whose ScanCallback to start 315 * @throws Exception 316 */ 317 @Rpc(description = "Starts a classic ble advertisement scan") bleStartClassicBleScan( @pcParametername = "leCallbackIndex") Integer leCallbackIndex )318 public boolean bleStartClassicBleScan( 319 @RpcParameter(name = "leCallbackIndex") 320 Integer leCallbackIndex 321 ) throws Exception { 322 Log.d("bluetooth_le_scan starting a background scan"); 323 boolean result = false; 324 if (mLeScanCallbackList.get(leCallbackIndex) != null) { 325 result = mBluetoothAdapter.startLeScan( 326 mLeScanCallbackList.get(leCallbackIndex)); 327 } else { 328 throw new Exception("Invalid leCallbackIndex input:" 329 + Integer.toString(leCallbackIndex)); 330 } 331 return result; 332 } 333 334 /** 335 * Starts a classic ble scan with service Uuids 336 * 337 * @param index the id of the myScan whose ScanCallback to start 338 * @throws Exception 339 */ 340 @Rpc(description = "Starts a classic ble advertisement scan with service Uuids") bleStartClassicBleScanWithServiceUuids( @pcParametername = "leCallbackIndex") Integer leCallbackIndex, @RpcParameter(name = "serviceUuids") String[] serviceUuidList )341 public boolean bleStartClassicBleScanWithServiceUuids( 342 @RpcParameter(name = "leCallbackIndex") 343 Integer leCallbackIndex, 344 @RpcParameter(name = "serviceUuids") 345 String[] serviceUuidList 346 ) throws Exception { 347 Log.d("bluetooth_le_scan starting a background scan"); 348 UUID[] serviceUuids = new UUID[serviceUuidList.length]; 349 for (int i = 0; i < serviceUuidList.length; i++) { 350 serviceUuids[i] = UUID.fromString(serviceUuidList[i]); 351 } 352 boolean result = false; 353 if (mLeScanCallbackList.get(leCallbackIndex) != null) { 354 result = mBluetoothAdapter.startLeScan(serviceUuids, 355 mLeScanCallbackList.get(leCallbackIndex)); 356 } else { 357 throw new Exception("Invalid leCallbackIndex input:" 358 + Integer.toString(leCallbackIndex)); 359 } 360 return result; 361 } 362 363 /** 364 * Trigger onBatchScanResults 365 * 366 * @throws Exception 367 */ 368 @Rpc(description = "Gets the results of the ble ScanCallback") bleFlushPendingScanResults( @pcParametername = "callbackIndex") Integer callbackIndex )369 public void bleFlushPendingScanResults( 370 @RpcParameter(name = "callbackIndex") 371 Integer callbackIndex 372 ) throws Exception { 373 if (mScanCallbackList.get(callbackIndex) != null) { 374 mBluetoothAdapter 375 .getBluetoothLeScanner().flushPendingScanResults( 376 mScanCallbackList.get(callbackIndex)); 377 } else { 378 throw new Exception("Invalid callbackIndex input:" 379 + Integer.toString(callbackIndex)); 380 } 381 } 382 383 /** 384 * Set scanSettings for ble scan. Note: You have to set all variables at once. 385 * 386 * @param callbackType Bluetooth LE scan callback type 387 * @param reportDelaySeconds Time of delay for reporting the scan result 388 * @param scanMode Bluetooth LE scan mode. 389 * @param scanResultType Bluetooth LE scan result type 390 * @throws Exception 391 */ 392 393 /** 394 * Set the scan setting's callback type 395 * @param callbackType Bluetooth LE scan callback type 396 */ 397 @Rpc(description = "Set the scan setting's callback type") bleSetScanSettingsCallbackType( @pcParametername = "callbackType") Integer callbackType)398 public void bleSetScanSettingsCallbackType( 399 @RpcParameter(name = "callbackType") 400 Integer callbackType) { 401 mScanSettingsBuilder.setCallbackType(callbackType); 402 } 403 404 /** 405 * Set the scan setting's report delay millis 406 * @param reportDelayMillis Time of delay for reporting the scan result 407 */ 408 @Rpc(description = "Set the scan setting's report delay millis") bleSetScanSettingsReportDelayMillis( @pcParametername = "reportDelayMillis") Long reportDelayMillis)409 public void bleSetScanSettingsReportDelayMillis( 410 @RpcParameter(name = "reportDelayMillis") 411 Long reportDelayMillis) { 412 mScanSettingsBuilder.setReportDelay(reportDelayMillis); 413 } 414 415 /** 416 * Set the scan setting's scan mode 417 * @param scanMode Bluetooth LE scan mode. 418 */ 419 @Rpc(description = "Set the scan setting's scan mode") bleSetScanSettingsScanMode( @pcParametername = "scanMode") Integer scanMode)420 public void bleSetScanSettingsScanMode( 421 @RpcParameter(name = "scanMode") 422 Integer scanMode) { 423 mScanSettingsBuilder.setScanMode(scanMode); 424 } 425 426 /** 427 * Set the scan setting's legacy mode 428 * @param legacy Wether scan is legacy. 429 */ 430 @Rpc(description = "Set the scan setting's legacy mode") bleSetScanSettingsLegacy( @pcParametername = "legacy") Boolean legacy)431 public void bleSetScanSettingsLegacy( 432 @RpcParameter(name = "legacy") 433 Boolean legacy) { 434 mScanSettingsBuilder.setLegacy(legacy); 435 } 436 437 /** 438 * Set the scan setting's phy mode 439 * @param phy 440 */ 441 @Rpc(description = "Set the scan setting's phy mode") bleSetScanSettingsPhy( @pcParametername = "phy") Integer phy)442 public void bleSetScanSettingsPhy( 443 @RpcParameter(name = "phy") 444 Integer phy) { 445 mScanSettingsBuilder.setPhy(phy); 446 } 447 448 /** 449 * Set the scan setting's scan result type 450 * @param scanResultType Bluetooth LE scan result type 451 */ 452 @Rpc(description = "Set the scan setting's scan result type") bleSetScanSettingsResultType( @pcParametername = "scanResultType") Integer scanResultType)453 public void bleSetScanSettingsResultType( 454 @RpcParameter(name = "scanResultType") 455 Integer scanResultType) { 456 mScanSettingsBuilder.setScanResultType(scanResultType); 457 } 458 /** 459 * Get ScanSetting's callback type 460 * 461 * @param index the ScanSetting object to use 462 * @return the ScanSetting's callback type 463 * @throws Exception 464 */ 465 @Rpc(description = "Get ScanSetting's callback type") bleGetScanSettingsCallbackType( @pcParametername = "index") Integer index )466 public Integer bleGetScanSettingsCallbackType( 467 @RpcParameter(name = "index") 468 Integer index 469 ) throws Exception { 470 if (mScanSettingsList.get(index) != null) { 471 ScanSettings mScanSettings = mScanSettingsList.get(index); 472 return mScanSettings.getCallbackType(); 473 } else { 474 throw new Exception("Invalid index input:" + Integer.toString(index)); 475 } 476 } 477 478 /** 479 * Get ScanSetting's report delay in milli seconds 480 * 481 * @param index the ScanSetting object to useSystemClock 482 * @return the ScanSetting's report delay in milliseconds 483 * @throws Exception 484 */ 485 @Rpc(description = "Get ScanSetting's report delay milliseconds") bleGetScanSettingsReportDelayMillis( @pcParametername = "index") Integer index)486 public Long bleGetScanSettingsReportDelayMillis( 487 @RpcParameter(name = "index") 488 Integer index) throws Exception { 489 if (mScanSettingsList.get(index) != null) { 490 ScanSettings mScanSettings = mScanSettingsList.get(index); 491 return mScanSettings.getReportDelayMillis(); 492 } else { 493 throw new Exception("Invalid index input:" + Integer.toString(index)); 494 } 495 } 496 497 /** 498 * Get ScanSetting's scan mode 499 * 500 * @param index the ScanSetting object to use 501 * @return the ScanSetting's scan mode 502 * @throws Exception 503 */ 504 @Rpc(description = "Get ScanSetting's scan mode") bleGetScanSettingsScanMode( @pcParametername = "index") Integer index)505 public Integer bleGetScanSettingsScanMode( 506 @RpcParameter(name = "index") 507 Integer index) throws Exception { 508 if (mScanSettingsList.get(index) != null) { 509 ScanSettings mScanSettings = mScanSettingsList.get(index); 510 return mScanSettings.getScanMode(); 511 } else { 512 throw new Exception("Invalid index input:" + Integer.toString(index)); 513 } 514 } 515 516 /** 517 * Get ScanSetting's scan result type 518 * 519 * @param index the ScanSetting object to use 520 * @return the ScanSetting's scan result type 521 * @throws Exception 522 */ 523 @Rpc(description = "Get ScanSetting's scan result type") bleGetScanSettingsScanResultType( @pcParametername = "index") Integer index)524 public Integer bleGetScanSettingsScanResultType( 525 @RpcParameter(name = "index") 526 Integer index) throws Exception { 527 if (mScanSettingsList.get(index) != null) { 528 ScanSettings mScanSettings = mScanSettingsList.get(index); 529 return mScanSettings.getScanResultType(); 530 } else { 531 throw new Exception("Invalid index input:" 532 + Integer.toString(index)); 533 } 534 } 535 536 /** 537 * Get ScanFilter's Manufacturer Id 538 * 539 * @param index the ScanFilter object to use 540 * @return the ScanFilter's manufacturer id 541 * @throws Exception 542 */ 543 @Rpc(description = "Get ScanFilter's Manufacturer Id") bleGetScanFilterManufacturerId( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)544 public Integer bleGetScanFilterManufacturerId( 545 @RpcParameter(name = "index") 546 Integer index, 547 @RpcParameter(name = "filterIndex") 548 Integer filterIndex) 549 throws Exception { 550 if (mScanFilterList.get(index) != null) { 551 if (mScanFilterList.get(index).get(filterIndex) != null) { 552 return mScanFilterList.get(index) 553 .get(filterIndex).getManufacturerId(); 554 } else { 555 throw new Exception("Invalid filterIndex input:" 556 + Integer.toString(filterIndex)); 557 } 558 } else { 559 throw new Exception("Invalid index input:" 560 + Integer.toString(index)); 561 } 562 } 563 564 /** 565 * Get ScanFilter's device address 566 * 567 * @param index the ScanFilter object to use 568 * @return the ScanFilter's device address 569 * @throws Exception 570 */ 571 @Rpc(description = "Get ScanFilter's device address") bleGetScanFilterDeviceAddress( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)572 public String bleGetScanFilterDeviceAddress( 573 @RpcParameter(name = "index") 574 Integer index, 575 @RpcParameter(name = "filterIndex") 576 Integer filterIndex) 577 throws Exception { 578 if (mScanFilterList.get(index) != null) { 579 if (mScanFilterList.get(index).get(filterIndex) != null) { 580 return mScanFilterList.get(index).get( 581 filterIndex).getDeviceAddress(); 582 } else { 583 throw new Exception("Invalid filterIndex input:" 584 + Integer.toString(filterIndex)); 585 } 586 } else { 587 throw new Exception("Invalid index input:" 588 + Integer.toString(index)); 589 } 590 } 591 592 /** 593 * Get ScanFilter's device address type 594 * 595 * @param index the ScanFilter object to use 596 * @return the ScanFilter's device address type 597 * @throws Exception 598 */ 599 @Rpc(description = "Get ScanFilter's device address type") bleGetScanFilterAddressType( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)600 public Integer bleGetScanFilterAddressType( 601 @RpcParameter(name = "index") 602 Integer index, 603 @RpcParameter(name = "filterIndex") 604 Integer filterIndex) 605 throws Exception { 606 if (mScanFilterList.get(index) != null) { 607 if (mScanFilterList.get(index).get(filterIndex) != null) { 608 return mScanFilterList.get(index).get( 609 filterIndex).getAddressType(); 610 } else { 611 throw new Exception("Invalid filterIndex input:" + filterIndex); 612 } 613 } else { 614 throw new Exception("Invalid index input:" + index); 615 } 616 } 617 618 /** 619 * Get ScanFilter's irk 620 * 621 * @param index the ScanFilter object to use 622 * @return the ScanFilter's irk 623 * @throws Exception 624 */ 625 @Rpc(description = "Get ScanFilter's irk") bleGetScanFilterIrk( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)626 public byte[] bleGetScanFilterIrk( 627 @RpcParameter(name = "index") 628 Integer index, 629 @RpcParameter(name = "filterIndex") 630 Integer filterIndex) 631 throws Exception { 632 if (mScanFilterList.get(index) != null) { 633 if (mScanFilterList.get(index).get(filterIndex) != null) { 634 return mScanFilterList.get(index).get(filterIndex).getIrk(); 635 } else { 636 throw new Exception("Invalid filterIndex input:" + filterIndex); 637 } 638 } else { 639 throw new Exception("Invalid index input:" + index); 640 } 641 } 642 643 /** 644 * Get ScanFilter's device name 645 * 646 * @param index the ScanFilter object to use 647 * @return the ScanFilter's device name 648 * @throws Exception 649 */ 650 @Rpc(description = "Get ScanFilter's device name") bleGetScanFilterDeviceName( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)651 public String bleGetScanFilterDeviceName( 652 @RpcParameter(name = "index") 653 Integer index, 654 @RpcParameter(name = "filterIndex") 655 Integer filterIndex) 656 throws Exception { 657 if (mScanFilterList.get(index) != null) { 658 if (mScanFilterList.get(index).get(filterIndex) != null) { 659 return mScanFilterList.get(index).get( 660 filterIndex).getDeviceName(); 661 } else { 662 throw new Exception("Invalid filterIndex input:" 663 + Integer.toString(filterIndex)); 664 } 665 } else { 666 throw new Exception("Invalid index input:" 667 + Integer.toString(index)); 668 } 669 } 670 671 /** 672 * Get ScanFilter's manufacturer data 673 * 674 * @param index the ScanFilter object to use 675 * @return the ScanFilter's manufacturer data 676 * @throws Exception 677 */ 678 @Rpc(description = "Get ScanFilter's manufacturer data") bleGetScanFilterManufacturerData( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)679 public byte[] bleGetScanFilterManufacturerData( 680 @RpcParameter(name = "index") 681 Integer index, 682 @RpcParameter(name = "filterIndex") 683 Integer filterIndex) 684 throws Exception { 685 if (mScanFilterList.get(index) != null) { 686 if (mScanFilterList.get(index).get(filterIndex) != null) { 687 return mScanFilterList.get(index).get( 688 filterIndex).getManufacturerData(); 689 } else { 690 throw new Exception("Invalid filterIndex input:" 691 + Integer.toString(filterIndex)); 692 } 693 } else { 694 throw new Exception("Invalid index input:" 695 + Integer.toString(index)); 696 } 697 } 698 699 /** 700 * Get ScanFilter's manufacturer data mask 701 * 702 * @param index the ScanFilter object to use 703 * @return the ScanFilter's manufacturer data mask 704 * @throws Exception 705 */ 706 @Rpc(description = "Get ScanFilter's manufacturer data mask") bleGetScanFilterManufacturerDataMask( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)707 public byte[] bleGetScanFilterManufacturerDataMask( 708 @RpcParameter(name = "index") 709 Integer index, 710 @RpcParameter(name = "filterIndex") 711 Integer filterIndex) 712 throws Exception { 713 if (mScanFilterList.get(index) != null) { 714 if (mScanFilterList.get(index).get(filterIndex) != null) { 715 return mScanFilterList.get(index).get( 716 filterIndex).getManufacturerDataMask(); 717 } else { 718 throw new Exception("Invalid filterIndex input:" 719 + Integer.toString(filterIndex)); 720 } 721 } else { 722 throw new Exception("Invalid index input:" 723 + Integer.toString(index)); 724 } 725 } 726 727 /** 728 * Get ScanFilter's service data 729 * 730 * @param index the ScanFilter object to use 731 * @return the ScanFilter's service data 732 * @throws Exception 733 */ 734 @Rpc(description = "Get ScanFilter's service data") bleGetScanFilterServiceData( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)735 public byte[] bleGetScanFilterServiceData( 736 @RpcParameter(name = "index") 737 Integer index, 738 @RpcParameter(name = "filterIndex") 739 Integer filterIndex) 740 throws Exception { 741 if (mScanFilterList.get(index) != null) { 742 if (mScanFilterList.get(index).get(filterIndex) != null) { 743 return mScanFilterList.get(index).get( 744 filterIndex).getServiceData(); 745 } else { 746 throw new Exception("Invalid filterIndex input:" 747 + Integer.toString(filterIndex)); 748 } 749 } else { 750 throw new Exception("Invalid index input:" 751 + Integer.toString(index)); 752 } 753 } 754 755 /** 756 * Get ScanFilter's service data mask 757 * 758 * @param index the ScanFilter object to use 759 * @return the ScanFilter's service data mask 760 * @throws Exception 761 */ 762 @Rpc(description = "Get ScanFilter's service data mask") bleGetScanFilterServiceDataMask( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)763 public byte[] bleGetScanFilterServiceDataMask( 764 @RpcParameter(name = "index") 765 Integer index, 766 @RpcParameter(name = "filterIndex") 767 Integer filterIndex) 768 throws Exception { 769 if (mScanFilterList.get(index) != null) { 770 if (mScanFilterList.get(index).get(filterIndex) != null) { 771 return mScanFilterList.get(index).get( 772 filterIndex).getServiceDataMask(); 773 } else { 774 throw new Exception("Invalid filterIndex input:" 775 + Integer.toString(filterIndex)); 776 } 777 } else { 778 throw new Exception("Invalid index input:" 779 + Integer.toString(index)); 780 } 781 } 782 783 /** 784 * Get ScanFilter's service uuid 785 * 786 * @param index the ScanFilter object to use 787 * @return the ScanFilter's service uuid 788 * @throws Exception 789 */ 790 @Rpc(description = "Get ScanFilter's service uuid") bleGetScanFilterServiceUuid( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)791 public String bleGetScanFilterServiceUuid( 792 @RpcParameter(name = "index") 793 Integer index, 794 @RpcParameter(name = "filterIndex") 795 Integer filterIndex) 796 throws Exception { 797 if (mScanFilterList.get(index) != null) { 798 if (mScanFilterList.get(index).get(filterIndex) != null) { 799 if (mScanFilterList.get(index).get( 800 filterIndex).getServiceUuid() != null) { 801 return mScanFilterList.get(index).get( 802 filterIndex).getServiceUuid().toString(); 803 } else { 804 throw new Exception("No Service Uuid set for filter:" 805 + Integer.toString(filterIndex)); 806 } 807 } else { 808 throw new Exception("Invalid filterIndex input:" 809 + Integer.toString(filterIndex)); 810 } 811 } else { 812 throw new Exception("Invalid index input:" 813 + Integer.toString(index)); 814 } 815 } 816 817 /** 818 * Get ScanFilter's service uuid mask 819 * 820 * @param index the ScanFilter object to use 821 * @return the ScanFilter's service uuid mask 822 * @throws Exception 823 */ 824 @Rpc(description = "Get ScanFilter's service uuid mask") bleGetScanFilterServiceUuidMask( @pcParametername = "index") Integer index, @RpcParameter(name = "filterIndex") Integer filterIndex)825 public String bleGetScanFilterServiceUuidMask( 826 @RpcParameter(name = "index") 827 Integer index, 828 @RpcParameter(name = "filterIndex") 829 Integer filterIndex) 830 throws Exception { 831 if (mScanFilterList.get(index) != null) { 832 if (mScanFilterList.get(index).get(filterIndex) != null) { 833 if (mScanFilterList.get(index).get( 834 filterIndex).getServiceUuidMask() != null) { 835 return mScanFilterList.get( 836 index).get(filterIndex).getServiceUuidMask() 837 .toString(); 838 } else { 839 throw new Exception("No Service Uuid Mask set for filter:" 840 + Integer.toString(filterIndex)); 841 } 842 } else { 843 throw new Exception("Invalid filterIndex input:" 844 + Integer.toString(filterIndex)); 845 } 846 } else { 847 throw new Exception("Invalid index input:" 848 + Integer.toString(index)); 849 } 850 } 851 852 /** 853 * Add filter "macAddress" to existing ScanFilter 854 * 855 * @param macAddress the macAddress to filter against 856 * @throws Exception 857 */ 858 @Rpc(description = "Add filter \"macAddress\" to existing ScanFilter") bleSetScanFilterDeviceAddress( @pcParametername = "macAddress") String macAddress )859 public void bleSetScanFilterDeviceAddress( 860 @RpcParameter(name = "macAddress") 861 String macAddress 862 ) { 863 mScanFilterBuilder.setDeviceAddress(macAddress); 864 } 865 866 /** 867 * Add filter "macAddress", and "addressType" to existing ScanFilter 868 * 869 * @param macAddress the macAddress to filter against 870 * @param addressType the type of macAddress to filter against 871 * @throws Exception 872 */ 873 @Rpc(description = "Add filter \"macAddress\" and \"addressType\" to existing ScanFilter") bleSetScanFilterDeviceAddressAndType( @pcParametername = "macAddress") String macAddress, @RpcParameter(name = "addressType") Integer addressType )874 public void bleSetScanFilterDeviceAddressAndType( 875 @RpcParameter(name = "macAddress") String macAddress, 876 @RpcParameter(name = "addressType") Integer addressType 877 ) { 878 mScanFilterBuilder.setDeviceAddress(macAddress, addressType); 879 } 880 881 /** 882 * Add filter "macAddress", "addressType", and "irk" to existing ScanFilter 883 * 884 * @param macAddress the macAddress to filter against 885 * @param addressType the type of macAddress to filter against 886 * @param irk IRK for address resolution 887 * @throws Exception 888 */ 889 @Rpc(description = 890 "Add filter \"macAddress\", \"addressType\", and \"irk\" to existing ScanFilter") bleSetScanFilterDeviceAddressTypeAndIrk( @pcParametername = "macAddress") String macAddress, @RpcParameter(name = "addressType") Integer addressType, @RpcParameter(name = "irk") String irk )891 public void bleSetScanFilterDeviceAddressTypeAndIrk( 892 @RpcParameter(name = "macAddress") String macAddress, 893 @RpcParameter(name = "addressType") Integer addressType, 894 @RpcParameter(name = "irk") String irk 895 ) { 896 mScanFilterBuilder.setDeviceAddress(macAddress, addressType, irk.getBytes()); 897 } 898 899 /** 900 * Add filter "macAddress", "addressType", and "irk" to existing ScanFilter 901 * 902 * @param macAddress the macAddress to filter against 903 * @param addressType the type of macAddress to filter against 904 * @param irk IRK for address resolution in hex format 905 * @throws Exception 906 */ 907 @Rpc(description = 908 "Add filter \"macAddress\", \"addressType\", and \"irk\" to existing ScanFilter") bleSetScanFilterDeviceAddressTypeAndIrkHexString( @pcParametername = "macAddress") String macAddress, @RpcParameter(name = "addressType") Integer addressType, @RpcParameter(name = "irk") String irk )909 public void bleSetScanFilterDeviceAddressTypeAndIrkHexString( 910 @RpcParameter(name = "macAddress") String macAddress, 911 @RpcParameter(name = "addressType") Integer addressType, 912 @RpcParameter(name = "irk") String irk 913 ) throws UnsupportedEncodingException { 914 mScanFilterBuilder.setDeviceAddress(macAddress, addressType, hexStringToByteArray(irk)); 915 } 916 hexStringToByteArray(String s)917 private static byte[] hexStringToByteArray(String s) { 918 if (s == null) { 919 throw new IllegalArgumentException("Hex String must not be null"); 920 } 921 int len = s.length(); 922 if ((len % 2) != 0 || len < 1) { // Multiple of 2 or empty 923 throw new IllegalArgumentException("Hex String must be an even number > 0"); 924 } 925 byte[] data = new byte[len / 2]; 926 for (int i = 0; i < len; i += 2) { 927 data[i / 2] = (byte) ((byte) (Character.digit(s.charAt(i), 16) << 4) 928 + (byte) Character.digit(s.charAt(i + 1), 16)); 929 } 930 return data; 931 } 932 933 /** 934 * Add filter "manufacturereDataId and/or manufacturerData" to existing 935 * ScanFilter 936 * @param manufacturerDataId the manufacturer data id to filter against 937 * @param manufacturerDataMask the manufacturere data mask to filter against 938 * @throws Exception 939 */ 940 @Rpc(description = "Add filter \"manufacturereDataId and/or manufacturerData\" to existing ScanFilter") bleSetScanFilterManufacturerData( @pcParametername = "manufacturerDataId") Integer manufacturerDataId, @RpcParameter(name = "manufacturerData") byte[] manufacturerData, @RpcParameter(name = "manufacturerDataMask") @RpcOptional byte[] manufacturerDataMask )941 public void bleSetScanFilterManufacturerData( 942 @RpcParameter(name = "manufacturerDataId") 943 Integer manufacturerDataId, 944 @RpcParameter(name = "manufacturerData") 945 byte[] manufacturerData, 946 @RpcParameter(name = "manufacturerDataMask") 947 @RpcOptional 948 byte[] manufacturerDataMask 949 ){ 950 if (manufacturerDataMask != null) { 951 mScanFilterBuilder.setManufacturerData(manufacturerDataId, 952 manufacturerData, manufacturerDataMask); 953 } else { 954 mScanFilterBuilder.setManufacturerData(manufacturerDataId, 955 manufacturerData); 956 } 957 } 958 959 /** 960 * Add filter "serviceData and serviceDataMask" to existing ScanFilter 961 * 962 * @param serviceData the service data to filter against 963 * @param serviceDataMask the servie data mask to filter against 964 * @throws Exception 965 */ 966 @Rpc(description = "Add filter \"serviceData and serviceDataMask\" to existing ScanFilter ") bleSetScanFilterServiceData( @pcParametername = "serviceUuid") String serviceUuid, @RpcParameter(name = "serviceData") byte[] serviceData, @RpcParameter(name = "serviceDataMask") @RpcOptional byte[] serviceDataMask )967 public void bleSetScanFilterServiceData( 968 @RpcParameter(name = "serviceUuid") String serviceUuid, 969 @RpcParameter(name = "serviceData") byte[] serviceData, 970 @RpcParameter(name = "serviceDataMask") 971 @RpcOptional byte[] serviceDataMask 972 ) { 973 if (serviceDataMask != null) { 974 mScanFilterBuilder 975 .setServiceData( 976 ParcelUuid.fromString(serviceUuid), 977 serviceData, serviceDataMask); 978 } else { 979 mScanFilterBuilder.setServiceData(ParcelUuid.fromString(serviceUuid), 980 serviceData); 981 } 982 } 983 984 /** 985 * Add filter "serviceUuid and/or serviceMask" to existing ScanFilter 986 * 987 * @param serviceUuid the service uuid to filter against 988 * @param serviceMask the service mask to filter against 989 * @throws Exception 990 */ 991 @Rpc(description = "Add filter \"serviceUuid and/or serviceMask\" to existing ScanFilter") bleSetScanFilterServiceUuid( @pcParametername = "serviceUuid") String serviceUuid, @RpcParameter(name = "serviceMask") @RpcOptional String serviceMask )992 public void bleSetScanFilterServiceUuid( 993 @RpcParameter(name = "serviceUuid") 994 String serviceUuid, 995 @RpcParameter(name = "serviceMask") 996 @RpcOptional 997 String serviceMask 998 ) { 999 if (serviceMask != null) { 1000 mScanFilterBuilder 1001 .setServiceUuid(ParcelUuid.fromString(serviceUuid), 1002 ParcelUuid.fromString(serviceMask)); 1003 } else { 1004 mScanFilterBuilder.setServiceUuid(ParcelUuid.fromString(serviceUuid)); 1005 } 1006 } 1007 1008 /** 1009 * Add filter "device name" to existing ScanFilter 1010 * 1011 * @param name the device name to filter against 1012 * @throws Exception 1013 */ 1014 @Rpc(description = "Sets the scan filter's device name") bleSetScanFilterDeviceName( @pcParametername = "name") String name )1015 public void bleSetScanFilterDeviceName( 1016 @RpcParameter(name = "name") 1017 String name 1018 ) { 1019 mScanFilterBuilder.setDeviceName(name); 1020 } 1021 1022 @Rpc(description = "Set the scan setting's match mode") bleSetScanSettingsMatchMode( @pcParametername = "mode") Integer mode)1023 public void bleSetScanSettingsMatchMode( 1024 @RpcParameter(name = "mode") Integer mode) { 1025 mScanSettingsBuilder.setMatchMode(mode); 1026 } 1027 1028 @Rpc(description = "Get the scan setting's match mode") bleGetScanSettingsMatchMode( @pcParametername = "scanSettingsIndex") Integer scanSettingsIndex )1029 public int bleGetScanSettingsMatchMode( 1030 @RpcParameter(name = "scanSettingsIndex") Integer scanSettingsIndex 1031 ) { 1032 return mScanSettingsList.get(scanSettingsIndex).getMatchMode(); 1033 } 1034 1035 @Rpc(description = "Set the scan setting's number of matches") bleSetScanSettingsNumOfMatches( @pcParametername = "matches") Integer matches)1036 public void bleSetScanSettingsNumOfMatches( 1037 @RpcParameter(name = "matches") Integer matches) { 1038 mScanSettingsBuilder.setNumOfMatches(matches); 1039 } 1040 1041 @Rpc(description = "Get the scan setting's number of matches") bleGetScanSettingsNumberOfMatches( @pcParametername = "scanSettingsIndex") Integer scanSettingsIndex)1042 public int bleGetScanSettingsNumberOfMatches( 1043 @RpcParameter(name = "scanSettingsIndex") 1044 Integer scanSettingsIndex) { 1045 return mScanSettingsList.get(scanSettingsIndex).getNumOfMatches(); 1046 } 1047 1048 private class myScanCallback extends ScanCallback { 1049 public Integer index; 1050 String mEventType; 1051 private final Bundle mResults; 1052 myScanCallback(Integer idx)1053 public myScanCallback(Integer idx) { 1054 index = idx; 1055 mEventType = "BleScan"; 1056 mResults = new Bundle(); 1057 } 1058 1059 @Override onScanFailed(int errorCode)1060 public void onScanFailed(int errorCode) { 1061 String errorString = "UNKNOWN_ERROR_CODE"; 1062 if (errorCode == ScanCallback.SCAN_FAILED_ALREADY_STARTED) { 1063 errorString = "SCAN_FAILED_ALREADY_STARTED"; 1064 } else if (errorCode 1065 == ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED) { 1066 errorString = "SCAN_FAILED_APPLICATION_REGISTRATION_FAILED"; 1067 } else if (errorCode 1068 == ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED) { 1069 errorString = "SCAN_FAILED_FEATURE_UNSUPPORTED"; 1070 } else if (errorCode == ScanCallback.SCAN_FAILED_INTERNAL_ERROR) { 1071 errorString = "SCAN_FAILED_INTERNAL_ERROR"; 1072 } 1073 Log.d("bluetooth_le_scan change onScanFailed " 1074 + mEventType + " " + index + " error " 1075 + errorString); 1076 mResults.putInt("ID", index); 1077 mResults.putString("Type", "onScanFailed"); 1078 mResults.putInt("ErrorCode", errorCode); 1079 mResults.putString("Error", errorString); 1080 mEventFacade.postEvent(mEventType + index + "onScanFailed", 1081 mResults.clone()); 1082 mResults.clear(); 1083 } 1084 1085 @Override onScanResult(int callbackType, ScanResult result)1086 public void onScanResult(int callbackType, ScanResult result) { 1087 Log.d("bluetooth_le_scan change onUpdate " 1088 + mEventType + " " + index); 1089 mResults.putInt("ID", index); 1090 mResults.putInt("CallbackType", callbackType); 1091 mResults.putString("Type", "onScanResult"); 1092 mResults.putParcelable("Result", result); 1093 mEventFacade.postEvent( 1094 mEventType + index + "onScanResults", mResults.clone()); 1095 mResults.clear(); 1096 } 1097 1098 @Override onBatchScanResults(List<ScanResult> results)1099 public void onBatchScanResults(List<ScanResult> results) { 1100 Log.d("reportResult " + mEventType + " " + index); 1101 mResults.putLong("Timestamp", System.currentTimeMillis() / 1000); 1102 mResults.putInt("ID", index); 1103 mResults.putString("Type", "onBatchScanResults"); 1104 mResults.putParcelableList("Results", results); 1105 mEventFacade.postEvent(mEventType 1106 + index + "onBatchScanResult", mResults.clone()); 1107 mResults.clear(); 1108 } 1109 } 1110 1111 private class myLeScanCallback implements LeScanCallback { 1112 public Integer index; 1113 String mEventType; 1114 private final Bundle mResults; 1115 myLeScanCallback(Integer idx)1116 public myLeScanCallback(Integer idx) { 1117 index = idx; 1118 mEventType = "ClassicBleScan"; 1119 mResults = new Bundle(); 1120 } 1121 1122 @Override onLeScan( BluetoothDevice device, int rssi, byte[] scanRecord)1123 public void onLeScan( 1124 BluetoothDevice device, int rssi, byte[] scanRecord) { 1125 Log.d("bluetooth_classic_le_scan " + mEventType + " " + index); 1126 mResults.putParcelable("Device", device); 1127 mResults.putInt("Rssi", rssi); 1128 mResults.putByteArray("ScanRecord", scanRecord); 1129 mResults.putString("Type", "onLeScan"); 1130 mEventFacade.postEvent(mEventType 1131 + index + "onLeScan", mResults.clone()); 1132 mResults.clear(); 1133 } 1134 } 1135 1136 @Override shutdown()1137 public void shutdown() { 1138 if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { 1139 for (myScanCallback mScanCallback : mScanCallbackList.values()) { 1140 if (mScanCallback != null) { 1141 try { 1142 mBluetoothAdapter.getBluetoothLeScanner() 1143 .stopScan(mScanCallback); 1144 } catch (NullPointerException e) { 1145 Log.e("Failed to stop ble scan callback.", e); 1146 } 1147 } 1148 } 1149 for (myLeScanCallback mLeScanCallback : mLeScanCallbackList.values()) { 1150 if (mLeScanCallback != null) { 1151 try { 1152 mBluetoothAdapter.stopLeScan(mLeScanCallback); 1153 } catch (NullPointerException e) { 1154 Log.e("Failed to stop classic ble scan callback.", e); 1155 } 1156 } 1157 } 1158 } 1159 mScanCallbackList.clear(); 1160 mScanFilterList.clear(); 1161 mScanSettingsList.clear(); 1162 mLeScanCallbackList.clear(); 1163 } 1164 } 1165