Network Provisioning: Objective-C Support for wireless regulatory provisioning
diff --git a/src/device-manager/cocoa/Makefile.am b/src/device-manager/cocoa/Makefile.am
index 64ebf32..0f2c976 100644
--- a/src/device-manager/cocoa/Makefile.am
+++ b/src/device-manager/cocoa/Makefile.am
@@ -68,6 +68,7 @@
     NLResourceIdentifier.h                 \
     NLWdmClientFlushUpdateError.h          \
     NLWdmClientFlushUpdateDeviceStatusError.h       \
+    NLWirelessRegConfig.h                  \
     $(NULL)
 
 noinst_HEADERS                           = \
@@ -79,6 +80,7 @@
     NLWdmClient_Protected.h                \
     NLResourceIdentifier_Protected.h       \
     NLGenericTraitUpdatableDataSink_Protected.h     \
+    NLWirelessRegConfig_Protected.h                  \
     $(NULL)
 
 libNLWeaveDeviceManager_a_SOURCES        = \
@@ -106,6 +108,7 @@
     NLResourceIdentifier.mm                \
     NLWdmClientFlushUpdateError.mm         \
     NLWdmClientFlushUpdateDeviceStatusError.mm                \
+    NLWirelessRegConfig.mm                  \
     $(NULL)
 
 endif # WEAVE_WITH_COCOA
diff --git a/src/device-manager/cocoa/NLWeaveDeviceManager.h b/src/device-manager/cocoa/NLWeaveDeviceManager.h
index 2a2285b..fc9d54e 100644
--- a/src/device-manager/cocoa/NLWeaveDeviceManager.h
+++ b/src/device-manager/cocoa/NLWeaveDeviceManager.h
@@ -1,6 +1,7 @@
 /*
  *
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
+ *    Copyright (c) 2018-2020 Google LLC.
  *    All rights reserved.
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +28,7 @@
 #import "NLWeaveErrorCodes.h"
 #import "NLIdentifyDeviceCriteria.h"
 #import "NLNetworkInfo.h"
+#import "NLWirelessRegConfig.h"
 #import "NLServiceInfo.h"
 
 typedef void (^WDMCompletionBlock)(id owner, id data);
@@ -187,6 +189,12 @@
     @{
  */
 
+- (void)getWirelessRegulatoryConfig:(WDMCompletionBlock)completionBlock failure:(WDMFailureBlock)failureBlock;
+
+- (void)setWirelessRegulatoryConfig:(NLWirelessRegConfig *)nlWirelessRegConfig
+                         completion:(WDMCompletionBlock)completionBlock
+                            failure:(WDMFailureBlock)failureBlock;
+
 - (void)scanNetworks:(NLNetworkType)networkType
           completion:(WDMCompletionBlock)completionBlock
              failure:(WDMFailureBlock)failureBlock;
