[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 */,