diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity index 948bd64..23d426d 100755 Binary files a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity and b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity differ diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib index 68168c8..082fb10 100755 Binary files a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib and b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib differ diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs index cedb0ba..fdd9402 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs @@ -69,6 +69,25 @@ public void CancelPeripheralConnection(CBPeripheral peripheral) _nativeCentralManagerProxy.CancelPeripheralConnection(peripheral); } + public CBPeripheral[] RetrievePeripherals(params string[] peripheralIds) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); + var peripheralHandles = _nativeCentralManagerProxy.RetrievePeripherals(peripheralIds); + var result = new CBPeripheral[peripheralHandles.Length]; + for (var i = 0; i < peripheralHandles.Length; i++) + { + var peripheral = new CBPeripheral(peripheralHandles[i]); + if (_peripherals.ContainsKey(peripheral.Identifier)) + { + _peripherals[peripheral.Identifier].Dispose(); + } + + _peripherals[peripheral.Identifier] = peripheral; + result[i] = peripheral; + } + return result; + } + public void ScanForPeripherals(string[] serviceUUIDs = null) { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs new file mode 100644 index 0000000..79084fe --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs @@ -0,0 +1,89 @@ +using System; +using System.Linq; + +namespace CoreBluetooth.Foundation +{ + internal class NSArray : IDisposable + { + public SafeNSArrayHandle Handle { get; private set; } + + public NSArray(SafeNSArrayHandle handle) + { + Handle = handle; + } + + public static NSArray FromStrings(params string[] strings) + { + if (strings is null) + throw new ArgumentNullException(nameof(strings)); + + var nsstrings = new NSString[strings.Length]; + for (var i = 0; i < strings.Length; i++) + { + if (strings[i] == null) + throw new ArgumentNullException(nameof(strings)); + nsstrings[i] = new NSString(strings[i]); + } + + var nsarray = FromNSObjects(nsstrings.Select(s => s.Handle).ToArray()); + foreach (var nsstring in nsstrings) + { + nsstring.Dispose(); + } + return nsarray; + } + + public static NSArray FromNSObjects(params SafeNSObjectHandle[] objects) + { + if (objects is null) + throw new ArgumentNullException(nameof(objects)); + + var values = objects.Select(o => o.DangerousGetHandle()).ToArray(); + var handle = NativeMethods.ns_array_new(values, values.Length); + return new NSArray(handle); + } + + public static IntPtr[] ArrayFromHandle(SafeNSArrayHandle handle) + { + if (handle.IsInvalid) + return null; + + var count = GetCount(handle); + var array = new IntPtr[count]; + for (var i = 0; i < count; i++) + { + var ptr = GetAtIndex(handle, i); + array[i] = ptr; + } + return array; + } + + public static string[] StringsFromHandle(SafeNSArrayHandle handle) + { + var ptrs = ArrayFromHandle(handle); + var array = new string[ptrs.Length]; + for (var i = 0; i < ptrs.Length; i++) + { + using var nsstring = new NSString(ptrs[i]); + array[i] = nsstring.ToString(); + } + return array; + } + + internal static int GetCount(SafeNSArrayHandle handle) + { + return NativeMethods.ns_array_count(handle); + } + + internal static IntPtr GetAtIndex(SafeNSArrayHandle handle, int index) + { + return NativeMethods.ns_array_get_at_index(handle, index); + } + + public void Dispose() + { + if (Handle != null && !Handle.IsInvalid) + Handle.Dispose(); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs.meta new file mode 100644 index 0000000..f56371e --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 187f6f6dc24974c34b6b6cb1923c892e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs index 9934910..b8b4f33 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs @@ -35,6 +35,15 @@ internal static class NativeMethods [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void ns_string_get_cstring_and_length(SafeNSStringHandle handle, out IntPtr ptr, out int length); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSArrayHandle ns_array_new(IntPtr[] values, int count); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern int ns_array_count(SafeNSArrayHandle handle); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ns_array_get_at_index(SafeNSArrayHandle handle, int index); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern SafeNSMutableDictionaryHandle ns_mutable_dictionary_new(); diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs new file mode 100644 index 0000000..5b1bfe5 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs @@ -0,0 +1,10 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class SafeNSArrayHandle : SafeNSObjectHandle + { + public SafeNSArrayHandle() : base() { } + public SafeNSArrayHandle(IntPtr handle) : base(handle) { } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs.meta new file mode 100644 index 0000000..f5ed1f0 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSArrayHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28922a71712b44ef09ebdaaecf45e813 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeCentralManagerProxy.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeCentralManagerProxy.cs index 1af5781..e663718 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeCentralManagerProxy.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeCentralManagerProxy.cs @@ -1,4 +1,4 @@ -using System; +using System.Linq; namespace CoreBluetooth { @@ -6,23 +6,30 @@ internal class NativeCentralManagerProxy { readonly SafeNativeCentralManagerHandle _handle; - internal NativeCentralManagerProxy(SafeNativeCentralManagerHandle handle, INativeCentralManagerDelegate centralManagerDelegate) + public NativeCentralManagerProxy(SafeNativeCentralManagerHandle handle, INativeCentralManagerDelegate centralManagerDelegate) { _handle = handle; _handle.SetDelegate(centralManagerDelegate); } - internal void Connect(CBPeripheral peripheral) + public void Connect(CBPeripheral peripheral) { NativeMethods.cb4u_central_manager_connect(_handle, peripheral.Handle); } - internal void CancelPeripheralConnection(CBPeripheral peripheral) + public void CancelPeripheralConnection(CBPeripheral peripheral) { NativeMethods.cb4u_central_manager_cancel_peripheral_connection(_handle, peripheral.Handle); } - internal void ScanForPeripherals(string[] serviceUUIDs = null) + public SafeNativePeripheralHandle[] RetrievePeripherals(string[] peripheralIds) + { + var arrayHandle = NativeMethods.cb4u_central_manager_retrieve_peripherals(_handle, peripheralIds, peripheralIds.Length); + var peripheralPtrs = Foundation.NSArray.ArrayFromHandle(arrayHandle); + return peripheralPtrs.Select(ptr => new SafeNativePeripheralHandle(ptr)).ToArray(); + } + + public void ScanForPeripherals(string[] serviceUUIDs = null) { foreach (string uuidString in serviceUUIDs) { @@ -36,12 +43,12 @@ internal void ScanForPeripherals(string[] serviceUUIDs = null) ); } - internal void StopScan() + public void StopScan() { NativeMethods.cb4u_central_manager_stop_scan(_handle); } - internal bool IsScanning() + public bool IsScanning() { return NativeMethods.cb4u_central_manager_is_scanning(_handle); } diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs index 3e264d1..57064fd 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using System.Text; +using CoreBluetooth.Foundation; namespace CoreBluetooth { @@ -52,6 +53,13 @@ CB4UCentralManagerDidUpdateStateHandler didUpdateStateHandler [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern int cb4u_central_manager_cancel_peripheral_connection(SafeNativeCentralManagerHandle handle, SafeNativePeripheralHandle peripheralHandle); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSArrayHandle cb4u_central_manager_retrieve_peripherals( + SafeNativeCentralManagerHandle handle, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 2)] string[] peripheralIds, + int peripheralIdsCount + ); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void cb4u_central_manager_scan_for_peripherals( SafeNativeCentralManagerHandle handle, diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs index 3e0fe8f..3243b1b 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs @@ -76,5 +76,17 @@ public IEnumerator ScanStartStop() centralManager.StopScan(); Assert.That(centralManager.IsScanning, Is.False); } + + [UnityTest] + public IEnumerator RetrievePeripherals() + { + using var centralManager = new CBCentralManager(); + yield return WaitUntilWithTimeout(() => centralManager.State != CBManagerState.Unknown, 1f); + if (centralManager.State != CBManagerState.PoweredOn) yield break; + + var peripherals = centralManager.RetrievePeripherals(validUUID1); + Assert.That(peripherals, Is.Not.Null); + Assert.That(peripherals.Length, Is.EqualTo(0)); + } } } diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs new file mode 100644 index 0000000..9d7b788 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs @@ -0,0 +1,24 @@ +using CoreBluetooth.Foundation; +using NUnit.Framework; + +namespace CoreBluetoothTests.Foundation +{ + public class NSArrayTests + { + [Test] + public void FromStrings() + { + using var nsArray = NSArray.FromStrings(new[] { "hoge", "fuga" }); + Assert.That(nsArray.Handle, Is.Not.Null); + Assert.That(nsArray.Handle.IsInvalid, Is.False); + } + + [Test] + public void StringsFromHandle() + { + using var nsArray = NSArray.FromStrings(new[] { "hoge", "fuga" }); + var strings = NSArray.StringsFromHandle(nsArray.Handle); + Assert.That(strings, Is.EqualTo(new[] { "hoge", "fuga" })); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs.meta new file mode 100644 index 0000000..012861e --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSArrayTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6730ea2d07ec4b86bd1a75caf26c8fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift index 45b2af4..4a9cd2d 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift @@ -41,6 +41,17 @@ public class CB4UCentralManager : NSObject { public func cancelPeripheralConnection(peripheral: CB4UPeripheral) { centralManager.cancelPeripheralConnection(peripheral.peripheral) } + + public func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> NSMutableArray { + let peripherals = centralManager.retrievePeripherals(withIdentifiers: identifiers) + let array = NSMutableArray() + for peripheral in peripherals { + let cb4u_peripheral = CB4UPeripheral(peripheral: peripheral) + peripheral.delegate = cb4u_peripheral + array.add(cb4u_peripheral) + } + return array + } public func scanForPeripherals(withServices serviceUUIDs: [CBUUID]?) { centralManager.scanForPeripherals(withServices: serviceUUIDs) diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift index 6c5fdcd..f1cf260 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift @@ -83,6 +83,26 @@ public func cb4u_central_manager_cancel_peripheral_connection(_ centralPtr: Unsa instance.cancelPeripheralConnection(peripheral: peripheral) } +@_cdecl("cb4u_central_manager_retrieve_peripherals") +public func cb4u_central_manager_retrieve_peripherals( + _ centralPtr: UnsafeRawPointer, + _ peripheralIds: UnsafePointer?>, + _ peripheralIdsCount: Int32 +) -> UnsafeMutableRawPointer { + let instance = Unmanaged.fromOpaque(centralPtr).takeUnretainedValue() + + let peripheralIdsArray = (0.. UUID? in + let uuidString = String(cString: peripheralIds[index]!) + guard let uuid = UUID(uuidString: uuidString) else { + return nil + } + return uuid + } + + let peripherals = instance.retrievePeripherals(withIdentifiers: peripheralIdsArray) + return Unmanaged.passRetained(peripherals).toOpaque() +} + @_cdecl("cb4u_central_manager_scan_for_peripherals") public func cb4u_central_manager_scan_for_peripherals( _ centralPtr: UnsafeRawPointer, diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift index 348db59..3d6da2f 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift @@ -53,6 +53,29 @@ public func ns_string_get_cstring_and_length(_ handle: UnsafeRawPointer, _ ptr: } } +@_cdecl("ns_array_new") +public func ns_array_new(_ values: UnsafePointer, _ count: Int32) -> UnsafeMutableRawPointer { + let instance = NSMutableArray() + for i in 0...fromOpaque(values[Int(i)]!).takeUnretainedValue() + instance.add(value) + } + return Unmanaged.passRetained(instance).toOpaque() +} + +@_cdecl("ns_array_count") +public func ns_array_count(_ handle: UnsafeRawPointer) -> Int32 { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + return Int32(instance.count) +} + +@_cdecl("ns_array_get_at_index") +public func ns_array_get_at_index(_ handle: UnsafeRawPointer, _ index: Int32) -> UnsafeMutableRawPointer { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + let value = instance[Int(index)] as! NSObject + return Unmanaged.passRetained(value).toOpaque() +} + @_cdecl("ns_mutable_dictionary_new") public func ns_mutable_dictionary_new() -> UnsafeMutableRawPointer { let instance = NSMutableDictionary()