diff --git a/src/device-manager/cocoa/NLWeaveDeviceManager.mm b/src/device-manager/cocoa/NLWeaveDeviceManager.mm
index 05bb3e5..bec3ece 100644
--- a/src/device-manager/cocoa/NLWeaveDeviceManager.mm
+++ b/src/device-manager/cocoa/NLWeaveDeviceManager.mm
@@ -1,6 +1,7 @@
 /*
  *
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
+ *    Copyright (c) 2018-2020 Google LLC.
  *    All rights reserved.
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,6 +44,7 @@
 #include <Weave/Profiles/device-description/DeviceDescription.h>
 #import "NLIdentifyDeviceCriteria_Protected.h"
 #import "Base64Encoding.h"
+#import "NLWirelessRegConfig_Protected.h"
 
 static void onIdentifyDeviceComplete(nl::Weave::DeviceManager::WeaveDeviceManager * deviceMgr, void * appReqState,
     const nl::Weave::DeviceManager::DeviceDescription::WeaveDeviceDescriptor * devdesc);
@@ -384,11 +386,11 @@
     return err;
 }
 
-- (WEAVE_ERROR)GetDeviceMgrPtr:(long long*)deviceMgrPtr
+- (WEAVE_ERROR)GetDeviceMgrPtr:(long long *)deviceMgrPtr
 {
     __block WEAVE_ERROR err = WEAVE_NO_ERROR;
     WDM_LOG_METHOD_SIG();
-    *deviceMgrPtr = (long long)_mWeaveCppDM;
+    *deviceMgrPtr = (long long) _mWeaveCppDM;
 
     return err;
 }
@@ -472,14 +474,14 @@
                     errorCode:devStatus->SystemErrorCode
                  statusReport:[dm statusReportToString:devStatus->StatusProfileId statusCode:devStatus->StatusCode]];
         requestError = NLWeaveRequestError_ProfileStatusError;
-        userInfo = @{ @"WeaveRequestErrorType" : @(requestError), @"errorInfo" : statusError };
+        userInfo = @{@"WeaveRequestErrorType" : @(requestError), @"errorInfo" : statusError};
 
         WDM_LOG_DEBUG(@"%@: status error: %@", dm.name, userInfo);
     } else {
         NLWeaveError * weaveError = [[NLWeaveError alloc] initWithWeaveError:code
                                                                       report:[NSString stringWithUTF8String:nl::ErrorStr(code)]];
         requestError = NLWeaveRequestError_WeaveError;
-        userInfo = @{ @"WeaveRequestErrorType" : @(requestError), @"errorInfo" : weaveError };
+        userInfo = @{@"WeaveRequestErrorType" : @(requestError), @"errorInfo" : weaveError};
     }
 
     error = [NSError errorWithDomain:@"com.nest.error" code:code userInfo:userInfo];
@@ -1082,6 +1084,78 @@
     });
 }
 
+static void onGetWirelessRegulatoryConfigComplete(nl::Weave::DeviceManager::WeaveDeviceManager * deviceMgr, void * reqState,
+    const nl::Weave::Profiles::NetworkProvisioning::WirelessRegConfig * regConfig)
+{
+    WDM_LOG_DEBUG(@"onGetWirelessRegulatoryConfigComplete");
+
+    NLWeaveDeviceManager * dm = (__bridge NLWeaveDeviceManager *) reqState;
+    // ignore the pointer to C++ device manager
+    (void) deviceMgr;
+
+    [dm DispatchAsyncCompletionBlock:[NLWirelessRegConfig createUsing:regConfig]];
+}
+
+- (void)getWirelessRegulatoryConfig:(WDMCompletionBlock)completionBlock failure:(WDMFailureBlock)failureBlock
+{
+    WDM_LOG_METHOD_SIG();
+
+    NSString * taskName = @"GetWirelessRegulatoryConfig";
+
+    // we use async for the results are sent back to caller via async means also
+    dispatch_async(_mWeaveWorkQueue, ^() {
+        if (nil == _mRequestName) {
+            _mRequestName = taskName;
+            _mCompletionHandler = completionBlock;
+            _mFailureHandler = failureBlock;
+
+            WEAVE_ERROR err = _mWeaveCppDM->GetWirelessRegulatoryConfig(
+                (__bridge void *) self, onGetWirelessRegulatoryConfigComplete, onWeaveError);
+
+            if (WEAVE_NO_ERROR != err) {
+                [self DispatchAsyncDefaultFailureBlockWithCode:err];
+            }
+        } else {
+            WDM_LOG_ERROR(@"%@: Attemp to %@ while we're still executing %@, ignore", _name, taskName, _mRequestName);
+
+            // do not change _mRequestName, as we're rejecting this request
+            [self DispatchAsyncFailureBlock:WEAVE_ERROR_INCORRECT_STATE taskName:taskName handler:failureBlock];
+        }
+    });
+}
+
+- (void)setWirelessRegulatoryConfig:(NLWirelessRegConfig *)nlWirelessRegConfig
+                         completion:(WDMCompletionBlock)completionBlock
+                            failure:(WDMFailureBlock)failureBlock
+{
+    WDM_LOG_METHOD_SIG();
+
+    NSString * taskName = @"SetWirelessRegulatoryConfig";
+
+    // we use async for the results are sent back to caller via async means also
+    dispatch_async(_mWeaveWorkQueue, ^() {
+        if (nil == _mRequestName) {
+            _mRequestName = taskName;
+            _mCompletionHandler = completionBlock;
+            _mFailureHandler = failureBlock;
+
+            nl::Weave::Profiles::NetworkProvisioning::WirelessRegConfig wirelessRegConfig =
+                [nlWirelessRegConfig toWirelessRegConfig];
+
+            WEAVE_ERROR err = _mWeaveCppDM->SetWirelessRegulatoryConfig(
+                &wirelessRegConfig, (__bridge void *) self, HandleSimpleOperationComplete, onWeaveError);
+            if (WEAVE_NO_ERROR != err) {
+                [self DispatchAsyncDefaultFailureBlockWithCode:err];
+            }
+        } else {
+            WDM_LOG_ERROR(@"%@: Attemp to %@ while we're still executing %@, ignore", _name, taskName, _mRequestName);
+
+            // do not change _mRequestName, as we're rejecting this request
+            [self DispatchAsyncFailureBlock:WEAVE_ERROR_INCORRECT_STATE taskName:taskName handler:failureBlock];
+        }
+    });
+}
+
 static void onNetworkScanComplete(
     nl::Weave::DeviceManager::WeaveDeviceManager * deviceMgr, void * reqState, uint16_t netCount, const NetworkInfo * netInfoList)
 {
diff --git a/src/device-manager/cocoa/NLWeaveDeviceManagerTypes.h b/src/device-manager/cocoa/NLWeaveDeviceManagerTypes.h
index 66b3aeb..fafd39c 100644
--- a/src/device-manager/cocoa/NLWeaveDeviceManagerTypes.h
+++ b/src/device-manager/cocoa/NLWeaveDeviceManagerTypes.h
@@ -40,6 +40,15 @@
 
 };
 
+// WiFi WirelessOperatingLocation
+//
+typedef NS_ENUM(NSInteger, NLWirelessOperatingLocation) {
+    kNLWirelessOperatingLocation_NotSpecified = 0x00,
+    kNLWirelessOperatingLocation_Unknown = 0x01,
+    kNLWirelessOperatingLocation_Indoors = 0x02,
+    kNLWirelessOperatingLocation_Outdoors = 0x03
+};
+
 // WiFi Operating Modes
 //
 typedef NS_ENUM(NSInteger, NLWiFiMode) {
diff --git a/src/device-manager/cocoa/NLWirelessRegConfig.h b/src/device-manager/cocoa/NLWirelessRegConfig.h
new file mode 100644
index 0000000..d98e5fa
--- /dev/null
+++ b/src/device-manager/cocoa/NLWirelessRegConfig.h
@@ -0,0 +1,47 @@
+/*
+ *
+ *    Copyright (c) 2020 Google LLC
+ *    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.
+ */
+
+/**
+ *    @file
+ *      Objective-C representation of wireless regulatory configuration information.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "NLWeaveDeviceManagerTypes.h"
+
+@interface NLWirelessRegConfig : NSObject
+
+@property (nonatomic, strong) NSString * RegDomain; // The region domain name
+@property (nonatomic) NLWirelessOperatingLocation OpLocation; // The Wireless Operating location.
+@property (nonatomic, strong) NSMutableArray * SupportedRegDomains; // The Supported Wireless reg domain.
+
+- (id)initWithSupportedRegDomains:(NSMutableArray *)supportedRegDomains
+                       opLocation:(NLWirelessOperatingLocation)opLocation
+                        RegDomain:(NSString *)regDomain;
+
+- (id)initWithRegDomain:(NSString *)regDomain opLocation:(NLWirelessOperatingLocation)opLocation;
+
+- (WEAVE_ERROR)getRegDomain:(NSString **)val;
+
+- (WEAVE_ERROR)getOpLocation:(NLWirelessOperatingLocation *)val;
+
+- (WEAVE_ERROR)getSupportedRegDomain:(NSMutableArray **)val;
+
+@end
diff --git a/src/device-manager/cocoa/NLWirelessRegConfig.mm b/src/device-manager/cocoa/NLWirelessRegConfig.mm
new file mode 100644
index 0000000..54ce393
--- /dev/null
+++ b/src/device-manager/cocoa/NLWirelessRegConfig.mm
@@ -0,0 +1,130 @@
+/*
+ *
+ *    Copyright (c) 2020 Google LLC
+ *    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.
+ */
+
+/**
+ *    @file
+ *      Objective-C representation of wireless regulatory configuration information.
+ *
+ */
+
+#import "NLLogging.h"
+#import "NLWeaveErrorCodes.h"
+#import "NLWirelessRegConfig_Protected.h"
+
+using nl::Weave::Profiles::NetworkProvisioning::WirelessOperatingLocation;
+using nl::Weave::Profiles::NetworkProvisioning::WirelessRegDomain;
+
+@implementation NLWirelessRegConfig
+
++ (NLWirelessRegConfig *)createUsing:(const WirelessRegConfig *)pWirelessRegConfig
+{
+    return [[NLWirelessRegConfig alloc] initWith:pWirelessRegConfig];
+}
+
+- (instancetype)initWithRegDomain:(NSString *)regDomain opLocation:(NLWirelessOperatingLocation)opLocation
+{
+    if (self = [self init]) {
+        _RegDomain = regDomain;
+        _OpLocation = opLocation;
+        _SupportedRegDomains = [NSMutableArray arrayWithCapacity:0];
+    }
+    return self;
+}
+
+- (instancetype)initWithSupportedRegDomains:(NSMutableArray *)supportedRegDomains
+                                 opLocation:(NLWirelessOperatingLocation)opLocation
+                                  RegDomain:(NSString *)regDomain
+{
+    if (self = [self init]) {
+        _RegDomain = regDomain;
+        _OpLocation = opLocation;
+        _SupportedRegDomains = supportedRegDomains;
+    }
+    return self;
+}
+
+- (instancetype)initWith:(const WirelessRegConfig *)pWirelessRegConfig
+{
+    if (self = [super init]) {
+        _RegDomain = [[NSString alloc] initWithCString:pWirelessRegConfig->RegDomain.Code encoding:NSUTF8StringEncoding];
+        _OpLocation = (NLWirelessOperatingLocation) pWirelessRegConfig->OpLocation;
+        _SupportedRegDomains = [[NSMutableArray alloc] initWithCapacity:pWirelessRegConfig->NumSupportedRegDomains];
+
+        WDM_LOG_DEBUG(@"pWirelessRegConfig->NumSupportedRegDomains = %u\n", pWirelessRegConfig->NumSupportedRegDomains);
+        for (uint32_t i = 0; i < pWirelessRegConfig->NumSupportedRegDomains; i++) {
+            NSMutableString * nlRegDomain = [NSMutableString string];
+            for (NSUInteger j = 0; j < sizeof(WirelessRegDomain::Code); j++) {
+                char ch = pWirelessRegConfig->SupportedRegDomains[i].Code[j];
+                [nlRegDomain appendFormat:@"%c", ch];
+            }
+            [_SupportedRegDomains addObject:nlRegDomain];
+        }
+        NSLog(@"_SupportedRegDomains is: %@", _SupportedRegDomains);
+    }
+    return self;
+}
+
+#pragma mark - Protected methods
+
+- (WirelessRegConfig)toWirelessRegConfig
+{
+    WirelessRegConfig wirelessRegConfig;
+
+    memcpy(wirelessRegConfig.RegDomain.Code, [_RegDomain UTF8String], [_RegDomain length]);
+    wirelessRegConfig.OpLocation = [self toWirelessOperatingLocation:_OpLocation];
+
+    wirelessRegConfig.NumSupportedRegDomains = 0;
+    // NOTE: The SupportRegulatoryDomains field is never sent *to* a device.  Thus we ignore the field value here.
+
+    return wirelessRegConfig;
+}
+
+- (nl::Weave::Profiles::NetworkProvisioning::WirelessOperatingLocation)toWirelessOperatingLocation:
+    (NLWirelessOperatingLocation)opLocation
+{
+    switch (opLocation) {
+    case kNLWirelessOperatingLocation_Unknown:
+        return nl::Weave::Profiles::NetworkProvisioning::kWirelessOperatingLocation_Unknown;
+    case kNLWirelessOperatingLocation_Indoors:
+        return nl::Weave::Profiles::NetworkProvisioning::kWirelessOperatingLocation_Indoors;
+    case kNLWirelessOperatingLocation_Outdoors:
+        return nl::Weave::Profiles::NetworkProvisioning::kWirelessOperatingLocation_Outdoors;
+    default:
+        return nl::Weave::Profiles::NetworkProvisioning::kWirelessOperatingLocation_NotSpecified;
+    }
+}
+
+- (WEAVE_ERROR)getRegDomain:(NSString **)val
+{
+    *val = _RegDomain;
+    return WEAVE_NO_ERROR;
+}
+
+- (WEAVE_ERROR)getOpLocation:(NLWirelessOperatingLocation *)val
+{
+    *val = _OpLocation;
+    return WEAVE_NO_ERROR;
+}
+
+- (WEAVE_ERROR)getSupportedRegDomain:(NSMutableArray **)val
+{
+    *val = _SupportedRegDomains;
+    return WEAVE_NO_ERROR;
+}
+
+@end
diff --git a/src/device-manager/cocoa/NLWirelessRegConfig_Protected.h b/src/device-manager/cocoa/NLWirelessRegConfig_Protected.h
new file mode 100644
index 0000000..5c6b643
--- /dev/null
+++ b/src/device-manager/cocoa/NLWirelessRegConfig_Protected.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *    Copyright (c) 2020 Google LLC
+ *    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.
+ */
+
+/**
+ *    @file
+ *      Objective-C representation of wireless regulatory configuration information.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "NLWirelessRegConfig.h"
+#include "WeaveDeviceManager.h"
+
+using nl::Weave::Profiles::NetworkProvisioning::WirelessRegConfig;
+
+@interface NLWirelessRegConfig ()
+
+- (instancetype)initWith:(const WirelessRegConfig *)pWirelessRegConfig NS_DESIGNATED_INITIALIZER;
+
++ (NLWirelessRegConfig *)createUsing:(const WirelessRegConfig *)pWirelessRegConfig;
+
+- (WirelessRegConfig)toWirelessRegConfig;
+@end