[darwin] Add support for downloading diagnostic logs  (#31638)

* [Matter.framework] Add MTRDiagnosticLogsDelegate to the Matter.framework

* [darwin-framework-tool] Add bdx commands to darwin-framework-tool
diff --git a/examples/darwin-framework-tool/BUILD.gn b/examples/darwin-framework-tool/BUILD.gn
index 7ee396d..1973c64 100644
--- a/examples/darwin-framework-tool/BUILD.gn
+++ b/examples/darwin-framework-tool/BUILD.gn
@@ -163,6 +163,8 @@
     "${chip_root}/examples/chip-tool/commands/common/Commands.h",
     "${chip_root}/examples/chip-tool/commands/common/HexConversion.h",
     "${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp",
+    "commands/bdx/Commands.h",
+    "commands/bdx/DownloadLogCommand.mm",
     "commands/clusters/ClusterCommandBridge.h",
     "commands/clusters/ModelCommandBridge.mm",
     "commands/clusters/ReportCommandBridge.h",
diff --git a/examples/darwin-framework-tool/commands/bdx/Commands.h b/examples/darwin-framework-tool/commands/bdx/Commands.h
new file mode 100644
index 0000000..126ea05
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/bdx/Commands.h
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (c) 2023 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.
+ *
+ */
+
+#pragma once
+
+#include "commands/bdx/DownloadLogCommand.h"
+#include "commands/common/Commands.h"
+
+void registerCommandsBdx(Commands & commands)
+{
+    const char * clusterName      = "Bdx";
+    commands_list clusterCommands = {
+        make_unique<DownloadLogCommand>(), //
+    };
+
+    commands.RegisterCommandSet(clusterName, clusterCommands, "Commands related to BDX");
+}
diff --git a/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.h b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.h
new file mode 100644
index 0000000..ce48270
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.h
@@ -0,0 +1,45 @@
+/*
+ *   Copyright (c) 2024 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.
+ *
+ */
+
+#pragma once
+
+#include "../common/CHIPCommandBridge.h"
+
+class DownloadLogCommand : public CHIPCommandBridge
+{
+public:
+    DownloadLogCommand() : CHIPCommandBridge("download")
+    {
+        AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to download the logs from.");
+        AddArgument("log-type", 0, 2, &mLogType,
+                    "The type of log being requested. This should correspond to a value in the enum MTRDiagnosticLogType.");
+        AddArgument("timeout", 0, UINT16_MAX, &mTimeout,
+                    "The timeout for getting the log. If the timeout expires, completion will be called with whatever has been "
+                    "retrieved by that point (which might be none or a partial log). If the timeout is set to 0, the request will "
+                    "not expire and completion will not be called until the log is fully retrieved or an error occurs.");
+    }
+
+    /////////// CHIPCommandBridge Interface /////////
+    CHIP_ERROR RunCommand() override;
+    chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); }
+
+private:
+    chip::NodeId mNodeId;
+    uint8_t mLogType;
+    uint16_t mTimeout;
+};
diff --git a/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm
new file mode 100644
index 0000000..494ec96
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm
@@ -0,0 +1,58 @@
+/*
+ *   Copyright (c) 2024 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 <Matter/Matter.h>
+
+#import "MTRError_Utils.h"
+
+#include "DownloadLogCommand.h"
+
+CHIP_ERROR DownloadLogCommand::RunCommand()
+{
+    ChipLogProgress(chipTool, "Downloading logs from node 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId));
+
+    MTRDeviceController * commissioner = CurrentCommissioner();
+    auto * device = [MTRDevice deviceWithNodeID:@(mNodeId) controller:commissioner];
+
+    auto logType = static_cast<MTRDiagnosticLogType>(mLogType);
+    auto queue = dispatch_queue_create("com.chip.bdx.downloader", DISPATCH_QUEUE_SERIAL);
+
+    auto * self = this;
+    auto completion = ^(NSURL * url, NSError * error) {
+        // A non-nil url indicates the presence of content, which can occur even in error scenarios like timeouts.
+        if (nil != url) {
+            NSError * readError = nil;
+            auto * data = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&readError];
+            VerifyOrReturn(nil == readError, self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(readError)));
+
+            auto * content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+            NSLog(@"Content: %@", content);
+        }
+
+        VerifyOrReturn(nil == error, self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error)));
+
+        // The url is nil when there are no logs on the target device.
+        if (nil == url) {
+            NSLog(@"No logs has been found onto node 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId));
+        }
+        self->SetCommandExitStatus(CHIP_NO_ERROR);
+    };
+
+    [device downloadLogOfType:logType timeout:mTimeout queue:queue completion:completion];
+    return CHIP_NO_ERROR;
+}
diff --git a/examples/darwin-framework-tool/main.mm b/examples/darwin-framework-tool/main.mm
index 8c96d49..070ae31 100644
--- a/examples/darwin-framework-tool/main.mm
+++ b/examples/darwin-framework-tool/main.mm
@@ -20,6 +20,7 @@
 
 #import "logging/logging.h"
 
+#include "commands/bdx/Commands.h"
 #include "commands/common/Commands.h"
 #include "commands/delay/Commands.h"
 #include "commands/discover/Commands.h"
@@ -38,6 +39,7 @@
         dft::logging::Setup();
 
         Commands commands;
+        registerCommandsBdx(commands);
         registerCommandsPairing(commands);
         registerCommandsDelay(commands);
         registerCommandsDiscover(commands);
diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h
index 4627b96..32a4593 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.h
+++ b/src/darwin/Framework/CHIP/MTRDevice.h
@@ -18,6 +18,7 @@
 #import <Foundation/Foundation.h>
 #import <Matter/MTRBaseDevice.h>
 #import <Matter/MTRDefines.h>
+#import <Matter/MTRDiagnosticLogsType.h>
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -325,6 +326,26 @@
  */
 - (void)removeClientDataForKey:(NSString *)key endpointID:(NSNumber *)endpointID MTR_UNSTABLE_API;
 
