Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>(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<string>(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();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace CoreBluetooth.Foundation
{
internal class SafeNSArrayHandle : SafeNSObjectHandle
{
public SafeNSArrayHandle() : base() { }
public SafeNSArrayHandle(IntPtr handle) : base(handle) { }
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
using System;
using System.Linq;

namespace CoreBluetooth
{
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<SafeNativePeripheralHandle>(arrayHandle);
return peripheralPtrs.Select(ptr => new SafeNativePeripheralHandle(ptr)).ToArray();
}

public void ScanForPeripherals(string[] serviceUUIDs = null)
{
foreach (string uuidString in serviceUUIDs)
{
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using CoreBluetooth.Foundation;

namespace CoreBluetooth
{
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Original file line number Diff line number Diff line change
@@ -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" }));
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnsafePointer<CChar>?>,
_ peripheralIdsCount: Int32
) -> UnsafeMutableRawPointer {
let instance = Unmanaged<CB4UCentralManager>.fromOpaque(centralPtr).takeUnretainedValue()

let peripheralIdsArray = (0..<Int(peripheralIdsCount)).compactMap { index -> 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnsafeRawPointer?>, _ count: Int32) -> UnsafeMutableRawPointer {
let instance = NSMutableArray()
for i in 0..<count {
let value = Unmanaged<NSObject>.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<NSArray>.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<NSArray>.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()
Expand Down