From 8bf81c55d9ade567a78c59df1257e730975c09cb Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Thu, 28 Nov 2024 11:20:40 +0100 Subject: [PATCH 1/5] [darwin-framework-tool] Add some HomeKit entitlements --- .../entitlements/darwin-framework-tool.entitlements | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements b/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements index 8b1769796fe97a..5fc25a2d6e03d6 100644 --- a/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements +++ b/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements @@ -8,5 +8,9 @@ group.com.appleinternal.chip-tool + com.apple.developer.homekit + + com.apple.developer.homekit.background-mode + From 95dbb4b41797a8e224a273832ebda5aedb9050e9 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 25 Feb 2025 16:01:26 +0100 Subject: [PATCH 2/5] [darwin-framework-tool] Ensure that darwin-framework-tool calls dispatch_main() --- examples/darwin-framework-tool/main.mm | 44 +++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/examples/darwin-framework-tool/main.mm b/examples/darwin-framework-tool/main.mm index e89c033633e182..cb1ab2bc1a88a6 100644 --- a/examples/darwin-framework-tool/main.mm +++ b/examples/darwin-framework-tool/main.mm @@ -38,24 +38,30 @@ int main(int argc, const char * argv[]) { - int exitCode = EXIT_SUCCESS; - @autoreleasepool { - dft::logging::Setup(); + __auto_type * runQueue = dispatch_queue_create("com.chip.main.dft", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + dispatch_async(runQueue, ^{ + int exitCode = EXIT_SUCCESS; - Commands commands; - registerCommandsBdx(commands); - registerCommandsPairing(commands); - registerCommandsDCL(commands); - registerCommandsDelay(commands); - registerCommandsDiscover(commands); - registerCommandsInteractive(commands); - registerCommandsMemory(commands); - registerCommandsPayload(commands); - registerClusterOtaSoftwareUpdateProviderInteractive(commands); - registerCommandsStorage(commands); - registerCommandsConfiguration(commands); - registerClusters(commands); - exitCode = commands.Run(argc, (char **) argv); - } - return ConditionalLeaksCheck(exitCode); + @autoreleasepool { + dft::logging::Setup(); + Commands commands; + registerCommandsBdx(commands); + registerCommandsPairing(commands); + registerCommandsDCL(commands); + registerCommandsDelay(commands); + registerCommandsDiscover(commands); + registerCommandsInteractive(commands); + registerCommandsMemory(commands); + registerCommandsPayload(commands); + registerClusterOtaSoftwareUpdateProviderInteractive(commands); + registerCommandsStorage(commands); + registerCommandsConfiguration(commands); + registerClusters(commands); + exitCode = commands.Run(argc, (char **) argv); + } + exit(ConditionalLeaksCheck(exitCode)); + }); + + dispatch_main(); + return EXIT_SUCCESS; } From 332c4444ac73d5d66f9abc71f425c859e392c15c Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 25 Feb 2025 19:07:13 +0100 Subject: [PATCH 3/5] [darwin] Update src/darwin/Framework/Matter.xcodeproj/project.pbxproj --- .../Matter.xcodeproj/project.pbxproj | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 702f853fdda5c1..8611528b5266df 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ @@ -385,6 +385,9 @@ B2E0D7B9245B0B5C003C5B48 /* MTRSetupPayload.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2E0D7B0245B0B5C003C5B48 /* MTRSetupPayload.mm */; }; B409D0AE2CCFB89600A7ED5A /* DeviceDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B409D0AC2CCFB89600A7ED5A /* DeviceDelegate.h */; }; B409D0AF2CCFB89600A7ED5A /* DeviceDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B409D0AD2CCFB89600A7ED5A /* DeviceDelegate.mm */; }; + B4382F882D6E231800F79AFC /* HomeKitConnector.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4382F872D6E231800F79AFC /* HomeKitConnector.mm */; platformFilters = (ios, maccatalyst, ); settings = {COMPILER_FLAGS = " -Wno-error=availability -Wno-nullability-completeness"; }; }; + B4382F892D6E231800F79AFC /* HomeKitConnector.h in Headers */ = {isa = PBXBuildFile; fileRef = B4382F862D6E231800F79AFC /* HomeKitConnector.h */; platformFilters = (ios, maccatalyst, ); }; + B4382F8B2D6F554B00F79AFC /* HomeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4382F8A2D6F554B00F79AFC /* HomeKit.framework */; platformFilters = (ios, maccatalyst, ); }; B43B39EA2CB859A5006AA284 /* DumpMemoryGraphCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B43B39E62CB859A5006AA284 /* DumpMemoryGraphCommand.mm */; }; B43B39EB2CB859A5006AA284 /* LeaksTool.mm in Sources */ = {isa = PBXBuildFile; fileRef = B43B39E82CB859A5006AA284 /* LeaksTool.mm */; }; B43B39EC2CB859A5006AA284 /* DumpMemoryGraphCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39E52CB859A5006AA284 /* DumpMemoryGraphCommand.h */; }; @@ -442,11 +445,11 @@ B4D67A3E2D00DAB700C49965 /* XPCServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A372D00DAB700C49965 /* XPCServer.mm */; }; B4D67A412D00DD3D00C49965 /* DFTKeypair.h in Headers */ = {isa = PBXBuildFile; fileRef = B4D67A3F2D00DD3D00C49965 /* DFTKeypair.h */; }; B4D67A422D00DD3D00C49965 /* DFTKeypair.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A402D00DD3D00C49965 /* DFTKeypair.mm */; }; + B4D67A462D07021700C49965 /* XPCServerProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = B4D67A452D07021700C49965 /* XPCServerProtocols.h */; }; B4D67A922D527F4A00C49965 /* DCLClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A8E2D527F4A00C49965 /* DCLClient.cpp */; }; B4D67A932D527F4A00C49965 /* DisplayTermsAndConditions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A8F2D527F4A00C49965 /* DisplayTermsAndConditions.cpp */; }; B4D67A952D527F4A00C49965 /* JsonSchemaMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A912D527F4A00C49965 /* JsonSchemaMacros.cpp */; }; B4D67A9B2D538E9700C49965 /* HTTPSRequest.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4D67A992D538E9700C49965 /* HTTPSRequest.mm */; }; - B4D67A462D07021700C49965 /* XPCServerProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = B4D67A452D07021700C49965 /* XPCServerProtocols.h */; }; B4E262162AA0CF1C00DBA5BC /* RemoteDataModelLogger.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E262122AA0C7A300DBA5BC /* RemoteDataModelLogger.mm */; }; B4E262172AA0CF2000DBA5BC /* RemoteDataModelLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */; }; B4E2621B2AA0D02000DBA5BC /* SleepCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */; }; @@ -951,6 +954,9 @@ B2E0D7B0245B0B5C003C5B48 /* MTRSetupPayload.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRSetupPayload.mm; sourceTree = ""; }; B409D0AC2CCFB89600A7ED5A /* DeviceDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceDelegate.h; sourceTree = ""; }; B409D0AD2CCFB89600A7ED5A /* DeviceDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeviceDelegate.mm; sourceTree = ""; }; + B4382F862D6E231800F79AFC /* HomeKitConnector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeKitConnector.h; sourceTree = ""; }; + B4382F872D6E231800F79AFC /* HomeKitConnector.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HomeKitConnector.mm; sourceTree = ""; }; + B4382F8A2D6F554B00F79AFC /* HomeKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HomeKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.4.sdk/System/iOSSupport/System/Library/Frameworks/HomeKit.framework; sourceTree = DEVELOPER_DIR; }; B43B39E42CB859A5006AA284 /* Commands.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Commands.h; sourceTree = ""; }; B43B39E52CB859A5006AA284 /* DumpMemoryGraphCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DumpMemoryGraphCommand.h; sourceTree = ""; }; B43B39E62CB859A5006AA284 /* DumpMemoryGraphCommand.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DumpMemoryGraphCommand.mm; sourceTree = ""; }; @@ -1001,6 +1007,7 @@ B45373F82A9FEC4F00807602 /* unix-misc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "unix-misc.c"; path = "repo/lib/plat/unix/unix-misc.c"; sourceTree = ""; }; B45373F92A9FEC4F00807602 /* unix-init.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "unix-init.c"; path = "repo/lib/plat/unix/unix-init.c"; sourceTree = ""; }; B45373FA2A9FEC4F00807602 /* unix-sockets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "unix-sockets.c"; path = "repo/lib/plat/unix/unix-sockets.c"; sourceTree = ""; }; + B46C4AA72D6CD2580024F65E /* HomeKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HomeKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/iOSSupport/System/Library/Frameworks/HomeKit.framework; sourceTree = DEVELOPER_DIR; }; B4C8E6B32B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDiagnosticLogsDownloader.h; sourceTree = ""; }; B4C8E6B42B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDiagnosticLogsDownloader.mm; sourceTree = ""; }; B4D67A362D00DAB700C49965 /* DeviceControllerServer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeviceControllerServer.mm; sourceTree = ""; }; @@ -1009,11 +1016,11 @@ B4D67A392D00DAB700C49965 /* XPCServerRegistry.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = XPCServerRegistry.mm; sourceTree = ""; }; B4D67A3F2D00DD3D00C49965 /* DFTKeypair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DFTKeypair.h; sourceTree = ""; }; B4D67A402D00DD3D00C49965 /* DFTKeypair.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DFTKeypair.mm; sourceTree = ""; }; + B4D67A452D07021700C49965 /* XPCServerProtocols.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPCServerProtocols.h; sourceTree = ""; }; B4D67A8E2D527F4A00C49965 /* DCLClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DCLClient.cpp; path = commands/dcl/DCLClient.cpp; sourceTree = ""; }; B4D67A8F2D527F4A00C49965 /* DisplayTermsAndConditions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayTermsAndConditions.cpp; path = commands/dcl/DisplayTermsAndConditions.cpp; sourceTree = ""; }; B4D67A912D527F4A00C49965 /* JsonSchemaMacros.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = JsonSchemaMacros.cpp; path = commands/dcl/JsonSchemaMacros.cpp; sourceTree = ""; }; B4D67A992D538E9700C49965 /* HTTPSRequest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HTTPSRequest.mm; sourceTree = ""; }; - B4D67A452D07021700C49965 /* XPCServerProtocols.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPCServerProtocols.h; sourceTree = ""; }; B4E262122AA0C7A300DBA5BC /* RemoteDataModelLogger.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RemoteDataModelLogger.mm; sourceTree = ""; }; B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteDataModelLogger.h; sourceTree = ""; }; B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SleepCommand.mm; sourceTree = ""; }; @@ -1048,6 +1055,7 @@ 039145F029931B2D00257B3E /* CoreBluetooth.framework in Frameworks */, 039145EE29931B2600257B3E /* Network.framework in Frameworks */, 039145EC29931ABF00257B3E /* Security.framework in Frameworks */, + B4382F8B2D6F554B00F79AFC /* HomeKit.framework in Frameworks */, 039145E52993124800257B3E /* SystemConfiguration.framework in Frameworks */, 039145E3299311FF00257B3E /* IOKit.framework in Frameworks */, 039546962991CEEC006D42A8 /* libCHIP.a in Frameworks */, @@ -1892,17 +1900,11 @@ path = "../common/websocket-server"; sourceTree = ""; }; - B4D67A9A2D538E9700C49965 /* dcl */ = { - isa = PBXGroup; - children = ( - B4D67A992D538E9700C49965 /* HTTPSRequest.mm */, - ); - path = dcl; - sourceTree = ""; - }; B4D67A3A2D00DAB700C49965 /* xpc */ = { isa = PBXGroup; children = ( + B4382F862D6E231800F79AFC /* HomeKitConnector.h */, + B4382F872D6E231800F79AFC /* HomeKitConnector.mm */, B4D67A452D07021700C49965 /* XPCServerProtocols.h */, B4D67A362D00DAB700C49965 /* DeviceControllerServer.mm */, B4D67A372D00DAB700C49965 /* XPCServer.mm */, @@ -1912,6 +1914,14 @@ path = xpc; sourceTree = ""; }; + B4D67A9A2D538E9700C49965 /* dcl */ = { + isa = PBXGroup; + children = ( + B4D67A992D538E9700C49965 /* HTTPSRequest.mm */, + ); + path = dcl; + sourceTree = ""; + }; B4E262182AA0CFFE00DBA5BC /* delay */ = { isa = PBXGroup; children = ( @@ -1943,6 +1953,8 @@ BA09EB3E2474762900605257 /* Frameworks */ = { isa = PBXGroup; children = ( + B46C4AA72D6CD2580024F65E /* HomeKit.framework */, + B4382F8A2D6F554B00F79AFC /* HomeKit.framework */, 039145EF29931B2D00257B3E /* CoreBluetooth.framework */, 039145ED29931B2600257B3E /* Network.framework */, 039145EA29931A4900257B3E /* Security.framework */, @@ -2016,6 +2028,7 @@ 7592BD012CBEE98C00EB74A0 /* EmberMetadata.h in Headers */, 7534D17B2CF8CDDF00F64654 /* AttributePersistenceProvider.h in Headers */, 7592BD022CBEE98C00EB74A0 /* CodegenDataModelProvider.h in Headers */, + B4382F892D6E231800F79AFC /* HomeKitConnector.h in Headers */, 037C3DAD2991BD4F00B7EEE2 /* PairingCommandBridge.h in Headers */, 037C3DBB2991BD5000B7EEE2 /* Commands.h in Headers */, 512431262BA0C8BA000BC136 /* ResetMRPParametersCommand.h in Headers */, @@ -2346,6 +2359,7 @@ 03F430AA2994113500166449 /* sysunix.c in Sources */, 7592BD0E2CC6BCC300EB74A0 /* EmberAttributeDataBuffer.cpp in Sources */, B45373BF2A9FEA9100807602 /* adopt.c in Sources */, + B4382F882D6E231800F79AFC /* HomeKitConnector.mm in Sources */, B4F773CB2CB54B61008C6B23 /* LeakChecker.mm in Sources */, B45373D12A9FEB0C00807602 /* alloc.c in Sources */, B45373DD2A9FEB5300807602 /* base64-decode.c in Sources */, @@ -2636,9 +2650,14 @@ CHIP_BUILD_TOOLS = true; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = "$(CHIP_ROOT)/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/System/iOSSupport/System/Library/Frameworks", + ); GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", CHIP_HAVE_CONFIG_H, @@ -2695,6 +2714,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; + SUPPORTS_MACCATALYST = YES; SYSTEM_HEADER_SEARCH_PATHS = "$(CHIP_ROOT)/src/darwin/Framework/CHIP/"; WARNING_CFLAGS = ( "-Wformat", @@ -2712,9 +2732,14 @@ CHIP_BUILD_TOOLS = true; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_ENTITLEMENTS = "$(CHIP_ROOT)/examples/darwin-framework-tool/entitlements/darwin-framework-tool.entitlements"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/System/iOSSupport/System/Library/Frameworks", + ); GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", CHIP_HAVE_CONFIG_H, @@ -2772,6 +2797,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; + SUPPORTS_MACCATALYST = YES; SYSTEM_HEADER_SEARCH_PATHS = "$(CHIP_ROOT)/src/darwin/Framework/CHIP/"; WARNING_CFLAGS = ( "-Wformat", From c02aac6ebcb97670c04368036578a01d358a7065 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 25 Feb 2025 19:05:22 +0100 Subject: [PATCH 4/5] [darwin-framework-tool] Add XPC connectivity support for HomeKit on iOS --- .../common/xpc/DeviceControllerServer.mm | 22 ++- .../commands/common/xpc/HomeKitConnector.h | 34 +++++ .../commands/common/xpc/HomeKitConnector.mm | 125 ++++++++++++++++++ .../commands/common/xpc/XPCServer.mm | 23 +++- 4 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.h create mode 100644 examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm diff --git a/examples/darwin-framework-tool/commands/common/xpc/DeviceControllerServer.mm b/examples/darwin-framework-tool/commands/common/xpc/DeviceControllerServer.mm index 7b0f9ac99c1e79..89b92c4d39a899 100644 --- a/examples/darwin-framework-tool/commands/common/xpc/DeviceControllerServer.mm +++ b/examples/darwin-framework-tool/commands/common/xpc/DeviceControllerServer.mm @@ -20,6 +20,10 @@ #import +#if TARGET_OS_MACCATALYST || TARGET_OS_IOS +#import "HomeKitConnector.h" +#endif + @interface DeviceControllerXPCServerImpl : NSObject @property (nonatomic, strong) id clientProxy; @property (nonatomic, strong) NSArray * controllers; @@ -331,19 +335,25 @@ - (void)start - (void)stop { +#if TARGET_OS_MACCATALYST || TARGET_OS_IOS + [[HomeKitConnector sharedInstance] stop]; +#endif } - (MTRDeviceController *)createController:(NSString *)controllerID serviceName:(NSString *)serviceName error:(NSError * __autoreleasing *)error { - __auto_type connectBlock = ^NSXPCConnection * - { + NSXPCConnection * (^connectBlock)(void) = nil; #if TARGET_OS_OSX + connectBlock = ^NSXPCConnection * + { return [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0]; -#else - ChipLogError(chipTool, "NSXPCConnection::initWithMachServiceName is not supported on this platform."); - return nil; -#endif // TARGET_OS_OSX }; +#elif TARGET_OS_MACCATALYST || TARGET_OS_IOS + connectBlock = [[HomeKitConnector sharedInstance] connectBlockFor:controllerID]; + controllerID = [[HomeKitConnector sharedInstance] homeControllerIDFor:controllerID]; +#endif + + VerifyOrReturnValue(nil != connectBlock, nil, ChipLogError(chipTool, "DeviceControllerXPCServerWithServiceName is not supported on this platform.")); return [MTRDeviceController sharedControllerWithID:controllerID xpcConnectBlock:connectBlock]; } @end diff --git a/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.h b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.h new file mode 100644 index 00000000000000..996d3e4e515c7e --- /dev/null +++ b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface HomeKitConnector : NSObject +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)sharedInstance; + +- (void)start; +- (void)stop; +- (NSString *)homeControllerIDFor:(NSString *)controllerID; +- (NSXPCConnection * (^)(void) )connectBlockFor:(NSString *)controllerID; +@end + +NS_ASSUME_NONNULL_END diff --git a/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm new file mode 100644 index 00000000000000..12cee2af3e4f31 --- /dev/null +++ b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "HomeKitConnector.h" +#import "../CHIPCommandBridge.h" +#import + +#import + +const int64_t kHomeManagerSetupTimeout = 10LL * NSEC_PER_SEC; +NSString * kControllerIdPrefixStr = @(kControllerIdPrefix); + +@interface HomeKitConnector () +@property (nonatomic, assign) BOOL connectorStarted; +@property (nonatomic, strong) HMHomeManager * homeManager; +@property (nonatomic, assign) BOOL homeManagerReady; +@end + +@implementation HomeKitConnector { + dispatch_group_t _homeManagerReadyGroup; +} + ++ (instancetype)sharedInstance +{ + static HomeKitConnector * sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[HomeKitConnector alloc] init]; + }); + return sharedInstance; +} + +- (void)start +{ + VerifyOrReturn(!_connectorStarted); + _connectorStarted = YES; + + _homeManagerReady = NO; + _homeManagerReadyGroup = dispatch_group_create(); + dispatch_group_enter(_homeManagerReadyGroup); + + _homeManager = [[HMHomeManager alloc] init]; + _homeManager.delegate = self; + + // Wait until homeManagerDidUpdateHomes is called or timeout + dispatch_group_wait(_homeManagerReadyGroup, dispatch_time(DISPATCH_TIME_NOW, kHomeManagerSetupTimeout)); +} + +- (void)stop +{ + VerifyOrReturn(_connectorStarted); + + _homeManager.delegate = nil; + _homeManager = nil; +} + +- (void)homeManagerDidUpdateHomes:(HMHomeManager *)manager +{ + VerifyOrReturn(!_homeManagerReady); + dispatch_group_leave(_homeManagerReadyGroup); +} + +- (HMHome *)homeFor:(NSString *)controllerID +{ + [[HomeKitConnector sharedInstance] start]; + + __auto_type * homes = _homeManager.homes; + VerifyOrReturnValue(0 != homes.count, nil, ChipLogError(chipTool, "HomeKit is not configured with any homes.")); + + NSNumber * fabricId = nil; + if ([controllerID hasPrefix:kControllerIdPrefixStr]) { + __auto_type * fabricIdString = [controllerID substringFromIndex:kControllerIdPrefixStr.length]; + fabricId = @([fabricIdString integerValue]); + } else { + fabricId = CHIPCommandBridge::GetCommissionerFabricId([controllerID UTF8String]); + } + + // When multiple homes exist, the first controller corresponds to the first home, the second controller to the second home, etc. + // If there are fewer homes than controllers, controllers beyond the last home will be associated with the final home in the list. + NSUInteger index = [fabricId unsignedShortValue] - 1; + if (index >= homes.count) { + index = homes.count - 1; + } + + return homes[index]; +} + +- (NSString *)homeControllerIDFor:(NSString *)controllerID +{ + __auto_type * home = [self homeFor:controllerID]; + return home.matterControllerID; +} + +- (NSXPCConnection * (^)(void) )connectBlockFor:(NSString *)controllerID; +{ + __auto_type * home = [self homeFor:controllerID]; + ChipLogProgress(chipTool, "Controller '%s' will be associated with home '%s'.", [controllerID UTF8String], [home.matterControllerID UTF8String]); + + if ([controllerID hasPrefix:kControllerIdPrefixStr]) { + if ([home respondsToSelector:NSSelectorFromString(@"matterStartupParametersXPCConnectBlock")]) { + return [home valueForKey:@"matterStartupParametersXPCConnectBlock"]; + } + + ChipLogError(chipTool, "Error: 'matterStartupParametersXPCConnectBlock' not available for controller '%s'.", [controllerID UTF8String]); + return nil; + } else { + return home.matterControllerXPCConnectBlock; + } +} +@end diff --git a/examples/darwin-framework-tool/commands/common/xpc/XPCServer.mm b/examples/darwin-framework-tool/commands/common/xpc/XPCServer.mm index 9aa7e77ec37177..bf35e9351073d1 100644 --- a/examples/darwin-framework-tool/commands/common/xpc/XPCServer.mm +++ b/examples/darwin-framework-tool/commands/common/xpc/XPCServer.mm @@ -20,6 +20,10 @@ #import +#if TARGET_OS_MACCATALYST || TARGET_OS_IOS +#import "HomeKitConnector.h" +#endif + @interface XPCServerImpl : NSObject @property (nonatomic, strong) id clientProxy; @property (nonatomic, strong) NSArray * controllers; @@ -239,19 +243,26 @@ - (void)start - (void)stop { +#if TARGET_OS_MACCATALYST || TARGET_OS_IOS + [[HomeKitConnector sharedInstance] stop]; +#endif } - (MTRDeviceController *)createController:(NSString *)controllerID serviceName:(NSString *)serviceName error:(NSError * __autoreleasing *)error { - __auto_type connectBlock = ^NSXPCConnection * - { + NSXPCConnection * (^connectBlock)(void) = nil; #if TARGET_OS_OSX + connectBlock = ^NSXPCConnection * + { return [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0]; -#else - ChipLogError(chipTool, "NSXPCConnection::initWithMachServiceName is not supported on this platform."); - return nil; -#endif // TARGET_OS_OSX }; +#elif TARGET_OS_MACCATALYST || TARGET_OS_IOS + connectBlock = [[HomeKitConnector sharedInstance] connectBlockFor:controllerID]; + controllerID = [[HomeKitConnector sharedInstance] homeControllerIDFor:controllerID]; +#endif + + VerifyOrReturnValue(nil != connectBlock, nil, ChipLogError(chipTool, "XPCServerWithServiceName is not supported on this platform.")); + __auto_type * uniqueIdentifier = [[NSUUID alloc] initWithUUIDString:controllerID]; __auto_type * xpcParams = [[MTRXPCDeviceControllerParameters alloc] initWithXPConnectionBlock:connectBlock uniqueIdentifier:uniqueIdentifier]; return [[MTRDeviceController alloc] initWithParameters:xpcParams error:error]; From 9d0470856efc5ae91a8ea9e1df91f8c7a47969f9 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Wed, 26 Feb 2025 16:46:11 +0100 Subject: [PATCH 5/5] [darwin-framework-tool] Add some logging code to HomeKit support on iOS --- .../commands/common/xpc/HomeKitConnector.mm | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm index 12cee2af3e4f31..ea6bf64ab8443e 100644 --- a/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm +++ b/examples/darwin-framework-tool/commands/common/xpc/HomeKitConnector.mm @@ -59,6 +59,8 @@ - (void)start // Wait until homeManagerDidUpdateHomes is called or timeout dispatch_group_wait(_homeManagerReadyGroup, dispatch_time(DISPATCH_TIME_NOW, kHomeManagerSetupTimeout)); + + [self printHomes]; } - (void)stop @@ -100,6 +102,73 @@ - (HMHome *)homeFor:(NSString *)controllerID return homes[index]; } +- (NSString *)paddedString:(NSString *)string width:(NSUInteger)width +{ + // Using length might not account for all unicode width details, but it's a simple approximation. + NSUInteger length = string.length; + if (length >= width) { + return string; + } + NSMutableString * result = [NSMutableString stringWithString:string]; + for (NSUInteger i = 0; i < (width - length); i++) { + [result appendString:@" "]; + } + return result; +} + +- (NSString *)repeatString:(NSString *)string count:(NSUInteger)count +{ + NSMutableString * result = [NSMutableString string]; + for (NSUInteger i = 0; i < count; i++) { + [result appendString:string]; + } + return result; +} + +- (void)printHomes +{ + for (HMHome * home in _homeManager.homes) { + NSUInteger maxNameLength = 0; + NSUInteger maxNodeIDLength = 0; + NSUInteger maxManufacturerLength = 0; + NSUInteger maxModelLength = 0; + + __auto_type * sortedAccessories = [home.accessories sortedArrayUsingComparator:^NSComparisonResult(HMAccessory * a, HMAccessory * b) { + return [a.name localizedCaseInsensitiveCompare:b.name]; + }]; + + for (HMAccessory * accessory in sortedAccessories) { + maxNameLength = MAX(maxNameLength, accessory.name.length); + maxManufacturerLength = MAX(maxManufacturerLength, accessory.manufacturer.length); + maxModelLength = MAX(maxModelLength, accessory.model.length); + maxNodeIDLength = MAX(maxNodeIDLength, [accessory.matterNodeID stringValue].length); + } + + __auto_type * rows = [NSMutableArray arrayWithCapacity:sortedAccessories.count]; + [sortedAccessories enumerateObjectsUsingBlock:^(HMAccessory * accessory, NSUInteger idx, BOOL * stop) { + if (accessory.matterNodeID == nil || [accessory.matterNodeID integerValue] == 0) { + return; + } + + __auto_type * name = [self paddedString:accessory.name width:maxNameLength]; + __auto_type * manufacturer = [self paddedString:accessory.manufacturer width:maxManufacturerLength]; + __auto_type * model = [self paddedString:accessory.model width:maxModelLength]; + __auto_type * nodeID = [self paddedString:[accessory.matterNodeID stringValue] width:maxNodeIDLength]; + __auto_type * formattedString = [NSString stringWithFormat:@" %@ │ %@ │ %@ │ %@ ", name, manufacturer, model, nodeID]; + [rows addObject:formattedString]; + }]; + + NSUInteger tableWidth = 1 + maxNameLength + 3 + maxManufacturerLength + 3 + maxModelLength + 3 + maxNodeIDLength + 1; + NSLog(@"╔%@╗", [self repeatString:@"═" count:tableWidth]); + NSLog(@"║%@║", [self paddedString:[NSString stringWithFormat:@" %@ [%@] ", home.name, home.matterControllerID] width:tableWidth]); + NSLog(@"╠%@╣", [self repeatString:@"═" count:tableWidth]); + for (NSString * row in rows) { + NSLog(@"║%@║", row); + } + NSLog(@"╚%@╝", [self repeatString:@"═" count:tableWidth]); + } +} + - (NSString *)homeControllerIDFor:(NSString *)controllerID { __auto_type * home = [self homeFor:controllerID];