blob: b78f498b2fbb82ac7df169c90b7bcc4a7471bbc7 [file] [log] [blame]
/**
*
* Copyright (c) 2022 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 "MTRDeviceControllerOverXPC_Internal.h"
#import "MTRDeviceController+XPC.h"
#import "MTRDeviceControllerXPCConnection.h"
#import "MTRDeviceOverXPC.h"
#import "MTRError.h"
#import "MTRLogging_Internal.h"
#import <Foundation/Foundation.h>
static dispatch_once_t workQueueInitOnceToken;
static dispatch_queue_t globalWorkQueue;
static void SetupXPCQueue(void)
{
dispatch_once(&workQueueInitOnceToken, ^{
globalWorkQueue = dispatch_queue_create("com.apple.matter.framework.xpc.workqueue", DISPATCH_QUEUE_SERIAL);
});
}
@implementation MTRDeviceControllerOverXPC
+ (MTRDeviceControllerOverXPC *)sharedControllerWithID:(id<NSCopying> _Nullable)controllerID
xpcConnectBlock:(MTRXPCConnectBlock)xpcConnectBlock
{
SetupXPCQueue();
return [[MTRDeviceControllerOverXPC alloc] initWithControllerID:controllerID
workQueue:globalWorkQueue
connectBlock:xpcConnectBlock];
}
- (BOOL)setupCommissioningSessionWithPayload:(MTRSetupPayload *)payload
newNodeID:(NSNumber *)newNodeID
error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDeviceController doesn't support setupCommissioningSessionWithPayload over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)pairDevice:(uint64_t)deviceID
discriminator:(uint16_t)discriminator
setupPINCode:(uint32_t)setupPINCode
error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)pairDevice:(uint64_t)deviceID
address:(NSString *)address
port:(uint16_t)port
discriminator:(uint16_t)discriminator
setupPINCode:(uint32_t)setupPINCode
error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)pairDevice:(uint64_t)deviceID onboardingPayload:(NSString *)onboardingPayload error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)commissionDevice:(uint64_t)deviceID
commissioningParams:(MTRCommissioningParameters *)commissioningParams
error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support commissionDevice over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support stopDevicePairing over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (nullable MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceID error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support getDeviceBeingCommissioned over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return nil;
}
- (BOOL)commissionNodeWithID:(NSNumber *)nodeID
commissioningParams:(MTRCommissioningParameters *)commissioningParams
error:(NSError * __autoreleasing *)error;
{
MTR_LOG_ERROR("MTRDeviceController doesn't support commissionNodeWithID over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (BOOL)cancelCommissioningForNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDeviceController doesn't support cancelCommissioningForNodeID over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return NO;
}
- (nullable MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDeviceController doesn't support deviceBeingCommissionedWithNodeID over XPC");
if (error != nil) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil];
}
return nil;
}
- (BOOL)getBaseDevice:(uint64_t)deviceID
queue:(dispatch_queue_t)queue
completionHandler:(MTRDeviceConnectionCallback)completionHandler
{
// Consumers expect their getAnyDeviceControllerWithCompletion to be called
// under here if we don't have a controller id aleady, so make sure we do
// that.
[self fetchControllerIdWithQueue:queue
completion:^(id _Nullable controllerID, MTRDeviceControllerXPCProxyHandle * _Nullable handle,
NSError * _Nullable error) {
if (error != nil) {
completionHandler(nil, error);
return;
}
__auto_type * device = [self baseDeviceForNodeID:@(deviceID)];
completionHandler(device, nil);
}];
return YES;
}
- (void)fetchControllerIdWithQueue:(dispatch_queue_t)queue completion:(MTRFetchControllerIDCompletion)completion
{
// Capture the proxy handle so that it won't be released prior to block call.
__block MTRDeviceControllerXPCProxyHandle * handleRetainer = nil;
dispatch_async(_workQueue, ^{
dispatch_group_t group = dispatch_group_create();
if (!self.controllerID) {
dispatch_group_enter(group);
[self.xpcConnection getProxyHandleWithCompletion:^(
dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) {
if (handle) {
[handle.proxy getAnyDeviceControllerWithCompletion:^(id _Nullable controller, NSError * _Nullable error) {
if (error) {
MTR_LOG_ERROR("Failed to fetch any shared remote controller");
} else {
self.controllerID = controller;
handleRetainer = handle;
}
dispatch_group_leave(group);
}];
} else {
MTR_LOG_ERROR("XPC disconnected while retrieving any shared remote controller");
dispatch_group_leave(group);
}
}];
}
dispatch_group_notify(group, queue, ^{
if (self.controllerID) {
completion(self.controllerID, handleRetainer, nil);
} else {
completion(nil, nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]);
}
});
});
}
- (MTRBaseDevice *)baseDeviceForNodeID:(NSNumber *)nodeID
{
return [[MTRDeviceOverXPC alloc] initWithControllerOverXPC:self deviceID:nodeID xpcConnection:self.xpcConnection];
}
- (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support openPairingWindow over XPC");
return NO;
}
- (nullable NSString *)openPairingWindowWithPIN:(uint64_t)deviceID
duration:(NSUInteger)duration
discriminator:(NSUInteger)discriminator
setupPIN:(NSUInteger)setupPIN
error:(NSError * __autoreleasing *)error
{
MTR_LOG_ERROR("MTRDevice doesn't support openPairingWindow over XPC");
return nil;
}
- (instancetype)initWithControllerID:(id)controllerID
workQueue:(dispatch_queue_t)queue
xpcConnection:(MTRDeviceControllerXPCConnection *)xpcConnection
{
_controllerID = controllerID;
_workQueue = queue;
_xpcConnection = xpcConnection;
return self;
}
// This is interface for unit testing
- (instancetype)initWithControllerID:(id)controllerID
workQueue:(dispatch_queue_t)queue
connectBlock:(MTRXPCConnectBlock)connectBlock
{
return [self initWithControllerID:controllerID
workQueue:queue
xpcConnection:[MTRDeviceControllerXPCConnection connectionWithWorkQueue:queue connectBlock:connectBlock]];
}
@end