diff --git a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs index 2e97394..3d72df8 100644 --- a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs +++ b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Text; using CoreBluetooth; using UnityEngine; @@ -155,6 +156,12 @@ public void DidUpdateName(CBPeripheral peripheral) Debug.Log($"[DidUpdateName] {peripheral}"); } + public void DidModifyServices(CBPeripheral peripheral, CBService[] services) + { + var serviceIds = services.Select(s => s.UUID.ToString()).ToArray(); + Debug.Log($"[DidModifyServices] services count: {services.Length} serviceIds: {string.Join(", ", serviceIds)}"); + } + public void OnClickWrite() { if (_peripheral == null) diff --git a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Peripheral.cs b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Peripheral.cs index dd31f5c..c1454e5 100644 --- a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Peripheral.cs +++ b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Peripheral.cs @@ -136,6 +136,11 @@ public void OnClickNotify() _peripheralManager.UpdateValue(_value, _characteristic, new CBCentral[] { _central }); } + public void OnClickRemoveAllServices() + { + _peripheralManager.RemoveAllServices(); + } + void OnDestroy() { foreach (var disposable in _disposables) diff --git a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_PeripheralScene.unity b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_PeripheralScene.unity index e005674..074df1c 100644 --- a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_PeripheralScene.unity +++ b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_PeripheralScene.unity @@ -324,6 +324,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 351522879} m_CullTransparentMesh: 1 +--- !u!1 &469850631 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 469850632} + - component: {fileID: 469850634} + - component: {fileID: 469850633} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &469850632 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 469850631} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1946430623} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &469850633 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 469850631} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 31 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: RemoveAllServices +--- !u!222 &469850634 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 469850631} + m_CullTransparentMesh: 1 --- !u!1 &662800339 GameObject: m_ObjectHideFlags: 0 @@ -763,6 +842,7 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1464279507} + - {fileID: 1946430623} - {fileID: 1870699850} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -998,6 +1078,139 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0b2fe08bc31c347a2a3c989871aa4ba4, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1 &1946430622 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1946430623} + - component: {fileID: 1946430626} + - component: {fileID: 1946430625} + - component: {fileID: 1946430624} + m_Layer: 5 + m_Name: RemoveAllServicesButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1946430623 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946430622} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 469850632} + m_Father: {fileID: 994619686} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -116} + m_SizeDelta: {x: 320, y: 60} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1946430624 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946430622} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1946430625} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1378470631} + m_TargetAssemblyTypeName: CoreBluetoothSample.SampleDebug_Peripheral, Assembly-CSharp + m_MethodName: OnClickRemoveAllServices + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1946430625 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946430622} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1946430626 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946430622} + m_CullTransparentMesh: 1 --- !u!1 &2006063358 GameObject: m_ObjectHideFlags: 0 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 fc4a0b9..8eb805a 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 c153b56..e37f7eb 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/CBPeripheral.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheral.cs index 5885822..ba68581 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheral.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheral.cs @@ -23,6 +23,7 @@ void IsReadyToSendWriteWithoutResponse(CBPeripheral peripheral) { } void DidUpdateNotificationStateForCharacteristic(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error) { } void DidReadRSSI(CBPeripheral peripheral, int rssi, CBError error) { } void DidUpdateName(CBPeripheral peripheral) { } + void DidModifyServices(CBPeripheral peripheral, CBService[] services) { } } /// @@ -254,6 +255,23 @@ void INativePeripheralDelegate.DidUpdateName() Delegate?.DidUpdateName(this); } + void INativePeripheralDelegate.DidModifyServices(string[] invalidatedServiceUUIDs) + { + if (_disposed) return; + List invalidatedServices = new List(); + foreach (var uuid in invalidatedServiceUUIDs) + { + var service = _services.FirstOrDefault(s => s.UUID == uuid); + if (service != null) + { + invalidatedServices.Add(service); + _services.Remove(service); + } + } + + Delegate?.DidModifyServices(this, invalidatedServices.ToArray()); + } + public override string ToString() { return $"CBPeripheral: identifier = {Identifier}, name = {Name}, state = {State}"; diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheralManager.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheralManager.cs index 39a2a69..be6c805 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheralManager.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBPeripheralManager.cs @@ -74,7 +74,26 @@ public void AddService(CBMutableService service) _addingServiceUUIDs.Add(service.UUID); _services.Add(service.UUID, service); - _nativePeripheralManagerProxy.AddService(service); + _nativePeripheralManagerProxy.AddService(service.Handle); + } + + public void RemoveService(CBMutableService service) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); + if (!_services.ContainsKey(service.UUID)) + { + throw new ArgumentException($"Service {service} is not added."); + } + + _services.Remove(service.UUID); + _nativePeripheralManagerProxy.RemoveService(service.Handle); + } + + public void RemoveAllServices() + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); + _services.Clear(); + _nativePeripheralManagerProxy.RemoveAllServices(); } public void StartAdvertising(StartAdvertisingOptions options = null) 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 aec1c8d..6c9210a 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs @@ -78,6 +78,7 @@ int serviceUUIDsCount internal delegate void CB4UPeripheralDidUpdateNotificationStateForCharacteristicHandler(IntPtr peripheralPtr, IntPtr serviceUUIDPtr, IntPtr characteristicUUIDPtr, int notificationState, int errorCode); internal delegate void CB4UPeripheralDidReadRSSIHandler(IntPtr peripheralPtr, int rssi, int errorCode); internal delegate void CB4UPeripheralDidUpdateNameHandler(IntPtr peripheralPtr); + internal delegate void CB4UPeripheralDidModifyServicesHandler(IntPtr peripheralPtr, IntPtr commaSeparatedServiceUUIDsPtr); [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void cb4u_peripheral_register_handlers( @@ -89,7 +90,8 @@ internal static extern void cb4u_peripheral_register_handlers( CB4UPeripheralIsReadyToSendWriteWithoutResponseHandler isReadyToSendWriteWithoutResponseHandler, CB4UPeripheralDidUpdateNotificationStateForCharacteristicHandler didUpdateNotificationStateForCharacteristicHandler, CB4UPeripheralDidReadRSSIHandler didReadRSSIHandler, - CB4UPeripheralDidUpdateNameHandler didUpdateNameHandler + CB4UPeripheralDidUpdateNameHandler didUpdateNameHandler, + CB4UPeripheralDidModifyServicesHandler didModifyServicesHandler ); [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] @@ -200,6 +202,15 @@ internal static extern int cb4u_peripheral_manager_add_service( SafeNativeMutableServiceHandle serviceHandle ); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void cb4u_peripheral_manager_remove_service( + SafeNativePeripheralManagerHandle peripheralHandle, + SafeNativeMutableServiceHandle serviceHandle + ); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void cb4u_peripheral_manager_remove_all_services(SafeNativePeripheralManagerHandle peripheralHandle); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void cb4u_peripheral_manager_start_advertising( SafeNativePeripheralManagerHandle peripheralHandle, diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativePeripheralManagerProxy.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativePeripheralManagerProxy.cs index 85cb63e..73e7571 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativePeripheralManagerProxy.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativePeripheralManagerProxy.cs @@ -13,9 +13,19 @@ internal NativePeripheralManagerProxy(SafeNativePeripheralManagerHandle handle, _handle.SetDelegate(peripheralManagerDelegate); } - internal void AddService(CBMutableService service) + internal void AddService(SafeNativeMutableServiceHandle service) { - NativeMethods.cb4u_peripheral_manager_add_service(_handle, service.Handle); + NativeMethods.cb4u_peripheral_manager_add_service(_handle, service); + } + + internal void RemoveService(SafeNativeMutableServiceHandle service) + { + NativeMethods.cb4u_peripheral_manager_remove_service(_handle, service); + } + + internal void RemoveAllServices() + { + NativeMethods.cb4u_peripheral_manager_remove_all_services(_handle); } internal void StartAdvertising(StartAdvertisingOptions options = null) diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativePeripheralHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativePeripheralHandle.cs index 85b5603..17a9596 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativePeripheralHandle.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativePeripheralHandle.cs @@ -14,6 +14,7 @@ void IsReadyToSendWriteWithoutResponse() { } void DidUpdateNotificationStateForCharacteristic(string serviceUUID, string characteristicUUID, bool enabled, CBError error) { } void DidReadRSSI(int rssi, CBError error) { } void DidUpdateName() { } + void DidModifyServices(string[] invalidatedServiceUUIDs) { } } internal class SafeNativePeripheralHandle : SafeHandle @@ -38,7 +39,8 @@ void RegisterHandlers() IsReadyToSendWriteWithoutResponse, DidUpdateNotificationStateForCharacteristic, DidReadRSSI, - DidUpdateName + DidUpdateName, + DidModifyServices ); } @@ -149,5 +151,19 @@ internal static void DidUpdateName(IntPtr peripheralPtr) { GetDelegate(peripheralPtr)?.DidUpdateName(); } + + [AOT.MonoPInvokeCallback(typeof(NativeMethods.CB4UPeripheralDidModifyServicesHandler))] + internal static void DidModifyServices(IntPtr peripheralPtr, IntPtr commaSeparatedServiceUUIDsPtr) + { + string commaSeparatedServiceUUIDs = Marshal.PtrToStringUTF8(commaSeparatedServiceUUIDsPtr); + if (string.IsNullOrEmpty(commaSeparatedServiceUUIDs)) + { + throw new ArgumentException("commaSeparatedServiceUUIDs is null or empty."); + } + + GetDelegate(peripheralPtr)?.DidModifyServices( + commaSeparatedServiceUUIDs.Split(',') + ); + } } } diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBPeripheralManagerTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBPeripheralManagerTests.cs index 12b18e3..1890967 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBPeripheralManagerTests.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBPeripheralManagerTests.cs @@ -72,6 +72,46 @@ public IEnumerator AddService() Assert.That(delegateMock.AddedService, Is.EqualTo(service)); } + [UnityTest] + public IEnumerator RemoveService() + { + var delegateMock = new CBPeripheralManagerDelegateMock(); + using var peripheralManager = new CBPeripheralManager(delegateMock); + + yield return WaitUntilWithTimeout(() => delegateMock.State != CBManagerState.Unknown, 1f); + if (delegateMock.State != CBManagerState.PoweredOn) yield break; + + using var service = new CBMutableService(validUUID1, true); + using var characteristic1 = new CBMutableCharacteristic(validUUID2, CBCharacteristicProperties.Read, null, CBAttributePermissions.Readable); + using var characteristic2 = new CBMutableCharacteristic(validUUID3, CBCharacteristicProperties.Write, null, CBAttributePermissions.Writeable); + var characteristics = new CBMutableCharacteristic[] { characteristic1, characteristic2 }; + service.Characteristics = characteristics; + peripheralManager.AddService(service); + + yield return WaitUntilWithTimeout(() => delegateMock.AddedService != null, 1f); + peripheralManager.RemoveService(service); + } + + [UnityTest] + public IEnumerator RemoveAllServices() + { + var delegateMock = new CBPeripheralManagerDelegateMock(); + using var peripheralManager = new CBPeripheralManager(delegateMock); + + yield return WaitUntilWithTimeout(() => delegateMock.State != CBManagerState.Unknown, 1f); + if (delegateMock.State != CBManagerState.PoweredOn) yield break; + + using var service = new CBMutableService(validUUID1, true); + using var characteristic1 = new CBMutableCharacteristic(validUUID2, CBCharacteristicProperties.Read, null, CBAttributePermissions.Readable); + using var characteristic2 = new CBMutableCharacteristic(validUUID3, CBCharacteristicProperties.Write, null, CBAttributePermissions.Writeable); + var characteristics = new CBMutableCharacteristic[] { characteristic1, characteristic2 }; + service.Characteristics = characteristics; + peripheralManager.AddService(service); + + yield return WaitUntilWithTimeout(() => delegateMock.AddedService != null, 1f); + peripheralManager.RemoveAllServices(); + } + [UnityTest] public IEnumerator Advertising() { diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheral.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheral.swift index 4b18161..dafb882 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheral.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheral.swift @@ -11,6 +11,7 @@ public class CB4UPeripheral : NSObject { public var didUpdateNotificationStateForCharacteristicHandler: CB4UPeripheralDidUpdateNotificationStateForCharacteristicHandler? public var didReadRSSIHandler: CB4UPeripheralDidReadRSSIHandler? public var didUpdateNameHandler: CB4UPeripheralDidUpdateNameHandler? + public var didModifyServicesHandler: CB4UPeripheralDidModifyServicesHandler? let success: Int32 = 0 let serviceNotFound: Int32 = -2 @@ -178,4 +179,12 @@ extension CB4UPeripheral : CBPeripheralDelegate { public func peripheralDidUpdateName(_ peripheral: CBPeripheral) { didUpdateNameHandler?(selfPointer()) } + + public func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) { + let commaSeparatedServiceIds = invalidatedServices.map { $0.uuid.uuidString }.joined(separator: ",") + + commaSeparatedServiceIds.withCString { (commaSeparatedServiceIdsCString) in + didModifyServicesHandler?(selfPointer(), commaSeparatedServiceIdsCString) + } + } } diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheralManager.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheralManager.swift index ff4682e..c737318 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheralManager.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UPeripheralManager.swift @@ -40,6 +40,14 @@ public class CB4UPeripheralManager : NSObject { peripheralManager.add(service.service) } + public func remove(_ service: CB4UMutableService) { + peripheralManager.remove(service.service) + } + + public func removeAllServices() { + peripheralManager.removeAllServices() + } + public func startAdvertising(_ options: StartAdvertisingOptions?) { peripheralManager.startAdvertising(options?.advertisementData()) } diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift index b4a6d66..4c6c542 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift @@ -120,6 +120,7 @@ public typealias CB4UPeripheralIsReadyToSendWriteWithoutResponseHandler = @conve public typealias CB4UPeripheralDidUpdateNotificationStateForCharacteristicHandler = @convention(c) (UnsafeRawPointer, UnsafePointer, UnsafePointer, Int32, Int32) -> Void public typealias CB4UPeripheralDidReadRSSIHandler = @convention(c) (UnsafeRawPointer, Int32, Int32) -> Void public typealias CB4UPeripheralDidUpdateNameHandler = @convention(c) (UnsafeRawPointer) -> Void +public typealias CB4UPeripheralDidModifyServicesHandler = @convention(c) (UnsafeRawPointer, UnsafePointer) -> Void @_cdecl("cb4u_peripheral_register_handlers") public func cb4u_peripheral_register_handlers( @@ -131,7 +132,8 @@ public func cb4u_peripheral_register_handlers( _ isReadyToSendWriteWithoutResponseHandler: @escaping CB4UPeripheralIsReadyToSendWriteWithoutResponseHandler, _ didUpdateNotificationStateForCharacteristicHandler: @escaping CB4UPeripheralDidUpdateNotificationStateForCharacteristicHandler, _ didReadRSSIHandler: @escaping CB4UPeripheralDidReadRSSIHandler, - _ didUpdateNameHandler: @escaping CB4UPeripheralDidUpdateNameHandler + _ didUpdateNameHandler: @escaping CB4UPeripheralDidUpdateNameHandler, + _ didModifyServicesHandler: @escaping CB4UPeripheralDidModifyServicesHandler ) { let instance = Unmanaged.fromOpaque(peripheralPtr).takeUnretainedValue() @@ -143,6 +145,7 @@ public func cb4u_peripheral_register_handlers( instance.didUpdateNotificationStateForCharacteristicHandler = didUpdateNotificationStateForCharacteristicHandler instance.didReadRSSIHandler = didReadRSSIHandler instance.didUpdateNameHandler = didUpdateNameHandler + instance.didModifyServicesHandler = didModifyServicesHandler } @_cdecl("cb4u_peripheral_identifier") @@ -343,6 +346,21 @@ public func cb4u_peripheral_manager_add_service(_ peripheralPtr: UnsafeRawPointe instance.add(service) } +@_cdecl("cb4u_peripheral_manager_remove_service") +public func cb4u_peripheral_manager_remove_service(_ peripheralPtr: UnsafeRawPointer, _ servicePtr: UnsafeRawPointer) { + let instance = Unmanaged.fromOpaque(peripheralPtr).takeUnretainedValue() + let service = Unmanaged.fromOpaque(servicePtr).takeUnretainedValue() + + instance.remove(service) +} + +@_cdecl("cb4u_peripheral_manager_remove_all_services") +public func cb4u_peripheral_manager_remove_all_services(_ peripheralPtr: UnsafeRawPointer) { + let instance = Unmanaged.fromOpaque(peripheralPtr).takeUnretainedValue() + + instance.removeAllServices() +} + @_cdecl("cb4u_peripheral_manager_start_advertising") public func cb4u_peripheral_manager_start_advertising(_ peripheralPtr: UnsafeRawPointer, _ localName: UnsafePointer?, _ serviceUUIDs: UnsafePointer?>, _ serviceUUIDsCount: Int32) { let serviceUUIDsArray = (0.. CBUUID in