26 package jau.direct_bt;
28 import java.lang.ref.WeakReference;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.concurrent.atomic.AtomicReference;
61 static final boolean PRINT_DEVICE_LISTS =
false;
63 private static AtomicInteger globThreadID =
new AtomicInteger(0);
64 private static int discoverTimeoutMS = 100;
66 private final int dev_id;
71 private final String name;
77 private final Object discoveryLock =
new Object();
78 private final Object discoveredDevicesLock =
new Object();
80 private final AtomicBoolean isClosing =
new AtomicBoolean(
false);
82 private final AtomicBoolean powered_state =
new AtomicBoolean(
false);
83 private final AtomicBoolean isDiscoverable =
new AtomicBoolean(
false);
85 private final AtomicReference<ScanType> currentMetaScanType =
new AtomicReference<ScanType>(
ScanType.
NONE);
87 private final List<WeakReference<BTDevice>> discoveredDevices =
new ArrayList<WeakReference<BTDevice>>();
90 final byte byteAddress[],
91 final byte byteAddressType,
92 final String name,
final int dev_id)
94 super(nativeInstance, compHash(java.util.Arrays.hashCode(byteAddress), 31+byteAddressType));
98 this.visibleAddressAndType = addressAndType;
111 if( !isClosing.compareAndSet(
false,
true) ) {
120 for(
final Iterator<BTDevice>
id = devices.iterator();
id.hasNext(); ) {
129 mngr.removeAdapter(
this);
134 private final void poweredOff() {
135 powered_state.set(
false);
145 if (obj ==
null || !(obj instanceof
DBTAdapter)) {
149 return dev_id == other.dev_id && addressAndType.
equals(other.addressAndType);
171 return findDeviceInCache(name, addressAndType);
176 return find(name, addressAndType, 0);
181 private final native
boolean isDeviceWhitelistedImpl(
final byte[] address,
byte address_type);
186 final short conn_interval_min,
final short conn_interval_max,
187 final short conn_latency,
final short timeout) {
189 conn_interval_min, conn_interval_max, conn_latency, timeout);
191 private native
boolean addDeviceToWhitelistImpl1(
final byte[] address,
final byte address_type,
final int ctype,
192 final short conn_interval_min,
final short conn_interval_max,
193 final short conn_latency,
final short timeout);
200 private native
boolean addDeviceToWhitelistImpl2(
final byte[] address,
final byte address_type,
final int ctype);
204 return removeDeviceFromWhitelistImpl(addressAndType.
address.
b, addressAndType.
type.
value);
206 private native
boolean removeDeviceFromWhitelistImpl(
final byte[] address,
final byte address_type);
215 return currentMetaScanType.
get();
221 return "Adapter" +
"\u271D" +
"["+addressAndType+
", '"+name+
"', id "+dev_id+
"]";
223 return toStringImpl();
230 private native String toStringImpl();
239 private native
byte resetImpl();
254 private native
BTDevice connectDeviceImpl(
byte[] address,
byte addressType);
281 return startDiscovery(keepAlive, le_scan_active, (
short)24, (
short)24, (
byte)0);
285 final short le_scan_interval,
final short le_scan_window,
287 synchronized( discoveryLock ) {
290 removeDiscoveredDevicesImpl2j();
291 final HCIStatusCode res =
HCIStatusCode.
get( startDiscoveryImpl(keepAlive, le_scan_active, le_scan_interval, le_scan_window, filter_policy) );
292 if( PRINT_DEVICE_LISTS || DEBUG ) {
299 private native
byte startDiscoveryImpl(
final boolean keepAlive,
final boolean le_scan_active,
300 final short le_scan_interval,
final short le_scan_window,
305 synchronized( discoveryLock ) {
308 if( PRINT_DEVICE_LISTS || DEBUG ) {
315 private native
byte stopDiscoveryImpl() throws
BTException;
318 private native List<
BTDevice> getDiscoveredDevicesImpl();
322 final int cj = removeDiscoveredDevicesImpl2j();
323 final int cn = removeDiscoveredDevicesImpl1();
326 System.err.println(
"DBTAdapter::removeDevices: Unexpected discovered device count: Native "+cn+
", callback "+cj);
331 private native
int removeDiscoveredDevicesImpl1() throws
BTException;
332 private
int removeDiscoveredDevicesImpl2j() {
333 synchronized(discoveredDevicesLock) {
334 final int n = discoveredDevices.size();
335 discoveredDevices.clear();
339 boolean removeDiscoveredDevice(
final BTDevice device) {
340 return removeDiscoveredDeviceImpl2j( device.getAddressAndType() );
345 final boolean cj = removeDiscoveredDeviceImpl2j(addressAndType);
346 final boolean cn = removeDiscoveredDeviceImpl1(addressAndType.
address.
b, addressAndType.
type.
value);
349 System.err.println(
"DBTAdapter::removeDevices("+addressAndType+
"): Unexpected discovered device count: Native "+cn+
", callback "+cj);
354 private native
boolean removeDiscoveredDeviceImpl1(
final byte[] address,
final byte addressType);
356 private boolean removeDiscoveredDeviceImpl2j(
final BDAddressAndType addressAndType) {
357 synchronized(discoveredDevicesLock) {
358 for(
final Iterator<WeakReference<BTDevice>> it = discoveredDevices.iterator(); it.hasNext();) {
372 final ArrayList<BTDevice> res =
new ArrayList<BTDevice>();
373 synchronized(discoveredDevicesLock) {
374 for(
final Iterator<WeakReference<BTDevice>> it = discoveredDevices.iterator(); it.hasNext();) {
385 private void cleanDiscoveredDevice() {
386 synchronized(discoveredDevicesLock) {
387 for(
final Iterator<WeakReference<BTDevice>> it = discoveredDevices.iterator(); it.hasNext();) {
398 final boolean added = addStatusListenerImpl(
null, l);
399 if( PRINT_DEVICE_LISTS || DEBUG ) {
410 if( !isClosing.get() ) {
411 res = removeStatusListenerImpl(l);
413 if( PRINT_DEVICE_LISTS || DEBUG ) {
426 printDeviceListsImpl();
427 List<WeakReference<BTDevice>> _discoveredDevices;
428 synchronized(discoveredDevicesLock) {
430 _discoveredDevices =
new ArrayList<WeakReference<BTDevice>>(discoveredDevices);
432 final int sz = _discoveredDevices.size();
433 BTUtils.
fprintf_td(System.err,
"- BTAdapter::DiscoveredDevicesJ: %d elements%s", sz, System.lineSeparator());
435 for(
final Iterator<WeakReference<BTDevice>> it = _discoveredDevices.iterator(); it.hasNext(); ++idx) {
438 BTUtils.
fprintf_td(System.err,
" - %d / %d: nil%s", (idx+1), sz, System.lineSeparator());
445 private final native
void printDeviceListsImpl();
453 final boolean initialSetting = oldmask.
isEmpty();
455 if( initialSetting ) {
456 System.err.println(
"Adapter.StatusListener.SETTINGS: "+oldmask+
" -> "+newmask+
", initial "+changedmask+
" on "+a);
458 System.err.println(
"Adapter.StatusListener.SETTINGS: "+oldmask+
" -> "+newmask+
", changed "+changedmask+
" on "+a);
461 if( initialSetting ) {
462 powered_state.set( newmask.
isSet(AdapterSettings.SettingType.POWERED) );
463 isDiscoverable.set( newmask.
isSet(AdapterSettings.SettingType.DISCOVERABLE) );
466 if( changedmask.
isSet(AdapterSettings.SettingType.POWERED) ) {
467 final boolean _isPowered = newmask.
isSet(AdapterSettings.SettingType.POWERED);
468 if( powered_state.compareAndSet(!_isPowered, _isPowered) ) {
474 if( changedmask.
isSet(AdapterSettings.SettingType.DISCOVERABLE) ) {
475 final boolean _isDiscoverable = newmask.
isSet(AdapterSettings.SettingType.DISCOVERABLE);
476 if( isDiscoverable.compareAndSet(!_isDiscoverable, _isDiscoverable) ) {
481 public void discoveringChanged(
final BTAdapter adapter,
final ScanType currentMeta,
final ScanType changedType,
final boolean changedEnabled,
final boolean keepAlive,
final long timestamp) {
483 System.err.println(
"Adapter.StatusListener.DISCOVERING: meta "+currentMeta+
", changed["+changedType+
", enabled "+changedEnabled+
", keepAlive "+keepAlive+
"] on "+adapter);
486 currentMetaScanType.set(currentMeta);
489 public boolean deviceFound(
final BTDevice device,
final long timestamp) {
490 synchronized(discoveredDevicesLock) {
491 cleanDiscoveredDevice();
492 discoveredDevices.add(
new WeakReference<BTDevice>(device));
494 if( PRINT_DEVICE_LISTS || DEBUG ) {
495 System.err.println(
"Adapter.FOUND: discoveredDevices "+ discoveredDevices.size() +
": "+device+
", on "+device.getAdapter());
501 public void deviceUpdated(
final BTDevice device,
final EIRDataTypeSet updateMask,
final long timestamp) {
502 final boolean rssiUpdated = updateMask.isSet( EIRDataTypeSet.DataType.RSSI );
503 final boolean mdUpdated = updateMask.isSet( EIRDataTypeSet.DataType.MANUF_DATA );
504 if( DEBUG && !rssiUpdated && !mdUpdated) {
505 System.err.println(
"Adapter.UPDATED: "+updateMask+
" of "+device+
" on "+device.getAdapter());
511 public void deviceConnected(
final BTDevice device,
final short handle,
final long timestamp) {
513 System.err.println(
"Adapter.CONNECTED: "+device+
" on "+device.getAdapter());
518 public void devicePairingState(
final BTDevice device,
final SMPPairingState state,
final PairingMode mode,
final long timestamp) {
520 System.err.println(
"Adapter.PAIRING_STATE: state "+state+
", mode "+mode+
": "+device);
525 public void deviceReady(
final BTDevice device,
final long timestamp) {
527 System.err.println(
"Adapter.READY: "+device);
532 public void deviceDisconnected(
final BTDevice device,
final HCIStatusCode reason,
final short handle,
final long timestamp) {
534 System.err.println(
"Adapter.DISCONNECTED: Reason "+reason+
", old handle 0x"+Integer.toHexString(handle)+
": "+device+
" on "+device.getAdapter());
540 return "AdapterStatusListener[adapter "+addressAndType.
toString()+
"]";
576 DBTObject findInCache(
final String name,
final String identifier,
final BTType type) {
577 final boolean anyType = BTType.NONE == type;
578 final boolean deviceType = BTType.DEVICE == type;
579 final boolean serviceType = BTType.GATT_SERVICE == type;
580 final boolean charType = BTType.GATT_CHARACTERISTIC== type;
581 final boolean descType = BTType.GATT_DESCRIPTOR == type;
583 if( !anyType && !deviceType && !serviceType && !charType && !descType ) {
586 synchronized(discoveredDevicesLock) {
587 cleanDiscoveredDevice();
589 if(
null == name &&
null == identifier && ( anyType || deviceType ) ) {
591 if( discoveredDevices.size() > 0 ) {
592 return (DBTDevice) discoveredDevices.get(0).get();
596 for(
int devIdx = discoveredDevices.size() - 1; devIdx >= 0; devIdx-- ) {
597 final DBTDevice device = (DBTDevice) discoveredDevices.get(devIdx).get();
598 if( ( anyType || deviceType ) ) {
599 if(
null != name &&
null != identifier &&
600 device.getName().equals(name) &&
601 device.getAddressAndType().address.toString().equals(identifier)
606 if(
null != identifier &&
607 device.getAddressAndType().address.toString().equals(identifier)
613 device.getName().equals(name)
619 if( anyType || serviceType || charType || descType ) {
620 final DBTObject dbtObj = device.findInCache(identifier, type);
621 if(
null != dbtObj ) {
630 DBTDevice findDeviceInCache(
final String name,
final BDAddressAndType addressAndType) {
631 synchronized(discoveredDevicesLock) {
632 cleanDiscoveredDevice();
634 if(
null == name &&
null == addressAndType ) {
636 if( discoveredDevices.size() > 0 ) {
637 return (DBTDevice) discoveredDevices.get(0).get();
641 for(
int devIdx = discoveredDevices.size() - 1; devIdx >= 0; devIdx-- ) {
642 final DBTDevice device = (DBTDevice) discoveredDevices.get(devIdx).get();
643 if(
null != name &&
null != addressAndType &&
644 device.getName().equals(name) &&
645 device.getAddressAndType().equals(addressAndType)
650 if(
null != addressAndType &&
651 device.getAddressAndType().equals(addressAndType)
657 device.getName().equals(name)