+/**
+ * Download log of the desired type from the device.
+ *
+ * Note: The consumer of this API should move the file that the url points to or open it for reading before the
+ * completion handler returns. Otherwise, the file will be deleted, and the data will be lost.
+ *
+ * @param type       The type of log being requested. This should correspond to a value in the enum MTRDiagnosticLogType.
+ * @param timeout    The timeout for getting the log. If the timeout expires, completion will be called with whatever
+ *                   has been retrieved by that point (which might be none or a partial log).
+ *                   If the timeout is set to 0, the request will not expire and completion will not be called until
+ *                   the log is fully retrieved or an error occurs.
+ * @param queue      The queue on which completion will be called.
+ * @param completion The completion that will be called to return the URL of the requested log if successful. Otherwise
+ *                   returns an error.
+ */
+- (void)downloadLogOfType:(MTRDiagnosticLogType)type
+                  timeout:(NSTimeInterval)timeout
+                    queue:(dispatch_queue_t)queue
+               completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+    MTR_NEWLY_AVAILABLE;
 @end
 
 @protocol MTRDeviceDelegate <NSObject>
diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm
index 909fe06..f25ac5b 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice.mm
@@ -1363,6 +1363,18 @@
     [baseDevice openCommissioningWindowWithDiscriminator:discriminator duration:duration queue:queue completion:completion];
 }
 
+- (void)downloadLogOfType:(MTRDiagnosticLogType)type
+                  timeout:(NSTimeInterval)timeout
+                    queue:(dispatch_queue_t)queue
+               completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+{
+    [_deviceController downloadLogFromNodeWithID:_nodeID
+                                            type:type
+                                         timeout:timeout
+                                           queue:queue
+                                      completion:completion];
+}
+
 #pragma mark - Cache management
 
 // assume lock is held
diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm
index 836ea10..decd879 100644
--- a/src/darwin/Framework/CHIP/MTRDeviceController.mm
+++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm
@@ -1216,6 +1216,20 @@
     [device nodeMayBeAdvertisingOperational];
 }
 
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+{
+    [_factory downloadLogFromNodeWithID:nodeID
+                             controller:self
+                                   type:type
+                                timeout:timeout
+                                  queue:queue
+                             completion:completion];
+}
+
 @end
 
 /**
diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
index 717679e..319c33f 100644
--- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
+++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
@@ -31,6 +31,7 @@
 #import "MTRDeviceControllerStartupParams.h"
 #import "MTRDeviceControllerStartupParams_Internal.h"
 #import "MTRDeviceController_Internal.h"
+#import "MTRDiagnosticLogsDownloader.h"
 #import "MTRError_Internal.h"
 #import "MTRFabricInfo_Internal.h"
 #import "MTRFramework.h"
@@ -133,6 +134,8 @@
 @property (nonatomic, readonly, nullable) id<MTROTAProviderDelegate> otaProviderDelegate;
 @property (nonatomic, readonly, nullable) dispatch_queue_t otaProviderDelegateQueue;
 
+@property (nonatomic, readonly) MTRDiagnosticLogsDownloader * diagnosticLogsDownloader;
+
 - (BOOL)findMatchingFabric:(FabricTable &)fabricTable
                     params:(MTRDeviceControllerStartupParams *)params
                     fabric:(const FabricInfo * _Nullable * _Nonnull)fabric;
@@ -332,6 +335,8 @@
         delete _persistentStorageDelegate;
         _persistentStorageDelegate = nullptr;
     }
+
+    _diagnosticLogsDownloader = nil;
 }
 
 - (CHIP_ERROR)_initFabricTable:(FabricTable &)fabricTable
@@ -1066,6 +1071,33 @@
     return [self runningControllerForFabricIndex:fabricIndex includeControllerStartingUp:YES includeControllerShuttingDown:YES];
 }
 
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                       controller:(MTRDeviceController *)controller
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+{
+    dispatch_sync(_chipWorkQueue, ^{
+        if (![self isRunning]) {
+            return;
+        }
+
+        if (_diagnosticLogsDownloader == nil) {
+            _diagnosticLogsDownloader = [[MTRDiagnosticLogsDownloader alloc] init];
+            auto systemState = _controllerFactory->GetSystemState();
+            systemState->BDXTransferServer()->SetDelegate([_diagnosticLogsDownloader getBridge]);
+        }
+
+        [_diagnosticLogsDownloader downloadLogFromNodeWithID:nodeID
+                                                  controller:controller
+                                                        type:type
+                                                     timeout:timeout
+                                                       queue:queue
+                                                  completion:completion];
+    });
+}
+
 - (void)operationalInstanceAdded:(chip::PeerId &)operationalID
 {
     assertChipStackLockedByCurrentThread();
diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h
index 72827d1..e413001 100644
--- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h
+++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h
@@ -22,6 +22,7 @@
 #import <Foundation/Foundation.h>
 #import <Matter/MTRDefines.h>
 #import <Matter/MTRDeviceController.h>
+#import <Matter/MTRDiagnosticLogsType.h>
 
 #if MTR_PER_CONTROLLER_STORAGE_ENABLED
 #import <Matter/MTRDeviceControllerParameters.h>
@@ -76,6 +77,16 @@
 - (void)operationalInstanceAdded:(chip::PeerId &)operationalID;
 
 /**
+ * Download log of the desired type from the device.
+ */
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                       controller:(MTRDeviceController *)controller
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion;
+
+/**
  * Initialize an MTRDeviceController with the given parameters.
  */
 - (nullable MTRDeviceController *)initializeController:(MTRDeviceController *)controller
diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h
index 6bc64e0..92873cf 100644
--- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h
+++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h
@@ -33,6 +33,7 @@
 
 #import <Matter/MTRDefines.h>
 #import <Matter/MTRDeviceControllerStartupParams.h>
+#import <Matter/MTRDiagnosticLogsType.h>
 #if MTR_PER_CONTROLLER_STORAGE_ENABLED
 #import <Matter/MTRDeviceControllerStorageDelegate.h>
 #else
@@ -233,6 +234,15 @@
  */
 - (void)operationalInstanceAdded:(chip::NodeId)nodeID;
 
+/**
+ * Download log of the desired type from the device.
+ */
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion;
+
 #pragma mark - Device-specific data and SDK access
 // DeviceController will act as a central repository for this opaque dictionary that MTRDevice manages
 - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID;
diff --git a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.h b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.h
new file mode 100644
index 0000000..f10fea5
--- /dev/null
+++ b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.h
@@ -0,0 +1,42 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    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 <Foundation/Foundation.h>
+
+#import <Matter/MTRDeviceController.h>
+#import <Matter/MTRDiagnosticLogsType.h>
+
+namespace chip {
+namespace bdx {
+    class BDXTransferServerDelegate;
+}
+}
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MTRDiagnosticLogsDownloader : NSObject
+- (chip::bdx::BDXTransferServerDelegate *)getBridge;
+
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                       controller:(MTRDeviceController *)controller
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm
new file mode 100644
index 0000000..4814b37
--- /dev/null
+++ b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm
@@ -0,0 +1,630 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    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 "MTRDiagnosticLogsDownloader.h"
+
+#include <protocols/bdx/BdxTransferServerDelegate.h>
+
+#import "MTRDeviceControllerFactory_Internal.h"
+#import "MTRDeviceController_Internal.h"
+#import "MTRError_Internal.h"
+#import "MTRLogging_Internal.h"
+#import "NSDataSpanConversion.h"
+#import "NSStringSpanConversion.h"
+
+#import "zap-generated/MTRClusters.h"
+
+typedef void (^AbortHandler)(NSError * error);
+
+static NSString * const kErrorInitDiagnosticLogsDownloader = @"Init failure while initializing Diagnostic Logs bridge.";
+static NSString * const kEndUserSupport = @"EndUserSupport";
+static NSString * const kNetworkDiagnostics = @"NetworkDiagnostics";
+static NSString * const kCrash = @"Crash";
+
+constexpr uint8_t kDiagnosticLogsEndPoint = 0;
+
+class DiagnosticLogsDownloaderBridge;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface Download : NSObject
+@property (nonatomic) NSString * fileDesignator;
+@property (nonatomic) NSNumber * fabricIndex;
+@property (nonatomic) NSNumber * nodeID;
+@property (nonatomic) NSURL * fileURL;
+@property (nonatomic) NSFileHandle * fileHandle;
+@property (nonatomic) AbortHandler abortHandler;
+@property (nonatomic) MTRStatusCompletion finalize;
+
+- (instancetype)initWithType:(MTRDiagnosticLogType)type
+                 fabricIndex:(NSNumber *)fabricIndex
+                      nodeID:(NSNumber *)nodeID
+                       queue:(dispatch_queue_t)queue
+                  completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+                        done:(void (^)(Download * finishedDownload))done;
+
+- (void)writeToFile:(NSData *)data error:(out NSError **)error;
+
+- (BOOL)compare:(NSString *)fileDesignator
+    fabricIndex:(NSNumber *)fabricIndex
+         nodeID:(NSNumber *)nodeID;
+
+- (void)checkInteractionModelResponse:(MTRDiagnosticLogsClusterRetrieveLogsResponseParams * _Nullable)response error:(NSError * _Nullable)error;
+
+- (void)success;
+- (void)failure:(NSError * _Nullable)error;
+@end
+
+@interface Downloads : NSObject
+@property (nonatomic, strong) NSMutableArray<Download *> * downloads;
+
+- (Download * _Nullable)get:(NSString *)fileDesignator
+                fabricIndex:(NSNumber *)fabricIndex
+                     nodeID:(NSNumber *)nodeID;
+
+- (Download * _Nullable)add:(MTRDiagnosticLogType)type
+                fabricIndex:(NSNumber *)fabricIndex
+                     nodeID:(NSNumber *)nodeID
+                      queue:(dispatch_queue_t)queue
+                 completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+                       done:(void (^)(Download * finishedDownload))done;
+@end
+
+@interface MTRDiagnosticLogsDownloader ()
+@property (readonly) DiagnosticLogsDownloaderBridge * bridge;
+@property (nonatomic, strong) Downloads * downloads;
+
+/**
+ * Notify the delegate when a BDX Session starts for some logs.
+ *
+ * If completion is passed a non-nil error, that will be converted into
+ * an error response to the BDX initiatior. Otherwise a success response will be sent.
+ */
+- (void)handleBDXTransferSessionBeginForFileDesignator:(NSString *)fileDesignator
+                                           fabricIndex:(NSNumber *)fabricIndex
+                                                nodeID:(NSNumber *)nodeID
+                                            completion:(MTRStatusCompletion)completion
+                                          abortHandler:(AbortHandler)abortHandler;
+
+/**
+ * Notify the delegate when some data is received on the BDX Session.
+ *
+ * If completion is passed a non-nil error, that will be converted into
+ * an error response to the sender. Otherwise a success response will be sent.
+ */
+- (void)handleBDXTransferSessionDataForFileDesignator:(NSString *)fileDesignator
+                                          fabricIndex:(NSNumber *)fabricIndex
+                                               nodeID:(NSNumber *)nodeID
+                                                 data:(NSData *)data
+                                           completion:(MTRStatusCompletion)completion;
+
+/**
+ * Notify the delegate when a BDX Session ends for some logs.
+ */
+- (void)handleBDXTransferSessionEndForFileDesignator:(NSString *)fileDesignator
+                                         fabricIndex:(NSNumber *)fabricIndex
+                                              nodeID:(NSNumber *)nodeID
+                                               error:(NSError * _Nullable)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+class DiagnosticLogsDownloaderBridge : public chip::bdx::BDXTransferServerDelegate {
+public:
+    DiagnosticLogsDownloaderBridge(MTRDiagnosticLogsDownloader * delegate);
+    ~DiagnosticLogsDownloaderBridge();
+
+    /////////// BDXTransferServerDelegate Interface /////////
+    CHIP_ERROR OnTransferBegin(chip::bdx::BDXTransferProxy * transfer) override;
+    CHIP_ERROR OnTransferEnd(chip::bdx::BDXTransferProxy * transfer, CHIP_ERROR error) override;
+    CHIP_ERROR OnTransferData(chip::bdx::BDXTransferProxy * transfer, const chip::ByteSpan & data) override;
+
+    CHIP_ERROR StartBDXTransferTimeout(Download * download, uint16_t timeoutInSeconds);
+    void CancelBDXTransferTimeout(Download * download);
+
+private:
+    static void OnTransferTimeout(chip::System::Layer * layer, void * context);
+    MTRDiagnosticLogsDownloader * mDelegate;
+    AbortHandler mAbortHandler;
+};
+
+@implementation Download
+- (instancetype)initWithType:(MTRDiagnosticLogType)type
+                 fabricIndex:(NSNumber *)fabricIndex
+                      nodeID:(NSNumber *)nodeID
+                       queue:(dispatch_queue_t)queue
+                  completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+                        done:(void (^)(Download * finishedDownload))done;
+{
+    self = [super init];
+    if (self) {
+        auto * fileDesignator = [self _toFileDesignatorString:type nodeID:nodeID];
+        auto * fileURL = [self _toFileURL:type nodeID:nodeID];
+
+        __weak typeof(self) weakSelf = self;
+        auto bdxTransferDone = ^(NSError * bdxError) {
+            dispatch_async(queue, ^{
+                Download * strongSelf = weakSelf;
+                if (strongSelf) {
+                    // If a fileHandle exists, it means that the BDX session has been initiated and a file has
+                    // been created to host the data of the session. So even if there is an error it may be some
+                    // data in the logs that the caller may find useful. For this reason, fileURL is passed in even
+                    // when there is an error but fileHandle is not nil.
+                    completion(strongSelf->_fileHandle ? fileURL : nil, bdxError);
+                    [strongSelf deleteFile];
+
+                    done(strongSelf);
+                }
+            });
+        };
+
+        _fileDesignator = fileDesignator;
+        _fabricIndex = fabricIndex;
+        _nodeID = nodeID;
+        _fileURL = fileURL;
+        _fileHandle = nil;
+        _finalize = bdxTransferDone;
+    }
+    return self;
+}
+
+- (void)checkInteractionModelResponse:(MTRDiagnosticLogsClusterRetrieveLogsResponseParams * _Nullable)response error:(NSError * _Nullable)error
+{
+    VerifyOrReturn(nil == error, [self failure:error]);
+
+    auto status = response.status;
+
+    VerifyOrReturn(![status isEqual:@(MTRDiagnosticLogsStatusBusy)], [self failure:[MTRError errorForCHIPErrorCode:CHIP_ERROR_BUSY]]);
+    VerifyOrReturn(![status isEqual:@(MTRDiagnosticLogsStatusDenied)], [self failure:[MTRError errorForCHIPErrorCode:CHIP_ERROR_ACCESS_DENIED]]);
+
+    // If there is not logs for the given type, forward it to the caller with a nil url and stop here.
+    VerifyOrReturn(![status isEqual:@(MTRDiagnosticLogsStatusNoLogs)], [self success]);
+
+    // If the whole log content fits into the response LogContent field, forward it to the caller
+    // and stop here.
+    if ([status isEqual:@(MTRDiagnosticLogsStatusExhausted)]) {
+        NSError * writeError = nil;
+        [self writeToFile:response.logContent error:&writeError];
+        VerifyOrReturn(nil == writeError, [self failure:[MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL]]);
+
+        [self success];
+        return;
+    };
+
+    // The file is going to be transferred over BDX. Everything past this point will be handled in bdxTransferDone.
+}
+
+- (void)createFile:(NSError **)error
+{
+    VerifyOrReturn(nil == _fileHandle);
+
+    auto * fileManager = [NSFileManager defaultManager];
+    [fileManager URLForDirectory:NSItemReplacementDirectory
+                        inDomain:NSUserDomainMask
+               appropriateForURL:_fileURL
+                          create:YES
+                           error:error];
+    VerifyOrReturn(nil == *error);
+
+    BOOL success = [fileManager createFileAtPath:[_fileURL path] contents:nil attributes:nil];
+    VerifyOrReturn(success, *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL]);
+
+    auto * fileHandle = [NSFileHandle fileHandleForWritingToURL:_fileURL error:error];
+    VerifyOrReturn(nil == *error);
+
+    _fileHandle = fileHandle;
+}
+
+- (void)deleteFile
+{
+    VerifyOrReturn(nil != _fileHandle);
+
+    NSError * error = nil;
+    [[NSFileManager defaultManager] removeItemAtPath:[_fileURL path] error:&error];
+    if (nil != error) {
+        // There is an error but there is really not much we can do at that point besides logging it.
+        MTR_LOG_ERROR("Error: %@", error);
+    }
+}
+
+- (void)writeToFile:(NSData *)data error:(out NSError **)error
+{
+    [self createFile:error];
+    [_fileHandle seekToEndOfFile];
+    [_fileHandle writeData:data error:error];
+}
+
+- (BOOL)compare:(NSString *)fileDesignator
+    fabricIndex:(NSNumber *)fabricIndex
+         nodeID:(NSNumber *)nodeID
+{
+    return [_fileDesignator isEqualToString:fileDesignator] && _fabricIndex == fabricIndex && _nodeID == nodeID;
+}
+
+- (void)failure:(NSError * _Nullable)error
+{
+    _finalize(error);
+}
+
+- (void)success
+{
+    _finalize(nil);
+}
+
+- (NSURL *)_toFileURL:(MTRDiagnosticLogType)type nodeID:(NSNumber *)nodeID
+{
+    auto * dateFormatter = [[NSDateFormatter alloc] init];
+    dateFormatter.dateFormat = @"yyyy-MM-dd_HH:mm:ss.SSSZZZ";
+    auto * timeString = [dateFormatter stringFromDate:NSDate.now];
+    auto * nodeIDString = [self _toNodeIDString:nodeID];
+    auto * typeString = [self _toTypeString:type];
+    auto * filename = [NSString stringWithFormat:@"%@_%@_%@", timeString, nodeIDString, typeString];
+    return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:filename] isDirectory:YES];
+}
+
+- (NSString *)_toFileDesignatorString:(MTRDiagnosticLogType)type nodeID:(NSNumber *)nodeID
+{
+    auto * nodeIDString = [self _toNodeIDString:nodeID];
+    auto * typeString = [self _toTypeString:type];
+    auto * fileDesignator = [NSString stringWithFormat:@"bdx://%@/%@", nodeIDString, typeString];
+    auto substringIndex = MIN(chip::bdx::DiagnosticLogs::kMaxFileDesignatorLen, [fileDesignator length]);
+    return [fileDesignator substringToIndex:substringIndex];
+}
+
+- (NSString *)_toNodeIDString:(NSNumber *)nodeID
+{
+    return [NSString stringWithFormat:@"%016llX", nodeID.unsignedLongLongValue];
+}
+
+- (NSString *)_toTypeString:(MTRDiagnosticLogType)type
+{
+    switch (type) {
+    case MTRDiagnosticLogTypeEndUserSupport:
+        return kEndUserSupport;
+    case MTRDiagnosticLogTypeNetworkDiagnostics:
+        return kNetworkDiagnostics;
+    case MTRDiagnosticLogTypeCrash:
+        return kCrash;
+    default:
+        // This should never happen.
+        chipDie();
+    }
+}
+
+@end
+
+@implementation Downloads
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _downloads = [[NSMutableArray alloc] init];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    auto error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL];
+    for (Download * download in _downloads) {
+        [download failure:error];
+    }
+    _downloads = nil;
+}
+
+- (Download * _Nullable)get:(NSString *)fileDesignator fabricIndex:(NSNumber *)fabricIndex nodeID:(NSNumber *)nodeID
+{
+    for (Download * download in _downloads) {
+        if ([download compare:fileDesignator fabricIndex:fabricIndex nodeID:nodeID]) {
+            return download;
+        }
+    }
+
+    return nil;
+}
+
+- (Download * _Nullable)add:(MTRDiagnosticLogType)type
+                fabricIndex:(NSNumber *)fabricIndex
+                     nodeID:(NSNumber *)nodeID
+                      queue:(dispatch_queue_t)queue
+                 completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
+                       done:(void (^)(Download * finishedDownload))done
+{
+    auto download = [[Download alloc] initWithType:type fabricIndex:fabricIndex nodeID:nodeID queue:queue completion:completion done:done];
+    VerifyOrReturnValue(nil != download, nil);
+
+    [_downloads addObject:download];
+    return download;
+}
+
+- (void)remove:(Download *)download
+{
+    [_downloads removeObject:download];
+}
+@end
+
+@implementation MTRDiagnosticLogsDownloader
+- (instancetype)init
+{
+    assertChipStackLockedByCurrentThread();
+
+    if (self = [super init]) {
+        _downloads = [[Downloads alloc] init];
+        _bridge = new DiagnosticLogsDownloaderBridge(self);
+        if (_bridge == nullptr) {
+            MTR_LOG_ERROR("Error: %@", kErrorInitDiagnosticLogsDownloader);
+            return nil;
+        }
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    if (_bridge) {
+        delete _bridge;
+        _bridge = nil;
+    }
+    _downloads = nil;
+}
+
+- (chip::bdx::BDXTransferServerDelegate *)getBridge
+{
+    return _bridge;
+}
+
+- (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
+                       controller:(MTRDeviceController *)controller
+                             type:(MTRDiagnosticLogType)type
+                          timeout:(NSTimeInterval)timeout
+                            queue:(dispatch_queue_t)queue
+                       completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion;
+{
+    assertChipStackLockedByCurrentThread();
+
+    uint16_t timeoutInSeconds = 0;
+    if (timeout <= 0) {
+        timeoutInSeconds = 0;
+    } else if (timeout > UINT16_MAX) {
+        MTR_LOG_INFO("Warning: timeout is too large. It will be truncated to UINT16_MAX.");
+        timeoutInSeconds = UINT16_MAX;
+    } else {
+        timeoutInSeconds = static_cast<uint16_t>(timeout);
+    }
+
+    // This block is always called when a download is finished.
+    auto done = ^(Download * finishedDownload) {
+        [controller asyncDispatchToMatterQueue:^() {
+            [self->_downloads remove:finishedDownload];
+
+            if (timeoutInSeconds > 0) {
+                self->_bridge->CancelBDXTransferTimeout(finishedDownload);
+            }
+        } errorHandler:nil];
+    };
+
+    auto fabricIndex = @(controller.fabricIndex);
+    auto download = [_downloads add:type fabricIndex:fabricIndex nodeID:nodeID queue:queue completion:completion done:done];
+    VerifyOrReturn(nil != download,
+        dispatch_async(queue, ^{ completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INTERNAL]); }));
+
+    auto interactionModelDone = ^(MTRDiagnosticLogsClusterRetrieveLogsResponseParams * _Nullable response, NSError * _Nullable error) {
+        [download checkInteractionModelResponse:response error:error];
+    };
+
+    auto * device = [controller deviceForNodeID:nodeID];
+    auto * cluster = [[MTRClusterDiagnosticLogs alloc] initWithDevice:device endpointID:@(kDiagnosticLogsEndPoint) queue:queue];
+
+    auto * params = [[MTRDiagnosticLogsClusterRetrieveLogsRequestParams alloc] init];
+    params.intent = @(type);
+    params.requestedProtocol = @(MTRDiagnosticLogsTransferProtocolBDX);
+    params.transferFileDesignator = download.fileDesignator;
+
+    [cluster retrieveLogsRequestWithParams:params expectedValues:nil expectedValueInterval:nil completion:interactionModelDone];
+
+    if (timeoutInSeconds > 0) {
+        auto err = _bridge->StartBDXTransferTimeout(download, timeoutInSeconds);
+        VerifyOrReturn(CHIP_NO_ERROR == err, [download failure:[MTRError errorForCHIPErrorCode:err]]);
+    }
+}
+
+- (void)handleBDXTransferSessionBeginForFileDesignator:(NSString *)fileDesignator
+                                           fabricIndex:(NSNumber *)fabricIndex
+                                                nodeID:(NSNumber *)nodeID
+                                            completion:(MTRStatusCompletion)completion
+                                          abortHandler:(AbortHandler)abortHandler;
+{
+    assertChipStackLockedByCurrentThread();
+    MTR_LOG_DEFAULT("BDX Transfer Session Begin: %@", fileDesignator);
+
+    auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID];
+    VerifyOrReturn(nil != download, completion([MTRError errorForCHIPErrorCode:CHIP_ERROR_NOT_FOUND]));
+
+    download.abortHandler = abortHandler;
+    completion(nil);
+}
+
+- (void)handleBDXTransferSessionDataForFileDesignator:(NSString *)fileDesignator
+                                          fabricIndex:(NSNumber *)fabricIndex
+                                               nodeID:(NSNumber *)nodeID
+                                                 data:(NSData *)data
+                                           completion:(MTRStatusCompletion)completion
+{
+    assertChipStackLockedByCurrentThread();
+    MTR_LOG_DEFAULT("BDX Transfer Session Data: %@: %@", fileDesignator, data);
+
+    auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID];
+    VerifyOrReturn(nil != download, completion([MTRError errorForCHIPErrorCode:CHIP_ERROR_NOT_FOUND]));
+
+    NSError * error = nil;
+    [download writeToFile:data error:&error];
+    VerifyOrReturn(nil != error, completion(error));
+
+    completion(nil);
+}
+
+- (void)handleBDXTransferSessionEndForFileDesignator:(NSString *)fileDesignator
+                                         fabricIndex:(NSNumber *)fabricIndex
+                                              nodeID:(NSNumber *)nodeID
+                                               error:(NSError * _Nullable)error
+{
+    assertChipStackLockedByCurrentThread();
+    MTR_LOG_DEFAULT("BDX Transfer Session End: %@: %@", fileDesignator, error);
+
+    auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID];
+    VerifyOrReturn(nil != download);
+
+    VerifyOrReturn(nil == error, [download failure:error]);
+    [download success];
+}
+@end
+
+DiagnosticLogsDownloaderBridge::DiagnosticLogsDownloaderBridge(MTRDiagnosticLogsDownloader * delegate)
+{
+    mDelegate = delegate;
+}
+
+DiagnosticLogsDownloaderBridge::~DiagnosticLogsDownloaderBridge()
+{
+    mDelegate = nil;
+}
+
+CHIP_ERROR DiagnosticLogsDownloaderBridge::OnTransferBegin(chip::bdx::BDXTransferProxy * transfer)
+{
+    VerifyOrReturnError(nil != mDelegate, CHIP_ERROR_INCORRECT_STATE);
+
+    auto fileDesignatorSpan = transfer->GetFileDesignator();
+    auto fileDesignator = AsString(fileDesignatorSpan);
+    VerifyOrReturnError(nil != fileDesignator, CHIP_ERROR_INCORRECT_STATE);
+
+    auto * fabricIndex = @(transfer->GetFabricIndex());
+    auto * nodeId = @(transfer->GetPeerNodeId());
+
+    auto completionHandler = ^(NSError * _Nullable error) {
+        assertChipStackLockedByCurrentThread();
+
+        if (error != nil) {
+            auto err = [MTRError errorToCHIPErrorCode:error];
+            transfer->Reject(err);
+        } else {
+            transfer->Accept();
+        }
+    };
+
+    // Ideally we would like to handle aborts a bit differently since this only works
+    // because our BDX stack supports one transfer at a time.
+    mAbortHandler = ^(NSError * error) {
+        assertChipStackLockedByCurrentThread();
+        auto err = [MTRError errorToCHIPErrorCode:error];
+        transfer->Reject(err);
+    };
+
+    [mDelegate handleBDXTransferSessionBeginForFileDesignator:fileDesignator
+                                                  fabricIndex:fabricIndex
+                                                       nodeID:nodeId
+                                                   completion:completionHandler
+                                                 abortHandler:mAbortHandler];
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR DiagnosticLogsDownloaderBridge::OnTransferEnd(chip::bdx::BDXTransferProxy * transfer, CHIP_ERROR error)
+{
+    VerifyOrReturnError(nil != mDelegate, CHIP_ERROR_INCORRECT_STATE);
+
+    auto fileDesignatorSpan = transfer->GetFileDesignator();
+    auto fileDesignator = AsString(fileDesignatorSpan);
+    VerifyOrReturnError(nil != fileDesignator, CHIP_ERROR_INCORRECT_STATE);
+
+    NSError * mtrError = nil;
+    if (CHIP_NO_ERROR != error) {
+        mtrError = [MTRError errorForCHIPErrorCode:error];
+    }
+
+    auto * fabricIndex = @(transfer->GetFabricIndex());
+    auto * nodeId = @(transfer->GetPeerNodeId());
+    [mDelegate handleBDXTransferSessionEndForFileDesignator:fileDesignator
+                                                fabricIndex:fabricIndex
+                                                     nodeID:nodeId
+                                                      error:mtrError];
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR DiagnosticLogsDownloaderBridge::OnTransferData(chip::bdx::BDXTransferProxy * transfer, const chip::ByteSpan & dataSpan)
+{
+    VerifyOrReturnError(nil != mDelegate, CHIP_ERROR_INCORRECT_STATE);
+
+    auto fileDesignatorSpan = transfer->GetFileDesignator();
+    auto fileDesignator = AsString(fileDesignatorSpan);
+    VerifyOrReturnError(nil != fileDesignator, CHIP_ERROR_INCORRECT_STATE);
+
+    auto * fabricIndex = @(transfer->GetFabricIndex());
+    auto * nodeId = @(transfer->GetPeerNodeId());
+
+    auto data = AsData(dataSpan);
+    VerifyOrReturnError(nil != data, CHIP_ERROR_INCORRECT_STATE);
+
+    auto completionHandler = ^(NSError * _Nullable error) {
+        assertChipStackLockedByCurrentThread();
+
+        if (error != nil) {
+            auto err = [MTRError errorToCHIPErrorCode:error];
+            transfer->Reject(err);
+        } else {
+            transfer->Continue();
+        }
+    };
+
+    mAbortHandler = nil;
+
+    [mDelegate handleBDXTransferSessionDataForFileDesignator:fileDesignator
+                                                 fabricIndex:fabricIndex
+                                                      nodeID:nodeId
+                                                        data:data
+                                                  completion:completionHandler];
+    return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR DiagnosticLogsDownloaderBridge::StartBDXTransferTimeout(Download * download, uint16_t timeoutInSeconds)
+{
+    assertChipStackLockedByCurrentThread();
+    return chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(timeoutInSeconds), OnTransferTimeout, (__bridge void *) download);
+}
+
+void DiagnosticLogsDownloaderBridge::CancelBDXTransferTimeout(Download * download)
+{
+    assertChipStackLockedByCurrentThread();
+    chip::DeviceLayer::SystemLayer().CancelTimer(OnTransferTimeout, (__bridge void *) download);
+}
+
+void DiagnosticLogsDownloaderBridge::OnTransferTimeout(chip::System::Layer * layer, void * context)
+{
+    assertChipStackLockedByCurrentThread();
+
+    auto * download = (__bridge Download *) context;
+    VerifyOrReturn(nil != download);
+
+    // If there is no abortHandler, it means that the BDX transfer has not started.
+    // When a BDX transfer has started we need to abort the transfer and we would error out
+    // at next poll. We would end up calling OnTransferEnd and eventually [download failure:error].
+    // But if the transfer has not started we would stop right now.
+    auto error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_TIMEOUT];
+    if (download.abortHandler == nil) {
+        [download failure:error];
+    } else {
+        download.abortHandler(error);
+    }
+}
diff --git a/src/darwin/Framework/CHIP/MTRDiagnosticLogsType.h b/src/darwin/Framework/CHIP/MTRDiagnosticLogsType.h
new file mode 100644
index 0000000..7d86759
--- /dev/null
+++ b/src/darwin/Framework/CHIP/MTRDiagnosticLogsType.h
@@ -0,0 +1,27 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    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.
+ */
+
+/**
+ * This enum is used to specify the type of log requested from this device.
+ *
+ * The log types are : End User Support, Network Diagnostics and Crash logs.
+ */
+typedef NS_ENUM(NSInteger, MTRDiagnosticLogType) {
+    MTRDiagnosticLogTypeEndUserSupport = 0, // End user support log is requested
+    MTRDiagnosticLogTypeNetworkDiagnostics = 1, // Network Diagnostics log is requested
+    MTRDiagnosticLogTypeCrash = 2 // Crash log is requested
+} MTR_NEWLY_AVAILABLE;
diff --git a/src/darwin/Framework/CHIP/Matter.h b/src/darwin/Framework/CHIP/Matter.h
index 2cf6242..85f1399 100644
--- a/src/darwin/Framework/CHIP/Matter.h
+++ b/src/darwin/Framework/CHIP/Matter.h
@@ -45,6 +45,7 @@
 #import <Matter/MTRDeviceControllerParameters.h>
 #import <Matter/MTRDeviceControllerStartupParams.h>
 #import <Matter/MTRDeviceControllerStorageDelegate.h>
+#import <Matter/MTRDiagnosticLogsType.h>
 #import <Matter/MTRError.h>
 #import <Matter/MTRFabricInfo.h>
 #import <Matter/MTRKeypair.h>
diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
index 72565dd..ea1b9b8 100644
--- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
@@ -299,10 +299,16 @@
 		B45373FF2A9FEC4F00807602 /* unix-misc.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373F82A9FEC4F00807602 /* unix-misc.c */; };
 		B45374002A9FEC4F00807602 /* unix-init.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373F92A9FEC4F00807602 /* unix-init.c */; };
 		B45374012A9FEC4F00807602 /* unix-sockets.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373FA2A9FEC4F00807602 /* unix-sockets.c */; };
+		B4C8E6B72B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4C8E6B42B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm */; };
 		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 */; };
 		B4E2621E2AA0D02D00DBA5BC /* WaitForCommissioneeCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */; };
+		B4FCD56A2B5EDBD300832859 /* MTRDiagnosticLogsType.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		B4FCD5732B611EB300832859 /* MTRDiagnosticLogsDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = B4C8E6B32B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.h */; };
+		B4FCD5702B603A6300832859 /* Commands.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56D2B603A6300832859 /* Commands.h */; };
+		B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */; };
+		B4FCD5722B603A6300832859 /* DownloadLogCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4FCD56F2B603A6300832859 /* DownloadLogCommand.mm */; };
 		BA09EB43247477BA00605257 /* libCHIP.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA09EB3F2474762900605257 /* libCHIP.a */; };
 		D4772A46285AE98400383630 /* MTRClusterConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = D4772A45285AE98300383630 /* MTRClusterConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
 /* End PBXBuildFile section */
@@ -669,10 +675,16 @@
 		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 = "<group>"; };
 		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 = "<group>"; };
 		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 = "<group>"; };
+		B4C8E6B32B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDiagnosticLogsDownloader.h; sourceTree = "<group>"; };
+		B4C8E6B42B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDiagnosticLogsDownloader.mm; sourceTree = "<group>"; };
 		B4E262122AA0C7A300DBA5BC /* RemoteDataModelLogger.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RemoteDataModelLogger.mm; sourceTree = "<group>"; };
 		B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteDataModelLogger.h; sourceTree = "<group>"; };
 		B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SleepCommand.mm; sourceTree = "<group>"; };
 		B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WaitForCommissioneeCommand.mm; sourceTree = "<group>"; };
+		B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDiagnosticLogsType.h; sourceTree = "<group>"; };
+		B4FCD56D2B603A6300832859 /* Commands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Commands.h; sourceTree = "<group>"; };
+		B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadLogCommand.h; sourceTree = "<group>"; };
+		B4FCD56F2B603A6300832859 /* DownloadLogCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DownloadLogCommand.mm; sourceTree = "<group>"; };
 		BA09EB3F2474762900605257 /* libCHIP.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCHIP.a; path = lib/libCHIP.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		BA107AEE2470CFBB004287EB /* chip_xcode_build_connector.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = chip_xcode_build_connector.sh; sourceTree = "<group>"; };
 		D437613E285BDC0D0051FEA2 /* MTRErrorTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRErrorTestUtils.h; sourceTree = "<group>"; };
@@ -741,6 +753,7 @@
 		037C3D7B2991BD4F00B7EEE2 /* commands */ = {
 			isa = PBXGroup;
 			children = (
+				B4FCD56C2B603A6300832859 /* bdx */,
 				B4E262182AA0CFFE00DBA5BC /* delay */,
 				03FB93DA2A46200A0048CB35 /* discover */,
 				037C3D7C2991BD4F00B7EEE2 /* pairing */,
@@ -1105,6 +1118,9 @@
 		B202528F2459E34F00F97062 /* CHIP */ = {
 			isa = PBXGroup;
 			children = (
+				B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */,
+				B4C8E6B32B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.h */,
+				B4C8E6B42B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm */,
 				1E4D655129C30A8700BC3478 /* MTRCommissionableBrowser.mm */,
 				1E4D654C29C208DD00BC3478 /* MTRCommissionableBrowser.h */,
 				1E4D654D29C208DD00BC3478 /* MTRCommissionableBrowserDelegate.h */,
@@ -1339,6 +1355,16 @@
 			path = delay;
 			sourceTree = "<group>";
 		};
+		B4FCD56C2B603A6300832859 /* bdx */ = {
+			isa = PBXGroup;
+			children = (
+				B4FCD56D2B603A6300832859 /* Commands.h */,
+				B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */,
+				B4FCD56F2B603A6300832859 /* DownloadLogCommand.mm */,
+			);
+			path = bdx;
+			sourceTree = "<group>";
+		};
 		BA09EB3E2474762900605257 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -1368,6 +1394,7 @@
 				037C3DCE2991BD5100B7EEE2 /* CHIPCommandBridge.h in Headers */,
 				037C3DD22991BD5200B7EEE2 /* InteractiveCommands.h in Headers */,
 				037C3DAF2991BD4F00B7EEE2 /* DeviceControllerDelegateBridge.h in Headers */,
+				B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */,
 				037C3DC32991BD5100B7EEE2 /* Commands.h in Headers */,
 				037C3DB82991BD5000B7EEE2 /* ClusterCommandBridge.h in Headers */,
 				037C3DC82991BD5100B7EEE2 /* CHIPToolKeypair.h in Headers */,
@@ -1380,6 +1407,7 @@
 				037C3DB92991BD5000B7EEE2 /* ReportCommandBridge.h in Headers */,
 				037C3DBE2991BD5000B7EEE2 /* OTASoftwareUpdateInteractive.h in Headers */,
 				037C3DBD2991BD5000B7EEE2 /* OTAProviderDelegate.h in Headers */,
+				B4FCD5702B603A6300832859 /* Commands.h in Headers */,
 				037C3DB02991BD4F00B7EEE2 /* Commands.h in Headers */,
 				037C3DC02991BD5100B7EEE2 /* Commands.h in Headers */,
 				B4E262172AA0CF2000DBA5BC /* RemoteDataModelLogger.h in Headers */,
@@ -1411,6 +1439,7 @@
 				5178E6812AE098520069DF72 /* MTRCommandTimedCheck.h in Headers */,
 				7596A84B287636C1004DAE0E /* MTRDevice_Internal.h in Headers */,
 				5A6FEC9927B5C88900F25F42 /* MTRDeviceOverXPC.h in Headers */,
+				B4FCD5732B611EB300832859 /* MTRDiagnosticLogsDownloader.h in Headers */,
 				51B22C222740CB1D008D5055 /* MTRCommandPayloadsObjc.h in Headers */,
 				51B22C1E2740CB0A008D5055 /* MTRStructsObjc.h in Headers */,
 				2CB7163B252E8A7B0026E2BB /* MTRDeviceControllerDelegateBridge.h in Headers */,
@@ -1460,6 +1489,7 @@
 				998F286D26D55E10001846C6 /* MTRKeypair.h in Headers */,
 				1ED276E426C5832500547A89 /* MTRCluster.h in Headers */,
 				3D843711294977000070D20A /* NSStringSpanConversion.h in Headers */,
+				B4FCD56A2B5EDBD300832859 /* MTRDiagnosticLogsType.h in Headers */,
 				5A6FEC9A27B5C89300F25F42 /* MTRDeviceControllerXPCConnection.h in Headers */,
 				5129BCFD26A9EE3300122DDF /* MTRError.h in Headers */,
 				2C8C8FC1253E0C2100797F05 /* MTRStorage.h in Headers */,
@@ -1643,6 +1673,7 @@
 				B45373C12A9FEA9100807602 /* close.c in Sources */,
 				039546A62991E151006D42A8 /* InteractionModel.cpp in Sources */,
 				B45373E72A9FEBA400807602 /* parsers.c in Sources */,
+				B4FCD5722B603A6300832859 /* DownloadLogCommand.mm in Sources */,
 				B45373BF2A9FEA9100807602 /* adopt.c in Sources */,
 				B45373F32A9FEC1A00807602 /* server-ws.c in Sources */,
 				03F430AA2994113500166449 /* sysunix.c in Sources */,
@@ -1736,6 +1767,7 @@
 				AF5F90FF2878D351005503FA /* MTROTAProviderDelegateBridge.mm in Sources */,
 				51E95DFC2A78443C00A434F0 /* MTRSessionResumptionStorageBridge.mm in Sources */,
 				7534F12828BFF20300390851 /* MTRDeviceAttestationDelegate.mm in Sources */,
+				B4C8E6B72B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm in Sources */,
 				2C5EEEF7268A85C400CAE3D3 /* MTRDeviceConnectionBridge.mm in Sources */,
 				51B22C262740CB32008D5055 /* MTRStructsObjc.mm in Sources */,
 				2C222AD1255C620600E446B9 /* MTRBaseDevice.mm in Sources */,