From ef190bc35cf47274ebe1df4e623c773c59f00f99 Mon Sep 17 00:00:00 2001 From: Soemin Tjong Date: Wed, 22 Feb 2017 01:17:52 -0800 Subject: [PATCH] Initial commit of IPCA. IPCA stands for IoTivity Procedural Client Api, which implements C APIs for accessing IoTivity RESTful CRUDN APIs. IPCA uses the C++ APIs provided by OCPlatform and OCResource. It is intended to be used by application developers, or by code generated from swagger, to develop IoTivity client apps. IPCA supports APIs for Discovery of IoTivity devices, Create, Delete and Observe resources, Get and Set property values, and security related APIs for password display (typically used in Onboarding Tool case) and password input (typically used in Multiple Ownership Transfer case). Please find more info in: https://wiki.iotivity.org/ipca. And more info in code: inc\IPCA.h for API documentation. src for the implementation of IPCA. samples\ElevatorClient for sample application that uses the IPCA api. samples\ipcaapp for sample application that discovers any IoTivity devices on the network. Change-Id: I683b02786d4df32b2feacdd9f1674350214b0618 Signed-off-by: Soemin Tjong Reviewed-on: https://gerrit.iotivity.org/gerrit/17449 Tested-by: jenkins-iotivity Reviewed-by: Dan Mihai --- resource/IPCA/SConscript | 35 + resource/IPCA/inc/ipca.h | 1151 +++++++++++++ .../IPCA/samples/ElevatorClient/ElevatorClient.cpp | 783 +++++++++ resource/IPCA/samples/ElevatorClient/OCFDevice.cpp | 157 ++ resource/IPCA/samples/ElevatorClient/OCFDevice.h | 60 + resource/IPCA/samples/ElevatorClient/SConscript | 44 + .../ElevatorServer/ElevatorServerSample.cpp | 35 + .../ElevatorServer/ElevatorServerSecurityDB.dat | Bin 0 -> 973 bytes .../ElevatorServer/ElevatorServerSecurityDB.json | 73 + resource/IPCA/samples/ElevatorServer/SConscript | 89 + .../IPCA/samples/ElevatorServer/elevatorserver.cpp | 484 ++++++ .../IPCA/samples/ElevatorServer/elevatorserver.h | 116 ++ resource/IPCA/samples/SConscript | 30 + resource/IPCA/samples/ipcaapp/SConscript | 44 + resource/IPCA/samples/ipcaapp/ipcaapp.cpp | 651 ++++++++ resource/IPCA/src/SConscript | 96 ++ resource/IPCA/src/app.cpp | 740 ++++++++ resource/IPCA/src/callback.cpp | 849 ++++++++++ resource/IPCA/src/common.cpp | 313 ++++ resource/IPCA/src/device.cpp | 218 +++ resource/IPCA/src/inc/app.h | 182 ++ resource/IPCA/src/inc/callback.h | 215 +++ resource/IPCA/src/inc/common.h | 92 + resource/IPCA/src/inc/device.h | 109 ++ resource/IPCA/src/inc/ipcainternal.h | 49 + resource/IPCA/src/inc/ocfframework.h | 280 ++++ resource/IPCA/src/ipca.cpp | 427 +++++ resource/IPCA/src/ipca.def | 68 + resource/IPCA/src/ipcavariant.cpp | 649 ++++++++ resource/IPCA/src/ocfframework.cpp | 1760 ++++++++++++++++++++ resource/IPCA/src/pretendocprovision.cpp | 109 ++ resource/IPCA/unittests/IPCAElevatorClient.cpp | 705 ++++++++ resource/IPCA/unittests/IPCAElevatorClient.h | 148 ++ resource/IPCA/unittests/IPCAUnitTest.dat | Bin 0 -> 1839 bytes resource/IPCA/unittests/IPCAUnitTest.json | 136 ++ resource/IPCA/unittests/SConscript | 132 ++ resource/IPCA/unittests/ipcatestdata.h | 52 + resource/IPCA/unittests/ipcaunittests.cpp | 614 +++++++ .../IPCA/unittests/mockInProcClientWrapper.cpp | 296 ++++ .../IPCA/unittests/mockInProcServerWrapper.cpp | 195 +++ resource/IPCA/unittests/mockOC.cpp | 849 ++++++++++ resource/IPCA/unittests/mockOCPlatform_impl.cpp | 87 + resource/IPCA/unittests/testelevatorclient.cpp | 395 +++++ resource/IPCA/unittests/testelevatorclient.h | 92 + resource/IPCA/unittests/testelevatorserver.cpp | 585 +++++++ resource/IPCA/unittests/testelevatorserver.h | 124 ++ resource/SConscript | 4 + resource/csdk/include/octypes.h | 3 + resource/csdk/stack/octbstack_product_secured.def | 2 + resource/unit_tests.scons | 4 + 50 files changed, 14331 insertions(+) create mode 100644 resource/IPCA/SConscript create mode 100644 resource/IPCA/inc/ipca.h create mode 100644 resource/IPCA/samples/ElevatorClient/ElevatorClient.cpp create mode 100644 resource/IPCA/samples/ElevatorClient/OCFDevice.cpp create mode 100644 resource/IPCA/samples/ElevatorClient/OCFDevice.h create mode 100644 resource/IPCA/samples/ElevatorClient/SConscript create mode 100644 resource/IPCA/samples/ElevatorServer/ElevatorServerSample.cpp create mode 100644 resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.dat create mode 100644 resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.json create mode 100644 resource/IPCA/samples/ElevatorServer/SConscript create mode 100644 resource/IPCA/samples/ElevatorServer/elevatorserver.cpp create mode 100644 resource/IPCA/samples/ElevatorServer/elevatorserver.h create mode 100644 resource/IPCA/samples/SConscript create mode 100644 resource/IPCA/samples/ipcaapp/SConscript create mode 100644 resource/IPCA/samples/ipcaapp/ipcaapp.cpp create mode 100644 resource/IPCA/src/SConscript create mode 100644 resource/IPCA/src/app.cpp create mode 100644 resource/IPCA/src/callback.cpp create mode 100644 resource/IPCA/src/common.cpp create mode 100644 resource/IPCA/src/device.cpp create mode 100644 resource/IPCA/src/inc/app.h create mode 100644 resource/IPCA/src/inc/callback.h create mode 100644 resource/IPCA/src/inc/common.h create mode 100644 resource/IPCA/src/inc/device.h create mode 100644 resource/IPCA/src/inc/ipcainternal.h create mode 100644 resource/IPCA/src/inc/ocfframework.h create mode 100644 resource/IPCA/src/ipca.cpp create mode 100644 resource/IPCA/src/ipca.def create mode 100644 resource/IPCA/src/ipcavariant.cpp create mode 100644 resource/IPCA/src/ocfframework.cpp create mode 100644 resource/IPCA/src/pretendocprovision.cpp create mode 100644 resource/IPCA/unittests/IPCAElevatorClient.cpp create mode 100644 resource/IPCA/unittests/IPCAElevatorClient.h create mode 100644 resource/IPCA/unittests/IPCAUnitTest.dat create mode 100644 resource/IPCA/unittests/IPCAUnitTest.json create mode 100644 resource/IPCA/unittests/SConscript create mode 100644 resource/IPCA/unittests/ipcatestdata.h create mode 100644 resource/IPCA/unittests/ipcaunittests.cpp create mode 100644 resource/IPCA/unittests/mockInProcClientWrapper.cpp create mode 100644 resource/IPCA/unittests/mockInProcServerWrapper.cpp create mode 100644 resource/IPCA/unittests/mockOC.cpp create mode 100644 resource/IPCA/unittests/mockOCPlatform_impl.cpp create mode 100644 resource/IPCA/unittests/testelevatorclient.cpp create mode 100644 resource/IPCA/unittests/testelevatorclient.h create mode 100644 resource/IPCA/unittests/testelevatorserver.cpp create mode 100644 resource/IPCA/unittests/testelevatorserver.h diff --git a/resource/IPCA/SConscript b/resource/IPCA/SConscript new file mode 100644 index 0000000..a292604 --- /dev/null +++ b/resource/IPCA/SConscript @@ -0,0 +1,35 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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. +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +## +# 'ipca' sub-project main build script +# +## + +Import('env') + +ipca_env = env.Clone() +ipca_env.AppendUnique(CCFLAGS=['/W4']) + +# Build ipca lib +SConscript('src/SConscript', 'ipca_env') + +# Build examples +SConscript('samples/SConscript', 'ipca_env') \ No newline at end of file diff --git a/resource/IPCA/inc/ipca.h b/resource/IPCA/inc/ipca.h new file mode 100644 index 0000000..c71e576 --- /dev/null +++ b/resource/IPCA/inc/ipca.h @@ -0,0 +1,1151 @@ +/****************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef IPCA_H_ // IoTivity Procedural Client API +#define IPCA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A physical device (piece of plastic) is represented by a platform object with a UUID identifier + * + * A logical device is a virtual device that runs on the platform and is represented by a UUID + * identifier. Multiple logical devices can run on a platform. + * + * A logical device is made up of one or more resources. Each resource is identified by a "/" + * separated string which is unique within the device. + * + * A resource implements one or more resource types. A resource type is identified by a "." + * separated string which is unique within the resource. + * + * Finally each resource type has a number of properties. Each property is represented by a pair of + * key and value. + * + * Below is an example of a physical device with one logical device. The logical device implements + * multiple resources as follows: + * 1. Base platform resources (/oic/p, /oic/d) [note: refer to the OCF spec for a complete set of + * mandatory resources a device implements] + * 2. OEM Elevator resource + * 3. OEM Display screen resource + * + * Platform (id: UUID) + * | + * + Device (id: UUID) + * | + * + Resource (name: "/oic/d") + * | | + * | + Resource Type (name: "oic.wk.d", interface: "oic.if.r") + * | | + * | + Property (key: "piid", type: string, access: read-only) + * | + Property (key: "di", type: string, access: read-only) + * | + Property (key: "n", type: string, access: read-only) + * | + Property (key: "icv", type: string, access: read-only) + * | + Property (key: "dmv", type: string, access: read-only) + * | + * + Resource (name: "/oic/p") + * | | + * | + Resource Type (name: "oic.wk.p", interface: "oic.if.r") + * | | + * | + Property (key: "pi", type: string, access: read-only) + * | + Property (key: "mnmn", type: string, access: read-only) + * | + Property (key: "mnmo", type: string, access: read-only) + * | + Property (key: "mnos", type: string, access: read-only) + * | + Property (key: "mnfv", type: string, access: read-only) + * | + ... + * | + * + Resource ("/oem/elevator") + * | | + * | + Resource Type ("x.oem.elevator.status", interface: "oic.if.r") + * | | | + * | | + Property ("x.oem.CurrentFloor", type: integer, access: read-only) + * | | + Property ("x.oem.Direction", type: integer, access: read-only) + * | | + * | + Resource Type ("x.oem.elevator.operation", interface: "oic.if.a") + * | | | + * | | + Property ("x.oem.TargetFloor" type: integer, access: read-write) + * | | + * | + Resource Type ("x.oem.elevator.maintenance", interface: "oic.if.r") + * | | + * | + Property ("x.oem.MotorTemperature", type: integer, access: read-only) + * | + Property ("x.oem.PulleyTension", type: integer, access: read-only) + * | + * + Resource ("/oem/display") + * | + * + Resource Type ("x.oem.display.screen", interface: "oic.if.r, oic.if.w") + * | + * + Property ("x.oem.DisplayText", type: string, access: read-write) + * + Property ("x.oem.DisplayBackground", type: string, access: read-write) + */ + +/** + * Handle types returned by IPCA. + */ +typedef struct IPCAAppHandleStruct* IPCAAppHandle; +typedef struct IPCACallbackHandleStruct* IPCACallbackHandle; +typedef struct IPCADeviceHandleStruct* IPCADeviceHandle; +typedef struct IPCAPropertyBagHandleStruct* IPCAPropertyBagHandle; +typedef struct IPCAHandleStruct* IPCAHandle; + +struct IPCAUuid +{ + uint8_t uuid[16]; +}; + +// @todo: determine if this needs to map to __stdcall. +// On x86 this causes the following run time failure. +// "Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. +// This is usually a result of calling a function declared with one calling convention with a +// function pointer declared with a different calling convention." +#define IPCA_CALL + +/** + * Some information about the device from device discovery. + * Use IPCAGetDeviceInfo() for detailed device information. + * + * deviceId UUID of the device. The value is as defined in OCF 1.0 Security Specification + * for DeviceID. + * An example of UUID string: d99c2350-d95e-11e6-bf26-cec0c932ce01. + * deviceUris An array of device URIs (e.g., coap://[fe80::c0bd:f33:6783:78fe%2]:49555). + * A device can have multiple endpoints. + * deviceUriCount Number of device URIs in the deviceUris array. + * deviceName Human friendly name of the device. + */ +typedef struct +{ + const char* deviceId; + const char** deviceUris; + size_t deviceUriCount; + const char* deviceName; +} IPCADiscoveredDeviceInfo; + +/** + * The IPCA version. + */ +typedef enum +{ + IPCA_VERSION_1 = 1 +} IPCAVersion; + +/** + * Device information. + * + * version The IPCA version of the IPCADeviceInfo structure matching version + * requested in IPCAOpen. + * protocolIndependentId Protocol independent ID of the device. This is in UUID format. + * An example of UUID string: d99c2350-d95e-11e6-bf26-cec0c932ce01. + * deviceId UUID of the device. The value is as defined in OCF 1.0 Security + * Specification for DeviceID. + * deviceUris An array of of device URIs + * (e.g., coap://[fe80::c0bd:f33:6783:78fe%2]:49555). + * A device can have multiple endpoints. + * deviceUriCount Number of device URIs in the deviceUris array. + * deviceName Human friendly name of the device. + * deviceSpecVersion Spec version of the the core specification this device is + * implemented to. + * dataModelVersions Array of spec versions of the vertical specifications. + * dataModelVersionCount Number of spec versions in dataModelVersions array. + * localizedDescriptions Detailed description of the device in one or more languages. Each + * object has a 'language' key field and 'value' field. + * deviceSoftwareVersion Version of device software. + * manufacturerName Name of manufacturer of device. Each object has a 'language' key + * field and manufacturer name 'value' field. + * modelNumber Model number as designated by manufacturer. + */ +typedef struct +{ + IPCAVersion version; + const char* protocolIndependentId; + const char* deviceId; + const char** deviceUris; + size_t deviceUriCount; + const char* deviceName; + const char* deviceSpecVersion; + const char** dataModelVersions; + size_t dataModelVersionCount; + IPCAPropertyBagHandle localizedDescriptions; + const char* deviceSoftwareVersion; + IPCAPropertyBagHandle manufacturerNames; + const char* modelNumber; +} IPCADeviceInfo; + +/** + * Platform information. + * + * version The IPCA version of the IPCAPlatformInfo structure matching version + * requested in IPCAOpen. + * platformId UUID of platform identifier. + * An example of UUID string: d99c2350-d95e-11e6-bf26-cec0c932ce01. + * manufacturerName Name of the manufacturer of the platform. + * manufacturerURL Manufacturer's URL. + * modelNumber Model number as defined by the manufacturer. + * manufacturingDate Manufacturing date. + * platformVersion Platform version. + * osVersion Platform resident OS version. + * hardwareVersion Platform hardware version. + * firmwareVersion Platform firmware version. + * manufacturerSupportURL Manufacturer's support information URL. + * referenceTime Reference time for the device. + */ +typedef struct +{ + IPCAVersion version; + const char* platformId; + const char* manufacturerName; + const char* manufacturerURL; + const char* modelNumber; + const char* manufacturingDate; + const char* platformVersion; + const char* osVersion; + const char* hardwareVersion; + const char* firmwareVersion; + const char* manufacturerSupportURL; + const char* referenceTime; +} IPCAPlatformInfo; + +/** + * Application's information. + * + * appId UUID of the app. + * appName Friendly name of the app. + * appSoftwareVersion Software version of the app. + * appCompanyName Name of the software company that produced this app. + * + */ +typedef struct +{ + IPCAUuid appId; + const char* appName; + const char* appSoftwareVersion; + const char* appCompanyName; +} IPCAAppInfo; + +/** + * IPCA status. + */ +typedef enum +{ + IPCA_OK = 0, // Successful. + IPCA_FAIL, // Generic error status. + IPCA_ALREADY_OPENED, // IPCAOpen() is already called. + // Use IPCAClose() to close the IPCA session. + IPCA_INVALID_ARGUMENT, // Invalid parameters passed to IPCA methods. + IPCA_INVALID_GUID, // Invalid GUID string. + IPCA_OUT_OF_MEMORY, // Out of memory error. + IPCA_INFO_VERSION_NOT_SUPPORTED, // Requested info version is not supported. + + // Device related + IPCA_DEVICE_APPEAR_OFFLINE = 0x1000, // Device is either offline or not responding and + // assumed offline. + IPCA_DEVICE_NOT_DISCOVERED, // Device is not discovered by IPCADiscoverDevices(). + IPCA_INFORMATION_NOT_AVAILABLE, // Information is not available, caller can retry in a + // few seconds. + + // Resource related + IPCA_RESOURCE_NOT_FOUND = 0x2000, // No matching resource. + IPCA_RESOURCE_CREATED, // Device created new resource. + IPCA_RESOURCE_DELETED, // Device deleted resource. + IPCA_REQUEST_TIMEOUT, // No response from server. + + // Resource type related + IPCA_PROPERTY_NOT_SUPPORTED = 0x3000, // When property is not mandatory in a resource type, + // e.g., Factory_Reset in oic.wk.mnt. + + // Security related + IPCA_ACCESS_DENIED = 0x4000, // The requested access to a device has failed due to + // the current security settings. + // The application can then call IPCARequestAccess, to + // request updated settings. + + IPCA_SECURITY_UPDATE_REQUEST_FINISHED, // Security settings have been updated. Re-attemping an + // operation that previously failed with status + // IPCA_ACCESS_DENIED might yield a different result. + + IPCA_SECURITY_UPDATE_REQUEST_INCORRECT_PASSWORD, // Updating the security settings failed + // due to an incorrect password. + + IPCA_SECURITY_UPDATE_REQUEST_NOT_SUPPORTED, // Updating the security settings corresponding to + // a device is not supported. + + IPCA_SECURITY_UPDATE_REQUEST_FAILED, // Updating the security settings has been + // attempted, but failed. +} IPCAStatus; + +/** + * Callback from IPCA to application when device sends a resource change notification. + * To receive resource change notifications, application calls IPCAObserveResource() to set + * observation on a resource. + * + * @param[in] result Result of the call. + * @param[in] context Caller's context set in IPCAObserveResource(). + * @param[in] propertyBagHandle Handle to an IPCAPropertyBag object. + * Use IPCAPropertyBag APIs to retrieve the values indexed by keys. + * propertyBagHandle is only valid during this callback. + */ +typedef void (IPCA_CALL *IPCAResourceChangeCallback)(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); + +/** + * Callback from IPCA to application when device responds to IPCAGetProperties(). + * + * @param[in] result Result of the call. + * @param[in] context Caller's context set in IPCAGetProperties(). + * @param[in] propertyBagHandle Handle to an IPCAPropertyBag bag object. + * Use IPCAPropertyBag APIs to retrieve the values indexed by keys. + * propertyBagHandle is only valid during this callback. + */ +typedef void (IPCA_CALL *IPCAGetPropertiesComplete)(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); +/** + * Callback from IPCA to application when device responds to IPCASetProperties(). + * + * @param[in] result Result of the call. + * @param[in] context Caller's context set in IPCASetProperties(). + * @param[in] propertyBagHandle Handle to an IPCAPropertyBag object. + * Use IPCAPropertyBag APIs to retrieve the values indexed by keys. + * propertyBagHandle is only valid during this callback. + */ +typedef void (IPCA_CALL *IPCASetPropertiesComplete)(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); + +/** + * Callback from IPCA to application when device responds to IPCACreateResource(). + * + * @param[in] result Result of the call. + * @param[in] context Caller's context set in IPCACreateResource(). + * @param[in] newResourcePath The relative path of the newly created resource in the collection. + * @param[in] propertyBagHandle Handle to an IPCAPropertyBag object. + * Use IPCAPropertyBag APIs to retrieve the values indexed by keys. + * propertyBagHandle is only valid during this callback. + * + */ +typedef void (IPCA_CALL *IPCACreateResourceComplete)(IPCAStatus result, + void* context, + const char* newResourcePath, + IPCAPropertyBagHandle propertyBagHandle); + +/** + * Callback from IPCA to application when device responds to IPCADeleteResource(). + * + * @param[in] result Result of the call. + * @param[in] context Caller's context set in IPCADeleteResource(). + * + */ +typedef void (IPCA_CALL *IPCADeleteResourceComplete)(IPCAStatus result, void* context); + +/** + * Discovery status in IPCADiscoverDeviceCallback. + */ +typedef enum +{ + IPCA_DEVICE_DISCOVERED = 1, /* Device responded to discovery request. */ + IPCA_DEVICE_UPDATED_INFO, /* There's an update in discoveredDeviceInfo. */ + IPCA_DEVICE_STOPPED_RESPONDING, /* Device stopped responding to discovery request. */ +} IPCADeviceStatus; + + +/** + * Callback when a device that implements resourceTypeList in IPCADiscoverDevices() is either + * discovered or has stopped responding to discovery request. + * + * @param[in] context Application's context in IPCADiscoverDevices(). + * @param[in] deviceStatus See IPCADeviceStatus. + * @param[in] discoveredDeviceInfo Some information of device. + */ + +typedef void (IPCA_CALL *IPCADiscoverDeviceCallback)( + void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* discoveredDeviceInfo); + +/** + * An application calls this method one time to register with IPCA. + * + * @param[in] ipcaAppInfo Application's information. See IPCAAppInfo. + * @param[in] ipcaVersion The IPCAVersion the application can work with. + * @param[out] ipcaAppHandle Handle returned by IPCA. Use IPCAClose() to close the handle. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCA_CALL IPCAOpen(const IPCAAppInfo* ipcaAppInfo, + IPCAVersion ipcaVersion, + IPCAAppHandle* ipcaAppHandle); + +/** + * Close the handle returned in IPCAOpen(). + * After this call returns, application should consider any outstanding calls to devices canceled + * and opened handles closed. + * + * @param[in] ipcaAppHandle Handle returned in IPCAOpen(). + */ +void IPCA_CALL IPCAClose(IPCAAppHandle ipcaAppHandle); + + +/** + * Application calls this method to start discovery of devices that implement resource types + * specified in resourceTypeList. + * The underlying framework triggers discovery request periodically until caller cancels the + * request using IPCACloseHandle(). + * + * @param[in] ipcaAppHandle Application handle returned in IPCAOpen(). + * @param[in] callback Callback from IPCA when devices are found. + * @param[in] context Application's context that is passed back as argument in the + * callback. + * @param[in] resourceTypeList A list of resource types, a device must implement all of them + * to be discovered. Empty string indicates any resource type. + * @param[in] resourceTypeCount Number of resource types pointed by resourceTypeList. + * @param[out] handle Handle that can be used in IPCACloseHandle() to stop device + * discovery. + * Null handle pointer is not allowed. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCA_CALL IPCADiscoverDevices(IPCAAppHandle ipcaAppHandle, + IPCADiscoverDeviceCallback discoverDeviceCallback, + void* context, + const char* const* resourceTypeList, + int resourceTypeCount, + IPCAHandle* handle); + +/** + * Application indicates to IPCA the intention to start working with a device whose ID matches + * deviceID. + * + * @param[in] ipcaAppHandle Application handle returned in IPCAOpen(). + * @param[in] deviceId UUID of device (see: IPCADeviceInfo). + * @param[out] deviceHandle Handle to the device. Use IPCACloseDevice() to close device handle. + * + * @return IPCA_OK if successful. IPCA_DEVICE_NOT_DISCOVERED if device is not yet discovered, call + * IPCADiscoverDevices(). + */ +IPCAStatus IPCA_CALL IPCAOpenDevice(IPCAAppHandle ipcaAppHandle, + const char* deviceId, + IPCADeviceHandle* deviceHandle); + +/** + * Closes device handle. The application will stop receiving notifications and callbacks from the + * device. + * + * @param[in] deviceHandle device handle returned in IPCAOpenDevice(). + */ +void IPCA_CALL IPCACloseDevice(IPCADeviceHandle deviceHandle); + +/** + * This method returns a pointer to IPCADeviceInfo structure. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[out] deviceInfo IPCA returns a pointer to IPCADeviceInfo structure. + * Use IPCAFreeDeviceInfo() to free the memory. + * + * @return + * IPCA_OK if successful. + * IPCA_INFORMATION_NOT_AVAILABLE if server has not returned the device info query. + * IPCA_INFO_VERSION_NOT_SUPPORTED when requested info version is not supported. + */ +IPCAStatus IPCAGetDeviceInfo(IPCADeviceHandle deviceHandle, IPCADeviceInfo** deviceInfo); + +/** + * Free the memory allocated in IPCAGetDeviceInfo(). + * + * @param[in] deviceInfo a Pointer to IPCADeviceInfo structure returned in IPCAGetDeviceInfo(). + */ +void IPCAFreeDeviceInfo(IPCADeviceInfo* deviceInfo); + +/** + * This method returns a pointer to IPCAPlatformInfo structure. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[out] platformInfo IPCA returns a pointer to IPCAPlatformInfo structure. + * Use IPCAFreePlatformInfo() to free the memory. + * + * @return + * IPCA_OK if successful. + * IPCA_INFORMATION_NOT_AVAILABLE if server has not returned the platform info query. + * IPCA_INFO_VERSION_NOT_SUPPORTED when requested info version is not supported. + */ +IPCAStatus IPCAGetPlatformInfo(IPCADeviceHandle deviceHandle, IPCAPlatformInfo** platformInfo); + +/** + * Free the memory allocated in IPCAGetPlatformInfo(). + * + * @param[in] platformInfo A pointer to IPCAPlatformInfo structure returned + * in IPCAGetPlatformInfo(). + */ +void IPCAFreePlatformInfo(IPCAPlatformInfo* platformInfo); + +/** + * Get the resources that implement resource types in IPCADiscoverDevices(). + * Application must call IPCAFreeStringArray() to free memory allocated for resourcePathList. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourceInterface Interface the resource must support. Use NULL or empty string + * for any interface. + * @param[in] resourceType Resource type the resource must implement. Use NULL or empty + * string for any resource type. + * @param[out] resourcePathList A pointer to an array of resource path, a percent encoded URI + * reference, that IPCA returns. + * @param[out] resourcePathCount Number of resource paths returned. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCA_CALL IPCAGetResources(IPCADeviceHandle deviceHandle, + const char* resourceInterface, + const char* resourceType, + char*** resourcePathList, + size_t* resourcePathCount); + +/** + * Get the list of resource types implemented by a resource. + * Application must call IPCAFreeStringArray() to free memory allocated for resourceTypeList. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourcePath Resource path, a percent encoded URI reference, e.g., "/oic/d". + * If resourcePath is equal to NULL or empty string, IPCA returns + * all resource types. + * @param[out] resourceTypeList A pointer to an array of resource types that IPCA returns. + * @param[out] resourceTypeCount Number of resource types returned. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCA_CALL IPCAGetResourceTypes(IPCADeviceHandle deviceHandle, + const char* resourcePath, + char*** resourceTypeList, + size_t* resourceTypeCount); + +/** + * Get the list of resource interfaces implemented by a resource. + * Application must call IPCAFreeStringArray() to free memory allocated for resourceInterfaceList. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourcePath Resource path, a percent encoded URI reference, + * e.g., "/oic/d". + * If resourcePath is equal to NULL or empty string, + * IPCA returns all interfaces the device supports. + * @param[out] resourceInterfaceList A pointer to an array of resource interfaces. + * @param[out] resourceInterfaceCount Number of resource interfaces returned. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCA_CALL IPCAGetResourceInterfaces(IPCADeviceHandle deviceHandle, + const char* resourcePath, + char*** resourceInterfaceList, + size_t* resourceInterfaceCount); + +/** + * Free string array returned in IPCAGetResources(), IPCAGetResourceTypes(), + * IPCAGetResourceInterfaces(). + * + * @param[in] stringArray Array of strings. + * @param[in] stringCount Number of strings. + * + */ +void IPCA_CALL IPCAFreeStringArray(char** stringArray, size_t stringCount); + +/** + * Get property values of a resource. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] getPropertiesComplete Caller's callback when device responds to + * IPCAGetProperties() + * @param[in] context Application's context that is passed back as argument in the + * callback. + * @param[in] resourcePath Resource which implements the resource type. E.g., "/oic/d". + * @param[in] resourceInterface Resource interface to be used. Use NULL or empty string for + * default interface. + * @param[in] resourceType The resource type which implements the properties. + * @param[out] handle Handle that can be used in IPCACloseHandle() to cancel the + * callback. + * The reference count to the handle is handled by IPCA. + * Therefore, caller does not need to close the handle. + * Null handle pointer is allowed when caller does not plan to + * receive device's response. + * + * @return IPCA_OK if successful. IPCA calls getPropertiesComplete() when device responds. + */ +IPCAStatus IPCA_CALL IPCAGetProperties(IPCADeviceHandle deviceHandle, + IPCAGetPropertiesComplete getPropertiesComplete, + void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAHandle* handle); + +/** + * Set property values of a resource. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] setPropertiesComplete Caller's callback when device responds to + * IPCASetProperties() + * @param[in] context Application's context that is passed back as argument in the + * callback. + * @param[in] resourcePath Resource path which implements the resource type. + * E.g., "/oic/d". + * @param[in] resourceInterface Resource interface to be used. Use NULL or empty string for + * default interface. + * @param[in] resourceType The resource type which implements the properties. + * @param[in] propertyBagHandle Data to set (see IPCAPropertyBagCreate()). + * @param[out] handle Handle that can be used in IPCACloseHandle() to cancel the + * callback. + * The reference count to the handle is handled by IPCA. + * Therefore, caller does not need to close the handle. + * Null handle pointer is allowed when caller does not plan to + * receive device's response. + * + * @return IPCA_OK if successful. IPCA calls setPropertiesComplete() when device reponds. + */ +IPCAStatus IPCA_CALL IPCASetProperties(IPCADeviceHandle deviceHandle, + IPCASetPropertiesComplete setPropertiesComplete, + void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAPropertyBagHandle propertyBagHandle, + IPCAHandle* handle); + +/** + * Request device to create a new resource. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] createResourceComplete Caller's callback when device responds to + * IPCACreateResource() + * @param[in] context Application's context that is passed back as argument in + * the callback. + * @param[in] resourceCollectionPath Resource that handles resource creation. + * @param[in] resourceInterface Resource interface to be used. Use NULL or empty string for + * default interface. + * @param[in] resourceType The resource type which implements the properties. + * @param[in] propertyBagHandle Data to set (see IPCAPropertyBagCreate()). + * @param[out] handle Handle that can be used in IPCACloseHandle() to cancel the + * callback. + * The reference count to the handle is handled by IPCA. + * Therefore, caller does not need to close the handle. + * Null handle pointer is allowed when caller does not plan to + * receive device's response. + * + * @return IPCA_OK if successful. IPCA calls createResourceComplete() when device reponds. + */ +IPCAStatus IPCA_CALL IPCACreateResource(IPCADeviceHandle deviceHandle, + IPCACreateResourceComplete createResourceComplete, + void* context, + const char* resourceCollectionPath, + const char* resourceInterface, + const char* resourceType, + IPCAPropertyBagHandle propertyBagHandle, + IPCAHandle* handle); + +/** + * Request device to delete a resource. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] deleteResourceComplete Caller's callback when device responds to + * IPCADeleteResource() + * @param[in] context Application's context that is passed back as argument in + * the callback. + * @param[in] resourcePath Resource path of resource to be deleted. + * @param[out] handle Handle that can be used in IPCACloseHandle() to cancel the + * callback. + * The reference count to the handle is handled by IPCA. + * Therefore, caller does not need to close the handle. + * Null handle pointer is allowed when caller does not plan to + * receive device's response. + * + * @return IPCA_OK if successful. IPCA calls deleteResourceComplete() when device reponds. + */ + +IPCAStatus IPCA_CALL IPCADeleteResource(IPCADeviceHandle deviceHandle, + IPCADeleteResourceComplete deleteResourceComplete, + void* context, + const char* resourcePath, + IPCAHandle* handle); + +/** + * Function returns whether a resource is observable. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourcePath Resource to check for observability. + * @param[out] isObservable Set to true if resourcePath is observable. Set to false otherwise. + * + * @return IPCA_OK if successful. + */ +void IPCA_CALL IPCAIsResourceObservable(IPCADeviceHandle deviceHandle, + const char* resourcePath, + bool* isObservable); + +/** + * Function to start observing a resource. Use IPCACloseHandle() to stop receiving resource change + * notifications from the device. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourceChangeCallback Caller's callback when device sends a resource change + * notification. + * @param[in] context Application's context that is passed back as argument in the + * callback. + * @param[in] resourcePath Resource to observe. E.g., "/oic/d". + * @param[in] resourceType The resource type which implements the properties to + * observe. + * @param[out] handle Handle that can be used in IPCACloseHandle() to stop + * observing the resource. + * Null handle pointer is not allowed. + * + * @return IPCA_OK if successful. IPCA calls resourceChangeCallback() when device sends + * notifications. + */ +IPCAStatus IPCA_CALL IPCAObserveResource(IPCADeviceHandle deviceHandle, + IPCAResourceChangeCallback resourceChangeCallback, + void* context, + const char* resourcePath, + const char* resourceType, + IPCAHandle* handle); + +/** + * Stop receiving callbacks for the handle. + * For handle returned by IPCAObserveResource(), IPCACloseHandle unsubscribes server's resource + * change notification. + * + * @param[in] handle Handle returned in the following methods: + * IPCAGetProperties() + * IPCASetProperties() + * IPCAObserveResource() + * IPCADiscoverDevices() + * + */ +void IPCA_CALL IPCACloseHandle(IPCAHandle handle); + +/** + * Perform factory reset. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * + * @return IPCA_OK if successful. IPCA_PROPERTY_NOT_SUPPORTED if device does not support factory + * reset. + */ +IPCAStatus IPCA_CALL IPCAFactoryReset(IPCADeviceHandle deviceHandle); + +/** + * Perform device warm reset. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * + * @return IPCA_OK if successful. IPCA_PROPERTY_NOT_SUPPORTED if device does not support reboot + * request. + */ +IPCAStatus IPCA_CALL IPCAReboot(IPCADeviceHandle deviceHandle); + +/** + * Create a handle to an IPCAPropertyBag object. Use IPCAPropertyBagDestroy() to delete the object. + * + * @param[out] propertyBagHandle Handle to an IPCAPropertyBag object. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagCreate(IPCAPropertyBagHandle* propertyBagHandle); + +/** + * Delete an IPCPropertyBag object. + * + * @param[in] propertyBagHandle Handle returned in IPCAPropertyBagCreate(). + */ + +void IPCAPropertyBagDestroy(IPCAPropertyBagHandle propertyBagHandle); + +/** + * Get the resource path of the property bag. + * Use IPCAPropertyBagFreeString() to free string buffer returned in resourcePath. + * + * @param[in] propertyBagHandle IPCAPropertyBagHandle object returned by device. + * @param[out] resourcePath Buffer containing resource path, caller must use + * IPCAPropertyBagFreeString() to free the buffer. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagGetResourcePath(IPCAPropertyBagHandle propertyBagHandle, + char** resourcePath); + +/** + * Value type of a property in a property bag. + */ +typedef enum +{ + IPCA_INTEGER = 1, + IPCA_DOUBLE, + IPCA_BOOLEAN, + IPCA_STRING, + IPCA_ARRAY, + IPCA_PROPERTY_BAG, + IPCA_VALUE_TYPE_NOT_SUPPORTED +} IPCAValueType; + +/** + * Get the key and the value type of all the properties in a property bag. + * Caller must free the memory allocated for the keys and valueTypes array returned. + * + * @param[in] propertyBagHandle IPCAPropertyBagHandle object returned by device. + * @param[out] keys A pointer to an array of property names. + * @param[out] valueTypes A pointer to an array of property value types. + * Note: valueTypes[0] is the value type for property keys[0] + * and so on. + * @param[out] count Number of entries in keys and valueTypes array. + * + * Note: + * Use IPCAPropertyBagFreeStringArray() to free keys buffer. + * Use IPCAPropertyBagFreeIPCAValueTypeArray() to free valueTypes buffer. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagGetAllKeyValuePairs(IPCAPropertyBagHandle propertyBagHandle, + char*** keys, + IPCAValueType** valueTypes, + size_t* count); + +/** + * Free memory allocated for IPCAValueType in IPCAPropertyBagGetAllKeyValuePairs(). + * + * @param[in] valueArray Array of IPCAValueType values returned in + * IPCAPropertyBagGetAllKeyValuePairs(). + */ +void IPCAPropertyBagFreeIPCAValueTypeArray(IPCAValueType* valueArray); + +/** + * Set property value. + * + * @param[in] propertyBagHandle Handle returned in IPCAPropertyBagCreate. + * @param[in] key The key of the key-value pair to be stored in the + * propertyBagHandle. + * @param[in] value The value of the key-value pair to be stored in propertyBagHandle. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagSetValueInt(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int value); + +IPCAStatus IPCAPropertyBagSetValueDouble(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double value); + +IPCAStatus IPCAPropertyBagSetValueBool(IPCAPropertyBagHandle + propertyBagHandle, + const char* key, + bool value); + +IPCAStatus IPCAPropertyBagSetValueString(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const char* value); + +IPCAStatus IPCAPropertyBagSetValuePropertyBag(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const IPCAPropertyBagHandle value); + +/** + * Set array value. + * + * @param[in] propertyBagHandle Handle returned in IPCAPropertyBagCreate. + * @param[in] key The key of the key-value pair to be stored in the + * propertyBagHandle. + * @param[in] valueArray The array value of the key-value pair to be stored in + * propertyBagHandle. + * @param[in] valueCount Size of the valueArray. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagSetValueIntArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const int* valueArray, + size_t valueCount); + +IPCAStatus IPCAPropertyBagSetValueDoubleArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const double* valueArray, + size_t valueCount); + +IPCAStatus IPCAPropertyBagSetValueBoolArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const bool* valueArray, + size_t valueCount); + +IPCAStatus IPCAPropertyBagSetValueStringArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const char** valueArray, + size_t valueCount); + +IPCAStatus IPCAPropertyBagSetValuePropertyBagArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const IPCAPropertyBagHandle* valueArray, + size_t valueCount); + +/** + * Get property value. + * + * @param[in] propertyBagHandle Handle returned in IPCAPropertyBagCreate or IPCAPropertyBagHandle + * object returned by device. + * @param[in] key The key of key-value pair of data stored in propertyBagHandle. + * @param[out] value The value of key-value pair of data stored in propertyBagHandle. + * + * Note: + * + * For IPCAPropertyBagGetValueString() caller must use IPCAPropertyBagFreeString() to free the + * string buffer returned in value argument. + * + * For IPCAPropertyBagGetValuePropertyBag(), use IPCAPropertyBagDestroy() to free the + * IPCAPropertyBagHandle returned in value argument. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagGetValueInt(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int* value); + +IPCAStatus IPCAPropertyBagGetValueDouble(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double* value); + +IPCAStatus IPCAPropertyBagGetValueBool(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + bool* value); + +IPCAStatus IPCAPropertyBagGetValueString(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + char** value); + +IPCAStatus IPCAPropertyBagGetValuePropertyBag(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + IPCAPropertyBagHandle* value); + +/** + * Get array value. + * + * @param[in] propertyBagHandle Handle returned in IPCAPropertyBagCreate or IPCAPropertyBagHandle + * object returned by device. + * @param[in] key The key of the key-value pair to be stored in the + * propertyBagHandle. + * @param[out] valueArray The array value of the key-value pair to be stored in + * propertyBagHandle. + * @param[out] valueCount Size of the valueArray. + * + * Note: + * Use corresponding IPCAPropertyBagFreeXXXXArray()to free the array of value type returned in value + * buffer. + * + * @return IPCA_OK if successful. + */ +IPCAStatus IPCAPropertyBagGetValueIntArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int** value, + size_t* valueCount); + +IPCAStatus IPCAPropertyBagGetValueDoubleArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double** value, + size_t* valueCount); + +IPCAStatus IPCAPropertyBagGetValueBoolArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + bool** value, + size_t* valueCount); + +IPCAStatus IPCAPropertyBagGetValueStringArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + char*** value, + size_t* valueCount); + +IPCAStatus IPCAPropertyBagGetValuePropertyBagArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + IPCAPropertyBagHandle** value, + size_t* valueCount); + +/** + * Free array types. + */ +void IPCAPropertyBagFreeIntArray(int* valueArray); +void IPCAPropertyBagFreeDoubleArray(double* valueArray); +void IPCAPropertyBagFreeBoolArray(bool* valueArray); +void IPCAPropertyBagFreeStringArray(char** valueArray, size_t valueCount); +void IPCAPropertyBagFreePropertyBagArray(IPCAPropertyBagHandle* valueArray, size_t valueCount); + +/** + * Free stringBuffer allocated in IPCAPropertyBagGetValueString(). + * + * @param[in] stringBuffer String buffer returned in IPCAPropertyBagGetValueString(). + */ +void IPCAPropertyBagFreeString(char* stringBuffer); + +/** + * Security authentication type used during Ownership Transfer communication. + * + * Ownership Transfer is currently supported in two different cases: + * - Between an Onboarding Tool (OBT) and the application. The OBT becomes the owner of the + * application. + * - Between the application and a device, if the device has Multiple Owner Transfer (MOT) enabled. + * The application becomes one of the sub-owners of the device. + * + */ +typedef enum +{ + IPCA_OWNERSHIP_TRANSFER_JUST_WORKS = 0, // No authentication is required. + + IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN = 1, // Either: + // - The device displays a random password, and the + // user inputs the same password in the + // application, or: + // - The application displays a random password, and + // the user inputs the same password in an + // onboarding tool. + + IPCA_OWNERSHIP_TRANSFER_MANUFACTURER_CERTIFICATE = 2, // Authenticate using a pre-provisioned + // security certificate. + + IPCA_OWNERSHIP_TRANSFER_PRECONFIGURED_PIN = 0xFF00, // The device owner configures a + // password using an onboarding tool. + // Then, the application obtains the + // password and passes it to IPCA. +} IPCAOwnershipTransferType; + +/** + * This callback will be called whenever a device requires that the app provide a password, in order + * to complete the security provisioning of the application. This will be called when a device + * implements the "Multiple Ownership Transfer" (MOT) method. The app is responsible for eliciting + * the appropriate password from the user. + * + * @param[in] context Applications's context set in IPCASetPasswordCallbacks(). + * @param[in] deviceInformation Information about the device or onboarding tool requiring + * authentication. + * @param[in] platformInformation Information about the platform of the device or onboarding tool + * requiring authentication. + * @param[in] type The type of authentication requiring the password entry. An + * application might display different messages to the user, + * depending on the value of authenticationType. + * The current version of IPCA supports these values for this + * parameter: + * - IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN + * - IPCA_OWNERSHIP_TRANSFER_PRECONFIGURED_PIN + * @param[out] passwordBuffer Buffer to fill with a password, as a zero-terminated string. The + * application can obtain the password: + * - Directly from the MOT-enabled device, for + * IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN + * - From the owner of the MOT-enabled device, for + * IPCA_OWNERSHIP_TRANSFER_PRECONFIGURED_PIN + * @param[in] passwordBufferSize Size of passwordBuffer. + * + * @return IPCA_OK if successful. IPCA_FAIL if the app did not provide a password, or if the + * passwordBufferSize was too small. + */ +typedef IPCAStatus (IPCA_CALL *IPCAProvidePasswordCallback)( + void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + char* passwordBuffer, + size_t passwordBufferSize); + +/** + * Callback used to display a password in the application. The user would typically read this + * password and communicate it to the user of an onboarding tool. + * + * @param[in] context Applications's context set in IPCASetPasswordCallbacks(). + * @param[in] deviceInformation Information about the device or onboarding tool requiring + * authentication. + * @param[in] platformInformation Information about the platform of the device or onboarding tool + * requiring authentication. + * @param[in] type The type of authentication that requires displaying the + * password. + * An application might display different messages to the user, + * depending on the value of authenticationType. The current + * version of IPCA supports the following value for this parameter: + * - IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN - to authenticate with an + * onboarding tool. + * @param[in] password Password to be displayed, as a zero-terminated string. + * + * @return IPCA_OK if successful. IPCA_FAIL if the app or its user decided to cancel the + * authentication with the onboarding tool. + */ +typedef IPCAStatus (IPCA_CALL *IPCADisplayPasswordCallback)( + void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + const char* password); + +/** + * An application calls this method one time to set up its password input and display callbacks. + * The callbacks get automatically removed by IPCA, when the application calls IPCAClose for the + * ipcaAppHandle. + * + * @param[in] ipcaAppHandle: Handle returned by IPCAOpen(). + * @param[in] providePasswordCallback: Callback from IPCA when the application must obtain a + * password and provide it to IPCA. + * @param[in] displayPasswordCallback: Callback from IPCA when the application must display a + * password to the user. + * @param[in] context: Application's context that is passed back by IPCA as + * argument in the password callbacks. + * + */ +IPCAStatus IPCA_CALL IPCASetPasswordCallbacks(IPCAAppHandle ipcaAppHandle, + IPCAProvidePasswordCallback providePasswordCallback, + IPCADisplayPasswordCallback displayPasswordCallback, + void* context); + +/** + * Callback from IPCA when a request initiated by the application using IPCARequestAccess has been + * completed. + * + * @param[in] completionStatus One of IPCA_SECURITY_UPDATE_REQUEST_FINISHED, + * IPCA_SECURITY_UPDATE_REQUEST_INCORRECT_PASSWORD, + * IPCA_SECURITY_UPDATE_REQUEST_NOT_SUPPORTED, + * IPCA_SECURITY_UPDATE_REQUEST_FAILED. + * @param[in] context Applications's context set in IPCARequestAccess(). + */ +typedef void (IPCA_CALL *IPCARequestAccessCompletionCallback)(IPCAStatus completionStatus, + void* context); + +/** + * Initiate a request to access a device. Called by the application after IPCAGetProperties or + * IPCASetProperties failed with status IPCA_ACCESS_DENIED. completionCallback gets called after the + * access request has been processed by IPCA. + * + * @param[in] deviceHandle Device handle returned in IPCAOpenDevice(). + * @param[in] resourcePath E.g., "/oic/d". If NULL the application requests access to all + * vertical resources on the device. + * @param[in] completionCallback Callback from IPCA when access request has been completed. + * @param[in] context Application's context that is passed back as argument in the + * completion callback. + * @param[out] handle Handle that can be used in IPCACloseHandle() to cancel the + * request. + * The reference count to the handle is handled by IPCA. Therefore, + * the caller does not need to close the handle. Null handle + * pointer is allowed when the caller does not plan to cancel the + * request. + * @return + * IPCA_OK if the request has been successfully initiated. + */ +IPCAStatus IPCA_CALL IPCARequestAccess(IPCADeviceHandle deviceHandle, + const char* resourcePath, + IPCARequestAccessCompletionCallback completionCallback, + void* context, + IPCAHandle* handle); +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // IPCA_H_ diff --git a/resource/IPCA/samples/ElevatorClient/ElevatorClient.cpp b/resource/IPCA/samples/ElevatorClient/ElevatorClient.cpp new file mode 100644 index 0000000..d284d1c --- /dev/null +++ b/resource/IPCA/samples/ElevatorClient/ElevatorClient.cpp @@ -0,0 +1,783 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipca.h" +#include "OCFDevice.h" + +#if defined(_MSC_VER) +#define UNREFERENCED_PARAMETER(P) (P) +#else +#define UNREFERENCED_PARAMETER(P) +#endif + +// Forward decls. +extern bool RediscoverElevator(); +extern void ResourceChangeNotificationCallback(IPCAStatus result, void* context, + IPCAPropertyBagHandle propertyBagHandle); + +// Key is device id. Value is pointer to OCFDevice. +std::map g_OCFDeviceList; +std::recursive_mutex g_globalMutex; // Sync access to g_OCFDeviceList. + +// Command line arguments +std::string g_targetDeviceId; +bool g_isList = false; +bool g_isId = false; +bool g_isSet = false; +bool g_isGet = false; +bool g_isObserve = false; +bool g_isAuth = false; +int g_targetFloor = 0; +bool g_isContinuous = false; + +// Handles +IPCADeviceHandle g_deviceHandle = nullptr; +IPCAHandle g_observeHandle = nullptr; +IPCAAppHandle g_ipcaAppHandle = nullptr; + +// Operation flags +bool g_targetElevatorDiscovered = false; // Set to true when device matching g_targetDeviceId is + // discovered. +bool g_rediscoverFailedQuitNow = false; + +// Call IPCAObserveResource(); +IPCAStatus RequestObserve() +{ + if (g_observeHandle != nullptr) + { + IPCACloseHandle(g_observeHandle); + g_observeHandle = nullptr; + } + + return IPCAObserveResource( + g_deviceHandle, + &ResourceChangeNotificationCallback, + 0, + "/ipca/sample/elevator", + nullptr, + &g_observeHandle); +} + +// Callback from IPCAObserveResource(). +void ResourceChangeNotificationCallback(IPCAStatus result, void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + UNREFERENCED_PARAMETER(context); + + int observedCurrentFloor = -1, observedDirection = -1, observedTargetFloor = -1; + + if (result != IPCA_OK) + { + std::cout << "!!! ResourceChangeNotificationCallback callback !!! error: " << result + << std::endl; + + RediscoverElevator(); + if (g_rediscoverFailedQuitNow != true) + { + // Restart the observe request. + RequestObserve(); + } + + return; + } + + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.CurrentFloor", + &observedCurrentFloor)) + { + std::cout << "ResourceChangeNotificationCallback(): failed get CurrentFloor." << std::endl; + } + + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.Direction", + &observedDirection)) + { + std::cout << "ResourceChangeNotificationCallback(): failed get Direction." << std::endl; + } + + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.TargetFloor", + &observedTargetFloor)) + { + std::cout << "ResourceChangeNotificationCallback(): failed get TargetFloor." << std::endl; + } + + std::cout << "Resource Change Notification: " << std::endl; + std::cout << " CurrentFloor: " << observedCurrentFloor; + std::cout << " Direction: " << observedDirection; + std::cout << " TargetFloor: " << observedTargetFloor << std::endl; +} + +// IPCAGetProperties() completion callback. +std::mutex g_getPropertiesCbMutex; +std::condition_variable g_getPropertiesCompleteCV; + +void GetPropertiesCallback(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + UNREFERENCED_PARAMETER(context); + + int currentFloor = 0, direction = 0, targetFloor = 0; + + if (result != IPCA_OK) + { + std::cout << "!!! GetProperties callback !!! error: " << result << std::endl; + RediscoverElevator(); + g_getPropertiesCompleteCV.notify_all(); + return; + } + + if (propertyBagHandle != nullptr) + { + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.CurrentFloor", + ¤tFloor)) + { + std::cout << "GetPropertiesCallback(): failed get CurrentFloor." << std::endl; + } + + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.Direction", + &direction)) + { + std::cout << "GetPropertiesCallback(): failed get Direction." << std::endl; + } + + if (IPCA_OK != IPCAPropertyBagGetValueInt(propertyBagHandle, + "x.org.iotivity.TargetFloor", + &targetFloor)) + { + std::cout << "GetPropertiesCallback(): failed get TargetFloor." << std::endl; + } + } + + std::cout << "*** GetProperties callback: successful ***" << std::endl; + std::cout << "Current Floor : " << currentFloor << std::endl; + std::cout << "Direction : " << direction << std::endl; + std::cout << "TargetFloor : " << targetFloor << std::endl; + + g_getPropertiesCompleteCV.notify_all(); +} + +// IPCASetProperties() completion callback. +std::mutex g_setPropertiesCbMutex; +std::condition_variable g_setPropertiesCompleteCV; +void SetPropertiesCallback(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(propertyBagHandle); + + if (result == IPCA_OK) + { + std::cout << "*** SetProperties callback: successful ***" << std::endl; + } + else + { + std::cout << "!!! SetProperties callback !!! error: " << result << std::endl; + RediscoverElevator(); + } + + g_setPropertiesCompleteCV.notify_all(); +} + +// Callback when device is discovered. +std::mutex g_deviceDiscoveredCbMutex; +std::condition_variable g_deviceDiscoveredCV; +void DiscoverDevicesCallback(void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* deviceInfo) +{ + UNREFERENCED_PARAMETER(context); + + std::lock_guard lock(g_globalMutex); + + std::string deviceId = deviceInfo->deviceId; + std::string deviceName = deviceInfo->deviceName; + std::vector deviceUris; + for (int i = 0; i < deviceInfo->deviceUriCount; i++) + { + deviceUris.push_back(deviceInfo->deviceUris[i]); + } + + if (g_OCFDeviceList.find(deviceId) == g_OCFDeviceList.end()) + { + OCFDevice::Ptr ocfDevice = std::shared_ptr(new + OCFDevice(g_ipcaAppHandle, deviceId)); + if (ocfDevice == nullptr) + { + std::cout << "Out of memory" << std::endl; + return; + } + g_OCFDeviceList[deviceId] = ocfDevice; + } + + OCFDevice::Ptr ocfDevice = g_OCFDeviceList[deviceId]; + + // Wake up any thread waiting for discovery of this device. + if ((!g_targetElevatorDiscovered) && + (g_targetDeviceId.length() != 0) && + (g_targetDeviceId.compare(deviceId) == 0)) + { + g_targetElevatorDiscovered = true; + g_deviceDiscoveredCV.notify_all(); + } + + if (g_isList) + { + if (deviceStatus == IPCA_DEVICE_DISCOVERED) + { + std::cout << "*** New elevator: *** " << std::endl << std::endl; + } + else + if (deviceStatus == IPCA_DEVICE_UPDATED_INFO) + { + std::cout << "+++ Elevator info updated for id: [" << deviceId << "] +++"; + std::cout << std::endl << std::endl; + } + else + { + std::cout << "--- Elevator is no longer discoverable. Device ID: ["; + std::cout << deviceId << "] ---" << std::endl << std::endl; + g_OCFDeviceList.erase(deviceId); + return; + } + + IPCADeviceInfo* di = ocfDevice->GetDeviceInfo(); + + if (di != nullptr) + { + std::cout << "Device Info: " << std::endl; + std::cout << std::endl; + if (deviceUris.size() != 0) + { + int i = 0; + for (auto& uri : deviceUris) + { + if (i++ == 0) + { + + std::cout << " Device URI . . . . . . . : " << uri << std::endl; + } + else + { + std::cout << " " << uri << std::endl; + } + } + } + + std::cout << " Device ID . . . . . . . : " << (di->deviceId ? di->deviceId : ""); + std::cout << std::endl; + + std::cout << " Device Name . . . . . . : "; + std::cout << (di ->deviceName ? di->deviceName : "") << std::endl; + + std::cout << " Device Software Version : "; + std::cout << (di->deviceSoftwareVersion ? di->deviceSoftwareVersion : "") << std::endl; + std::cout << std::endl; + + } + else + { + std::cout << "Device Info: Not available." << std::endl << std::endl; + } + + IPCAPlatformInfo* pi = ocfDevice->GetPlatformInfo(); + + if (pi != nullptr) + { + std::cout << "Platform Info:" << std::endl; + std::cout << std::endl; + + std::cout << " Plaform ID . . . . . . . : "; + std::cout << (pi->platformId ? pi->platformId : "") << std::endl; + + std::cout << " Manufacturer Name . . . : "; + std::cout << (pi->manufacturerName ? pi->manufacturerName : "") << std::endl; + + std::cout << " Manufacturer URL . . . . : "; + std::cout << (pi->manufacturerURL ? pi->manufacturerURL : "") << std::endl; + + std::cout << " Model Number . . . . . . : "; + std::cout << (pi->modelNumber ? pi->modelNumber : "") << std::endl; + + std::cout << " Manufacturing Date . . . : "; + std::cout << (pi->manufacturingDate ? pi->manufacturingDate : "") << std::endl; + + std::cout << " Platform Version . . . . : "; + std::cout << (pi->platformVersion ? pi->platformVersion : "") << std::endl; + + std::cout << " OS Version . . . . . . . : "; + std::cout << (pi->osVersion ? pi->osVersion : "") << std::endl; + + std::cout << " Hardware Version . . . . : "; + std::cout << (pi->hardwareVersion ? pi->hardwareVersion : "") << std::endl; + + std::cout << " Firmware Version . . . . : "; + std::cout << (pi->firmwareVersion ? pi->firmwareVersion : "") << std::endl; + + std::cout << " Manufacturer Support URL : "; + std::cout << (pi->manufacturerSupportURL ? pi->manufacturerSupportURL : ""); + std::cout << std::endl; + + std::cout << " Reference Time . . . . . : "; + std::cout << (pi->referenceTime ? pi->referenceTime : "") << std::endl; + std::cout << std::endl; + } + else + { + std::cout << "Platform info: Not available. " << std::endl << std::endl; + } + + std::cout << "Resource List:" << std::endl; + ResourceList resourceList = ocfDevice->GetResourceInfo(); + for (auto const& resource : resourceList) + { + bool firstEntry = true; + std::cout << " Resource URI . . . . . : " << resource.first << std::endl; + std::cout << " Resource Types . . . : "; + for (auto const& resourceType : resource.second) + { + if (firstEntry == true) + { + std::cout << resourceType << std::endl; + firstEntry = false; + } + else + { + std::cout << " " << resourceType << std::endl; + } + } + std::cout << std::endl; + } + std::cout << std::endl; + std::cout << "[Press enter to exit discovery]"<< std::endl << std::endl; + } +} + +void ShowUsage() +{ + std::cout << std::endl; + std::cout << "USAGE: elevatorclient [-l] [-id elevator_id] [-s target_floor] [-g] [-o]"; + std::cout << std::endl; + std::cout << std::endl; + std::cout << "Options" << std::endl; + std::cout << " -l Discover and list elevators." << std::endl; + std::cout << " -id Device ID of elevator, must be set for -s, -g, and -o arguments."; + std::cout << std::endl; + std::cout << " -auth Authenticate to device." << std::endl; + std::cout << " -s target_floor Set target floor." << std::endl; + std::cout << " -g Get current floor." << std::endl; + std::cout << " -o Observe." << std::endl; + std::cout << " -cont Run continuous -g or -s with increasing target floor"; + std::cout << std::endl; +} + +bool ParseArgv(int argc, char* argv[]) +{ + const std::string ListArgument = "-l"; + const std::string IdArgument = "-id"; + const std::string SetArgument = "-s"; + const std::string GetArgument = "-g"; + const std::string ObserveArgument = "-o"; + const std::string ContinuousArgument = "-cont"; + const std::string AuthenticateToDeviceArgument = "-auth"; + + for (int i = 1 ; i < argc; i++) + { + if (ListArgument.compare(argv[i]) == 0) + { + g_isList = true; + continue; + } + + if (GetArgument.compare(argv[i]) == 0) + { + g_isGet = true; + continue; + } + + if (ObserveArgument.compare(argv[i]) == 0) + { + g_isObserve = true; + continue; + } + + if (ContinuousArgument.compare(argv[i]) == 0) + { + g_isContinuous = true; + continue; + } + + if (IdArgument.compare(argv[i]) == 0) + { + if (++i == argc) + { + return false; + } + + g_targetDeviceId = argv[i]; + g_isId = true; + continue; + } + + if (SetArgument.compare(argv[i]) == 0) + { + if (++i == argc) + { + return false; + } + + g_targetFloor = std::stoi(argv[i]); + g_isSet = true; + continue; + } + + if (AuthenticateToDeviceArgument.compare(argv[i]) == 0) + { + g_isAuth = true; + continue; + } + } + + return true; +} + +void CloseDeviceTargetElevator() +{ + if (g_deviceHandle != nullptr) + { + IPCACloseDevice(g_deviceHandle); + g_deviceHandle = nullptr; + } +} + +bool OpenDeviceTargetElevator() +{ + if (g_deviceHandle != nullptr) + { + CloseDeviceTargetElevator(); + } + + IPCAStatus status = IPCAOpenDevice(g_ipcaAppHandle, g_targetDeviceId.c_str(), &g_deviceHandle); + if (status != IPCA_OK) + { + return false; + } + + return true; +} + +IPCAHandle g_discoverDeviceHandle; + +// Synchronous call to discovery device. +// If not free run, Function waits for timeOut milliseconds and stops when function exits. +bool DiscoverElevator(bool freeRun, size_t timeOutMs) +{ + std::unique_lock lock { g_deviceDiscoveredCbMutex }; + + char* resourceTypes[] = { + "x.org.iotivity.sample.elevator", + "x.org.iotivity.sample.elevator2", + "x.org.iotivity.sample.elevator3", + "x.org.iotivity.sample.elevator4", + }; + + const int NUMBER_OF_RESOURCE_TYPES = sizeof(resourceTypes) / sizeof(char*); + + IPCAStatus status = IPCADiscoverDevices( + g_ipcaAppHandle, + &DiscoverDevicesCallback, + nullptr, + resourceTypes, + NUMBER_OF_RESOURCE_TYPES, + &g_discoverDeviceHandle); + + if (status != IPCA_OK) + { + return false; + } + + if (!freeRun) + { + g_deviceDiscoveredCV.wait_for(lock, std::chrono::milliseconds{ timeOutMs }); + + // Stop discovery. + IPCACloseHandle(g_discoverDeviceHandle); + } + + return g_targetElevatorDiscovered; +} + +bool RediscoverElevator() +{ + g_targetElevatorDiscovered = false; + + CloseDeviceTargetElevator(); + + std::cout << "Rediscovering elevator..." << std::endl; + + if (false == DiscoverElevator(false, INT_MAX) || false == OpenDeviceTargetElevator()) + { + std::cout << "Failed to rediscover elevator! " << std::endl; + g_rediscoverFailedQuitNow = true; + return false; + } + + std::cout << "Elevator rediscovered." << std::endl; + return true; +} + +IPCAStatus PwdInputCallback( + void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + char* passwordBuffer, + size_t passwordBufferSize) +{ + UNREFERENCED_PARAMETER(passwordBufferSize); + UNREFERENCED_PARAMETER(type); + UNREFERENCED_PARAMETER(platformInformation); + UNREFERENCED_PARAMETER(deviceInformation); + UNREFERENCED_PARAMETER(context); + + std::cout << "Received Password Input Callback" << std::endl; + + std::cout << "Input password: "; + + for (int ret = 0; 1 != ret; ) + { + ret = scanf("%32s", passwordBuffer); + for (; 0x20 <= getchar(); ); // for removing overflow garbages + // '0x20<=code' is character region + } + + std::cout << std::endl; + + return IPCA_OK; +} + +IPCAStatus PwdDisplayCallback( + void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + const char* password) +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(deviceInformation); + UNREFERENCED_PARAMETER(platformInformation); + UNREFERENCED_PARAMETER(type); + + + std::cout << "==========================================" << std::endl; + + std::cout << "Password for device: " << password << std::endl; + + std::cout << "==========================================" << std::endl; + + return IPCA_OK; +} + +void AuthCompletionCallback(IPCAStatus completionStatus, void* context) +{ + UNREFERENCED_PARAMETER(context); + std::cout << "AuthCompletionCallback(). Completion status is: " << completionStatus; + std::cout << std::endl; +} + +int main(int argc, char* argv[]) +{ + if ((argc == 1) || (ParseArgv(argc, argv) == false)) + { + ShowUsage(); + return 0; + } + + IPCAUuid appId = {0xb6, 0x12, 0x38, 0x0c, 0x8c, 0x4c, 0x11, 0xe6, + 0xae, 0x22, 0x56, 0xb6, 0xb6, 0x49, 0x96, 0x11}; + + IPCAAppInfo ipcaAppInfo = { appId, "ElevatorClient", "1.0.0", "Microsoft" }; + + IPCAStatus status = IPCAOpen(&ipcaAppInfo, IPCA_VERSION_1, &g_ipcaAppHandle); + if (status != IPCA_OK) + { + std::cout << "Failed IPCAOpen(). Status: " << status << std::endl; + goto exit; + } + + status = IPCASetPasswordCallbacks(g_ipcaAppHandle, + &PwdInputCallback, + &PwdDisplayCallback, + nullptr); + + if (status != IPCA_OK) + { + std::cout << "Failed IPCASetPasswordCallbacks(). Status: " << status << std::endl; + goto exit; + } + + // List discovered elevators. + if (g_isList) + { + DiscoverElevator(true, 0); + std::cin.get(); + goto exit; + } + + // Other than "-l" option, device ID is required. + if (!g_isId) + { + std::cout << "Error, device ID is required." << std::endl; + ShowUsage(); + goto exit; + } + + // Wait for discovery of target elevator. + if (false == DiscoverElevator(false, 3000)) + { + std::cout << "Target elevator was not discovered." << std::endl; + goto exit; + } + + // Open target elevator. + if (false == OpenDeviceTargetElevator()) + { + std:: cout << "Error opening target elevator" << std::endl; + goto exit; + } + + // Get target elevator data. + if (g_isGet) + { + std::unique_lock lock { g_getPropertiesCbMutex }; + int loopCount = (g_isContinuous == true) ? INT_MAX : 1; + + for (int i = 0; i < loopCount; i++) + { + std::cout << "Calling IPCAGetProperties().. (" << i << ")" << std::endl; + IPCAStatus getStatus = IPCAGetProperties(g_deviceHandle, + &GetPropertiesCallback, + 0, + "/ipca/sample/elevator", + nullptr, + nullptr, + nullptr); + if (IPCA_OK == getStatus) + { + g_getPropertiesCompleteCV.wait_for(lock, std::chrono::milliseconds{ INT_MAX }); + } + if (g_rediscoverFailedQuitNow) + { + break; + } + } + goto exit; + } + + // Set target elevator's TargetFloor. + if (g_isSet) + { + std::unique_lock lock { g_setPropertiesCbMutex }; + int loopCount = g_isContinuous == true ? INT_MAX : 1; + int targetFloor = g_targetFloor; + + for (int i = 0; i < loopCount; i++) + { + std::cout << "Calling IPCASetProperties() to target floor: "; + std::cout << targetFloor << " (" << i << ")" << std::endl; + + IPCAPropertyBagHandle propertyBagHandle; + status = IPCAPropertyBagCreate(&propertyBagHandle); + if (IPCA_OK == status) + { + status = IPCAPropertyBagSetValueInt(propertyBagHandle, + "x.org.iotivity.TargetFloor", + targetFloor++); + + if (IPCA_OK == status) + { + status = IPCASetProperties(g_deviceHandle, + &SetPropertiesCallback, + 0, + "/ipca/sample/elevator", + "x.org.iotivity.sample.elevator", + nullptr, + propertyBagHandle, + nullptr); + + if (IPCA_OK == status) + { + g_setPropertiesCompleteCV.wait_for( + lock, std::chrono::milliseconds{ INT_MAX }); + } + } + IPCAPropertyBagDestroy(propertyBagHandle); + } + + if (g_rediscoverFailedQuitNow) + { + break; + } + } + goto exit; + } + + // Register for observation. + if (g_isObserve) + { + RequestObserve(); + std::cout << "any key and enter to exit.." << std::endl; + std::cin.get(); + goto exit; + } + + // Authenticate to a device (i.e. MOT). + if (g_isAuth) + { + IPCARequestAccess(g_deviceHandle, nullptr, AuthCompletionCallback, nullptr, nullptr); + std::cout << "Authenticating to device. Use ctrl-c to quit when authentication is complete."; + std::cout << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1000)); + } + +exit: + CloseDeviceTargetElevator(); + g_OCFDeviceList.clear(); + + if (g_ipcaAppHandle != nullptr) + { + IPCAClose(g_ipcaAppHandle); + } + + return 0; +} diff --git a/resource/IPCA/samples/ElevatorClient/OCFDevice.cpp b/resource/IPCA/samples/ElevatorClient/OCFDevice.cpp new file mode 100644 index 0000000..3f30edc --- /dev/null +++ b/resource/IPCA/samples/ElevatorClient/OCFDevice.cpp @@ -0,0 +1,157 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include +#include +#include "ipca.h" +#include "OCFDevice.h" + +OCFDevice::OCFDevice(IPCAAppHandle appHandle, std::string id) : + m_ipcaAppHandle(appHandle), + m_deviceId(id), + m_deviceHandle(nullptr), + m_deviceInfo(nullptr), + m_platformInfo(nullptr) +{ +} + +OCFDevice::~OCFDevice() +{ + if (m_deviceHandle != nullptr) + { + IPCACloseDevice(m_deviceHandle); + } + + if (m_deviceInfo != nullptr) + { + IPCAFreeDeviceInfo(m_deviceInfo); + } + + if (m_platformInfo != nullptr) + { + IPCAFreePlatformInfo(m_platformInfo); + } +} + +IPCAStatus OCFDevice::OpenDevice() +{ + if (m_deviceHandle != nullptr) + { + return IPCA_OK; + } + else + { + return IPCAOpenDevice(m_ipcaAppHandle, m_deviceId.c_str(), &m_deviceHandle); + } +} + +IPCADeviceInfo* OCFDevice::GetDeviceInfo() +{ + IPCAStatus status; + + if (m_deviceInfo != nullptr) + { + return m_deviceInfo; + } + + if (OpenDevice() != IPCA_OK) + { + return nullptr; + } + + status = IPCAGetDeviceInfo(m_deviceHandle, &m_deviceInfo); + + if (IPCA_OK != status) + { + return nullptr; + } + + return m_deviceInfo; +} + +IPCAPlatformInfo* OCFDevice::GetPlatformInfo() +{ + IPCAStatus status; + + if (m_platformInfo != nullptr) + { + return m_platformInfo; + } + + if (OpenDevice() != IPCA_OK) + { + return nullptr; + } + + status = IPCAGetPlatformInfo(m_deviceHandle, &m_platformInfo); + + if (IPCA_OK != status) + { + return nullptr; + } + + return m_platformInfo; +} + +ResourceList OCFDevice::GetResourceInfo() +{ + IPCAStatus status; + + ResourceList emptyResourceList; + if (OpenDevice() != IPCA_OK) + { + return emptyResourceList; + } + + char** resourcePathList; + size_t resourceListCount; + status = IPCAGetResources(m_deviceHandle, + nullptr, nullptr, &resourcePathList, &resourceListCount); + if (IPCA_OK != status) + { + return emptyResourceList; + } + + m_resourceList.clear(); + size_t i; + for (i = 0 ; i < resourceListCount ; i++) + { + char** resourceTypes; + size_t resourceTypeCount; + status = IPCAGetResourceTypes(m_deviceHandle, + resourcePathList[i], &resourceTypes, &resourceTypeCount); + if (IPCA_OK == status) + { + int j; + for (j = 0 ; j < resourceTypeCount; j++) + { + m_resourceList[resourcePathList[i]].push_back(resourceTypes[j]); + } + IPCAFreeStringArray(resourceTypes, resourceTypeCount); + } + else + { + std::cout << "Failed IPCAGetResourceTypes() for resource: " << resourcePathList[i]; + std::cout << std::endl; + } + } + + IPCAFreeStringArray(resourcePathList, resourceListCount); + return m_resourceList; +} diff --git a/resource/IPCA/samples/ElevatorClient/OCFDevice.h b/resource/IPCA/samples/ElevatorClient/OCFDevice.h new file mode 100644 index 0000000..1fab50d --- /dev/null +++ b/resource/IPCA/samples/ElevatorClient/OCFDevice.h @@ -0,0 +1,60 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipca.h" + +// Key of map is resource path. Value is array of resource types. +typedef std::map> ResourceList; + +/** + * Each OCFDevice object represents a device discovered by IPCA. + */ +class OCFDevice +{ +public: + typedef std::shared_ptr Ptr; + OCFDevice(IPCAAppHandle appHandle, std::string id); + ~OCFDevice(); + IPCADeviceInfo* GetDeviceInfo(); + IPCAPlatformInfo* GetPlatformInfo(); + ResourceList GetResourceInfo(); + +private: + IPCAStatus OpenDevice(); + +private: + IPCAAppHandle m_ipcaAppHandle; + std::string m_deviceId; + std::string m_hostAddress; + IPCADeviceHandle m_deviceHandle; // from IPCAOpenDevice(); + IPCADeviceInfo* m_deviceInfo; // valid between IPCAOpenDevice() and IPCACloseDevice(). + IPCAPlatformInfo* m_platformInfo; // valid between IPCAOpenDevice() and IPCACloseDevice(). + ResourceList m_resourceList; +}; diff --git a/resource/IPCA/samples/ElevatorClient/SConscript b/resource/IPCA/samples/ElevatorClient/SConscript new file mode 100644 index 0000000..a16c8d3 --- /dev/null +++ b/resource/IPCA/samples/ElevatorClient/SConscript @@ -0,0 +1,44 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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. +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +## +# ElevatorClient (application) build script +## +Import('ipca_env') +target_os = ipca_env.get('TARGET_OS') +elevator_client_env = ipca_env.Clone() + +###################################################################### +# Build flags +###################################################################### +elevator_client_env.PrependUnique(CPPPATH = [ + '..\..\inc', + ]) + +elevator_client_env.PrependUnique(LIBS = ['ipca']) + +elevator_client_src = [ + 'ElevatorClient.cpp', + 'OCFDevice.cpp' + ] +###################################################################### +# Source files and Targets +###################################################################### +ipcapp = elevator_client_env.Program('elevatorclient', elevator_client_src) \ No newline at end of file diff --git a/resource/IPCA/samples/ElevatorServer/ElevatorServerSample.cpp b/resource/IPCA/samples/ElevatorServer/ElevatorServerSample.cpp new file mode 100644 index 0000000..3adf197 --- /dev/null +++ b/resource/IPCA/samples/ElevatorServer/ElevatorServerSample.cpp @@ -0,0 +1,35 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "ipca.h" +#include "elevatorserver.h" + +ElevatorServer g_testElevator; + +int main() +{ + std::string elevatorName = "Standalone Test Elevator"; + g_testElevator.Start(elevatorName); + + std::cout << "enter any key and press enter to exit" << std::endl; + int userInput; + std::cin >> userInput; + + g_testElevator.Stop(); +} diff --git a/resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.dat b/resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.dat new file mode 100644 index 0000000000000000000000000000000000000000..66b344378eae1b8252aaa309b4ca542f05540d57 GIT binary patch literal 973 zcmbVKOHu+c5FIGWJGghHHDO8t;U>7urz06Mlc^*FxCGAO!jg@r-~=AU9a{^NfJjYQ zGMGg=ov!!tUU$|MQ~A)mS#cMIu`di$FpJl*DM<>{mZf0$T4*q;)UL8zv5^KoYAZq8 z?v`jfPu&?!$H=FG&xKC~M^aJ^65c=E+M#$rF^?JTG0Y(rYL6h;fD}1|qGQm%6BNe; zMNNXV0G$bAgvy(z++{(h0orD2mK`i=6jEogD!8gMKM)kgV(S^k-lImlyavUO21g!R z7TT;)NdF`_vU-tfHO)c$G0v_a>Gkm)^x{DRJnrDzUX1U%JRXo_&}W@H-tB~!!UQ#P zKf=$mY8HZr;qx04Mim=EvnH`wL{!!yCTpb(CW1o!7yl*kR6Cg;)$n8OgIPw6%WcH{ y5oAOSwGHrXVVkqXO6TsVqse~7K32Bx+S&f}3@QDHgtB5H>=R-I-PHJm+wB+MO?9XM literal 0 HcmV?d00001 diff --git a/resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.json b/resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.json new file mode 100644 index 0000000..86143a7 --- /dev/null +++ b/resource/IPCA/samples/ElevatorServer/ElevatorServerSecurityDB.json @@ -0,0 +1,73 @@ +{ + "acl": { + "aclist": { + "aces": [ + { + "subjectuuid": "*", + "resources": [ + { + "href": "/oic/res", + "rel": "", + "rt": ["oic.wk.res"], + "if": ["oic.if.ll"] + },{ + "href": "/oic/d", + "rel": "", + "rt": ["oic.wk.d"], + "if": ["oic.if.baseline", "oic.if.r"] + },{ + "href": "/oic/p", + "rel": "", + "rt": ["oic.wk.p"], + "if": ["oic.if.baseline", "oic.if.r"] + } + ], + "permission": 2 + }, + { + "subjectuuid": "*", + "resources": [ + { + "href": "/oic/sec/doxm", + "rel": "", + "rt": ["oic.r.doxm"], + "if": ["oic.if.baseline"] + }, + { + "href": "/oic/sec/pstat", + "rel": "", + "rt": ["oic.r.pstat"], + "if": ["oic.if.baseline"] + }, + { + "href": "/oic/sec/cred", + "rel": "", + "rt": ["oic.r.cred"], + "if": ["oic.if.baseline"] + } + ], + "permission": 6 + } + ] + }, + "rowneruuid" : "E068A3E6-90EF-4A26-A75F-9B098D43F54D" + }, + "pstat": { + "isop": false, + "deviceuuid": "E068A3E6-90EF-4A26-A75F-9B098D43F54D", + "rowneruuid": "E068A3E6-90EF-4A26-A75F-9B098D43F54D", + "cm": 2, + "tm": 0, + "om": 4, + "sm": 4 + }, + "doxm": { + "oxms": [0,1,65280], + "oxmsel": 0, + "sct": 1, + "owned": false, + "deviceuuid": "E068A3E6-90EF-4A26-A75F-9B098D43F54D", + "devowneruuid": "", + "rowneruuid": "E068A3E6-90EF-4A26-A75F-9B098D43F54D" + } +} diff --git a/resource/IPCA/samples/ElevatorServer/SConscript b/resource/IPCA/samples/ElevatorServer/SConscript new file mode 100644 index 0000000..f1167b5 --- /dev/null +++ b/resource/IPCA/samples/ElevatorServer/SConscript @@ -0,0 +1,89 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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 os +import os.path + +## +# ElevatorServer build script +## +Import('ipca_env') +target_os = ipca_env.get('TARGET_OS') +elevator_server_env = ipca_env.Clone() +rd_mode = elevator_server_env.get('RD_MODE') + +###################################################################### +# Build flags +###################################################################### +elevator_server_env.PrependUnique(CPPPATH = [ + '#/resource/include', + '#/resource/oc_logger/include', + '#/resource/csdk/include', + '#/resource/csdk/stack/include', + '#/resource/csdk/security/include', + '#/resource/csdk/security/provisioning/include', + '#/resource/csdk/security/provisioning/include/internal', + '#/resource/csdk/connectivity/api', + '#/resource/csdk/connectivity/external/inc', + '#/resource/csdk/ocsocket/include', + '#/resource/c_common/ocrandom/include', + '#/resource/csdk/logger/include', + '#/extlibs/boost/boost', + '../../inc' + ]) + +elevator_server_env.AppendUnique(LIBPATH = [ipca_env.get('BUILD_DIR')]) +elevator_server_env.PrependUnique(LIBS = [ + 'oc', + 'connectivity_abstraction', + 'coap', + 'octbstack', + 'oc_logger', + ]) + +if elevator_server_env.get('SECURED') == '1': + elevator_server_env.AppendUnique(CPPDEFINES = ['SECURED']) + elevator_server_env.AppendUnique(LIBS = ['mbedtls', 'mbedx509','mbedcrypto', 'ocprovision']) + +if target_os in ['msys_nt', 'windows']: + elevator_server_env.AppendUnique(LINKFLAGS = ['/subsystem:CONSOLE']) + +if 'CLIENT' in rd_mode or 'SERVER' in rd_mode: + elevator_server_env.PrependUnique(LIBS = ['resource_directory']) + +###################################################################### +# Source files and Targets +###################################################################### +elevator_server_src = [ + 'ElevatorServerSample.cpp', + 'elevatorserver.cpp' + ] + +elevator_server_src_dir = os.path.join(elevator_server_env.get('SRC_DIR'), + 'resource', 'ipca', 'samples', 'ElevatorServer') + os.sep +elevator_server_build_dir = os.path.join(elevator_server_env.get('BUILD_DIR'), + 'resource', 'ipca', 'samples', 'ElevatorServer') + os.sep + +elevator_server_env.Install(elevator_server_build_dir, + elevator_server_src_dir + 'ElevatorServerSecurityDB.json') +elevator_server_env.Install(elevator_server_build_dir, + elevator_server_src_dir + 'ElevatorServerSecurityDB.dat') + +elevator_app = elevator_server_env.Program('ElevatorServer', elevator_server_src) \ No newline at end of file diff --git a/resource/IPCA/samples/ElevatorServer/elevatorserver.cpp b/resource/IPCA/samples/ElevatorServer/elevatorserver.cpp new file mode 100644 index 0000000..65680f9 --- /dev/null +++ b/resource/IPCA/samples/ElevatorServer/elevatorserver.cpp @@ -0,0 +1,484 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "logger.h" +#include "ElevatorServer.h" + +using namespace OC; +using namespace std::placeholders; + +#define TAG "ElevatorServer.cpp" + +// Secure Virtual Resource database for Iotivity Server +// It contains Server's Identity and the PSK credentials +// of other devices which the server trusts +static char CredFile[] = "ElevatorServerSecurityDB.dat"; + +FILE* server_fopen(const char *path, const char *mode) +{ + (void)path; + return fopen(CredFile, mode); +} + +// +// class ElevatorServer implementation. +// +ElevatorServer::ElevatorServer() : + engineThread(), + m_elevatorResourceHandle(nullptr), + m_elevatorResourceHandle2(nullptr), + m_elevatorResourceHandle3(nullptr), + m_elevatorResourceHandle4(nullptr), + m_targetFloor(1), + m_currentFloor(1), + m_direction(ElevatorDirection::Stopped) +{ +#ifdef SECURED + m_displayPasswordCallbackHandle = nullptr; +#endif +} + +ElevatorServer::~ElevatorServer() +{ + Stop(); +} + +// Return all properties in response. +OCStackResult ElevatorServer::SendResponse(std::shared_ptr request) +{ + // Values to return. + OCRepresentation responseRep; + responseRep["x.org.iotivity.CurrentFloor"] = GetCurrentFloor(); + responseRep["x.org.iotivity.TargetFloor"] = GetTargetFloor(); + responseRep["x.org.iotivity.Direction"] = (int)GetElevatorDirection(); + + // Prepare the response. + auto pResponse = std::make_shared(); + pResponse->setRequestHandle(request->getRequestHandle()); + pResponse->setResourceHandle(request->getResourceHandle()); + pResponse->setResourceRepresentation(responseRep); + pResponse->setResponseResult(OC_EH_OK); + + // Send the response. + return OCPlatform::sendResponse(pResponse); +} + +// Callback handler for elevator resource. +OCEntityHandlerResult ElevatorServer::ElevatorEntityHandler( + std::shared_ptr request) +{ + OCEntityHandlerResult ehResult = OC_EH_ERROR; + + if(request != nullptr) + { + // Get the request type and request flag + std::string requestType = request->getRequestType(); + int requestFlag = request->getRequestHandlerFlag(); + + if(requestFlag & RequestHandlerFlag::RequestFlag) + { + // If the request type is GET + if(requestType == "GET") + { + if (SendResponse(request) == OC_STACK_OK) + { + ehResult = OC_EH_OK; + } + } + else if(requestType == "POST") + { + OCRepresentation requestRep = request->getResourceRepresentation(); + + // Target floor can be set. + int targetFloor; + if (requestRep.getValue("x.org.iotivity.TargetFloor", targetFloor)) + { + SetTargetFloor(static_cast(targetFloor)); + } + + if(OC_STACK_OK == SendResponse(request)) + { + ehResult = OC_EH_OK; + } + } + else if(requestType == "PUT") + { + // not supported. + } + else if(requestType == "DELETE") + { + // not supported. + } + } + + if(requestFlag & RequestHandlerFlag::ObserverFlag) + { + // Hold the lock to make sure no iterator is in progress. + std::lock_guard lock(m_elevatorMutex); + + ObservationInfo observationInfo = request->getObservationInfo(); + if(ObserveAction::ObserveRegister == observationInfo.action) + { + OIC_LOG_V(INFO, TAG, "ElevatorEntityHandler(): new observer ID: %d", + observationInfo.obsId); + m_observers.push_back(observationInfo.obsId); + } + else if(ObserveAction::ObserveUnregister == observationInfo.action) + { + OIC_LOG_V(INFO, TAG, "ElevatorEntityHandler(): removing observer ID: %d", + observationInfo.obsId); + m_observers.erase(std::remove( + m_observers.begin(), + m_observers.end(), + observationInfo.obsId), + m_observers.end()); + } + + ehResult = OC_EH_OK; + } + } + + return ehResult; +} + +// Copy from std::string to char array. Return true if source is truncated at dest. +bool CopyStringToBuffer(const std::string& source, char* dest, size_t destSize) +{ + bool isTruncated = false; + size_t copied = source.copy(dest, destSize, 0); + if (copied == destSize) + { + copied -= 1; // make room for null + isTruncated = true; + } + + // std::string copy does not include null. + dest[copied] = 0x00; + return isTruncated; +} + +// Initialize Persistent Storage for security database +OCPersistentStorage ps = {server_fopen, fread, fwrite, fclose, unlink}; + +bool ElevatorServer::Start(const std::string& elevatorName) +{ + std::lock_guard lock(m_elevatorMutex); + + // OCPlatform needs only 1 time initialization. + static bool OCFInitialized = false; + if (false == OCFInitialized) + { + PlatformConfig Configuration { + ServiceType::InProc, + ModeType::Server, + "0.0.0.0", // By setting to "0.0.0.0", it binds to all available + // interfaces + 0, // Uses randomly available port + QualityOfService::NaQos, + &ps + }; + + OCPlatform::Configure(Configuration); + OCFInitialized = true; + } + + if (false == m_isRunning) + { + std::string defaultResourceTypeName("x.org.iotivity.sample.elevator"); + std::string defaultResourceName("/ipca/sample/elevator"); + + m_name = elevatorName; + + // Start with known state. + m_targetFloor = m_currentFloor = 1; + m_direction = ElevatorDirection::Stopped; + + // Start the engine thread. + m_isRunning = true; + engineThread = std::thread(&ElevatorServer::Engine, this); + + // Device Info. + char devName[256]; + char resTypeName[256]; + CopyStringToBuffer(m_name, devName, 256); + CopyStringToBuffer(defaultResourceTypeName, resTypeName, 256); + OCStringLL types { nullptr, resTypeName }; + OCDeviceInfo deviceInfo = { devName, &types, "0.0.1", nullptr }; + + // Platform Info + char platformId[] = "6cb6c994-8c4b-11e6-ae22-56b6b6499611"; + char manufacturerName[] = "E Manufacturer"; + char manufacturerUrl[] = "http://www.example.com/elevator"; + char modelNumber[] = "Elevator Model Number"; + char dateManufacture[] = "2017-02-28"; + char platformVersion[] = "Elevator Platform Version"; + char osVersion[] = "Elevator OS Version"; + char hardwareVersion[] = "Elevator HW Version"; + char firmwareVersion[] = "Elevator FW Version"; + char supportURL[] = "http://www.example.com/elevator/support"; + + OCPlatformInfo platformInfo = { + platformId, + manufacturerName, + manufacturerUrl, + modelNumber, + dateManufacture, + platformVersion, + osVersion, + hardwareVersion, + firmwareVersion, + supportURL, + nullptr}; + + // Register elevator's platformInfo, deviceInfo, and resource. + if (OC_STACK_OK != OCPlatform::registerPlatformInfo(platformInfo)) + { + return false; + } + + if (OC_STACK_OK != OCPlatform::registerDeviceInfo(deviceInfo)) + { + return false; + } + +#ifdef SECURED + if (OC_STACK_OK != OCSecure::registerDisplayPinCallback( + std::bind(&ElevatorServer::PinDisplayCallback, this, _1, _2), + &m_displayPasswordCallbackHandle)) + { + return false; + } +#endif + OCStackResult result = OCPlatform::registerResource( + m_elevatorResourceHandle, + defaultResourceName, + defaultResourceTypeName, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + + if (result != OC_STACK_OK) + { + return false; + } + + // These extra resources are created but not implemented by the entity handler. + // They better reflect real devices supporting multiple resources. + + std::string defaultResourceTypeName2("x.org.iotivity.sample.elevator2"); + std::string defaultResourceName2("/ipca/sample/elevator2"); + result = OCPlatform::registerResource( + m_elevatorResourceHandle2, + defaultResourceName2, + defaultResourceTypeName2, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + + if (result != OC_STACK_OK) + { + return false; + } + + std::string defaultResourceTypeName3("x.org.iotivity.sample.elevator3"); + std::string defaultResourceName3("/ipca/sample/elevator3"); + result = OCPlatform::registerResource( + m_elevatorResourceHandle3, + defaultResourceName3, + defaultResourceTypeName3, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + + if (result != OC_STACK_OK) + { + return false; + } + + std::string defaultResourceTypeName4("x.org.iotivity.sample.elevator4"); + std::string defaultResourceName4("/ipca/sample/elevator4"); + result = OCPlatform::registerResource( + m_elevatorResourceHandle4, + defaultResourceName4, + defaultResourceTypeName4, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + + return (result == OC_STACK_OK); + } + + // It's already running. + return true; +} + +void ElevatorServer::Stop() +{ + std::lock_guard lock(m_elevatorMutex); + + if (true == m_isRunning) + { + // Unregister OCF resource. + OCPlatform::unregisterResource(m_elevatorResourceHandle); + OCPlatform::unregisterResource(m_elevatorResourceHandle2); + OCPlatform::unregisterResource(m_elevatorResourceHandle3); + OCPlatform::unregisterResource(m_elevatorResourceHandle4); + m_elevatorResourceHandle = nullptr; + m_elevatorResourceHandle2 = nullptr; + m_elevatorResourceHandle3 = nullptr; + m_elevatorResourceHandle4 = nullptr; + + // Signal the engineThread to stop and wait for it to exit. + m_isRunning = false; + if (engineThread.joinable()) + { + engineThread.join(); + } + +#ifdef SECURED + // Unregister the password display callback + OCSecure::deregisterDisplayPinCallback(m_displayPasswordCallbackHandle); + m_displayPasswordCallbackHandle = nullptr; +#endif + } +} + +void ElevatorServer::SetTargetFloor(int floor) +{ + m_targetFloor = floor; +} + +int ElevatorServer::GetTargetFloor() +{ + return m_targetFloor; +} + +int ElevatorServer::GetCurrentFloor() +{ + return m_currentFloor; +} + +ElevatorDirection ElevatorServer::GetElevatorDirection() +{ + return m_direction; +} + +void ElevatorServer::Engine(ElevatorServer* elevator) +{ + const size_t EngineSleepTimeSeconds = 2; + std::chrono::seconds engineSleepTime(EngineSleepTimeSeconds); + + while (elevator->m_isRunning == true) + { + elevator->MoveElevator(); + std::this_thread::sleep_for(engineSleepTime); + } +} + +void ElevatorServer::NotifyObservers() +{ + // Local copy of observers so the code below doesn't need to hold the lock when + // calling out to notifyListOfObservers. + ObservationIds localCopyObservers; + + if (m_observers.size() == 0) + { + return; + } + else + { + std::lock_guard lock(m_elevatorMutex); + localCopyObservers = m_observers; + } + + OCRepresentation rep; + rep["x.org.iotivity.CurrentFloor"] = GetCurrentFloor(); + rep["x.org.iotivity.Direction"] = GetElevatorDirection(); + rep["x.org.iotivity.TargetFloor"] = GetTargetFloor(); + + // Prepare the response. + auto response = std::make_shared(); + response->setResourceRepresentation(rep, DEFAULT_INTERFACE); + + OCStackResult result = OCPlatform::notifyListOfObservers( + m_elevatorResourceHandle, + localCopyObservers, + response); + + if(OC_STACK_NO_OBSERVERS == result) + { + std::cout << "ElevatorServer:: failed notifyListOfObservers: result = "; + std::cout << result << std::endl; + } +} + +void ElevatorServer::MoveElevator() +{ + int dwTargetFloor = m_targetFloor; + int incrementValue; + + if (m_currentFloor == dwTargetFloor) + { + return; + } + + if (m_currentFloor < dwTargetFloor) + { + m_direction = ElevatorDirection::Up; + incrementValue = 1; + } + else + { + m_direction = ElevatorDirection::Down; + incrementValue = -1; + } + + std::cout << "ElevatorServer::MoveElevator() new Direction: " << m_direction << std::endl; + NotifyObservers(); + + const int DelayBetweenFloorMilliseconds = 10; + std::chrono::milliseconds delayBetweenFloor(DelayBetweenFloorMilliseconds); + + while (m_currentFloor != dwTargetFloor) + { + + m_currentFloor += incrementValue; + NotifyObservers(); + std::cout << "ElevatorServer::MoveElevator() new CurrentFloor: " << m_currentFloor; + std::cout << std::endl; + std::this_thread::sleep_for(delayBetweenFloor); + } + + m_direction = ElevatorDirection::Stopped; + NotifyObservers(); + std::cout << "ElevatorServer::MoveElevator() new Direction: " << m_direction << std::endl; +} + +void ElevatorServer::PinDisplayCallback(char* pinData, size_t pinLength) +{ + if ((nullptr == pinData) || (pinLength == 0)) + { + std::cout << "Invalid pin!" << std::endl; + return; + } + + std::cout << "============================" << std::endl; + std::cout << " PIN CODE: " << pinData << std::endl; + std::cout << "============================" << std::endl; +} diff --git a/resource/IPCA/samples/ElevatorServer/elevatorserver.h b/resource/IPCA/samples/ElevatorServer/elevatorserver.h new file mode 100644 index 0000000..09cf6dc --- /dev/null +++ b/resource/IPCA/samples/ElevatorServer/elevatorserver.h @@ -0,0 +1,116 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef ELEVATOR_SERVER_H_ +#define ELEVATOR_SERVER_H_ + +#include +#include +#include "OCPlatform.h" +#include "OCApi.h" +#include + +using namespace OC; + +typedef enum +{ + Stopped = 0, + Up, + Down +} ElevatorDirection; + +class ElevatorServer +{ +public: + ElevatorServer(); + ~ElevatorServer(); + + // Start stop the thread processing the elevator movement. Also register/unregister the + // elevator from IoTivity. + bool Start(const std::string& elevatorName); + void Stop(); + + // Target floor is set by caller. + void SetTargetFloor(int floor); + int GetTargetFloor(); + + // Current floor is set by elevator. + int GetCurrentFloor(); + ElevatorDirection GetElevatorDirection(); + +private: + std::mutex m_elevatorMutex; + + // List of observers, when client app calls resource->Observer(). + ObservationIds m_observers; + + // Send notification to observers. + void NotifyObservers(); + + // Elevator resources + OCResourceHandle m_elevatorResourceHandle; + OCResourceHandle m_elevatorResourceHandle2; + OCResourceHandle m_elevatorResourceHandle3; + OCResourceHandle m_elevatorResourceHandle4; + + int m_targetFloor; // where elevator needs to be. + int m_currentFloor; // where elevator is. + ElevatorDirection m_direction; // current direction of the elevator. + + // Thread moving the elevator. + std::thread engineThread; + bool m_isRunning; + static void Engine(ElevatorServer* elevator); + + // Move current floor to target floor. + void MoveElevator(); + + // Helper function to send response for a request. + OCStackResult SendResponse(std::shared_ptr request); + + // OCF callback for this elevator. + OCEntityHandlerResult ElevatorEntityHandler(std::shared_ptr request); + + // OCF callback for a generated pin + void PinDisplayCallback(char* pinData, size_t pinLength); + + // Elevator device details. + std::string m_name; + + // Elevator platform details. + std::string m_platformID; + std::string m_modelNumber; + std::string m_platformVersion; + std::string m_serialNumber; + std::string m_specVersion; + std::string m_defaultLanguage; + std::string m_manufacturerName; + std::string m_manufacturerUrl; + std::string m_dateOfManufacture; + std::string m_operatingSystemVersion; + std::string m_hardwareVersion; + std::string m_firmwareVersion; + std::string m_supportUrl; + std::string m_systemTime; + +#ifdef SECURED + DisplayPinCallbackHandle m_displayPasswordCallbackHandle; +#endif +}; +#endif // ELEVATOR_SERVER_H_ diff --git a/resource/IPCA/samples/SConscript b/resource/IPCA/samples/SConscript new file mode 100644 index 0000000..14fb69a --- /dev/null +++ b/resource/IPCA/samples/SConscript @@ -0,0 +1,30 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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. +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +## +# ipca samples +# +## + +Import('ipca_env') + +SConscript('ipcaapp/SConscript', 'ipca_env') +SConscript('elevatorserver/SConscript', 'ipca_env') +SConscript('elevatorclient/SConscript', 'ipca_env') diff --git a/resource/IPCA/samples/ipcaapp/SConscript b/resource/IPCA/samples/ipcaapp/SConscript new file mode 100644 index 0000000..998c362 --- /dev/null +++ b/resource/IPCA/samples/ipcaapp/SConscript @@ -0,0 +1,44 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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. +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +## +# ipcapp (application) build script +## +Import('ipca_env') +target_os = ipca_env.get('TARGET_OS') +ipcaapp_env = ipca_env.Clone() + +###################################################################### +# Build flags +###################################################################### +ipcaapp_env.PrependUnique(CPPPATH = [ + '..\..\inc', + ]) + +ipcaapp_env.PrependUnique(LIBS = ['ipca']) + +###################################################################### +# Source files and Targets +###################################################################### +ipcapp_src = [ + 'ipcaapp.cpp' + ] + +ipcaapp = ipcaapp_env.Program('ipcaapp', ipcapp_src) \ No newline at end of file diff --git a/resource/IPCA/samples/ipcaapp/ipcaapp.cpp b/resource/IPCA/samples/ipcaapp/ipcaapp.cpp new file mode 100644 index 0000000..8ccc41d --- /dev/null +++ b/resource/IPCA/samples/ipcaapp/ipcaapp.cpp @@ -0,0 +1,651 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipca.h" + +#if defined(_MSC_VER) +#define UNREFERENCED_PARAMETER(P) (P) +#else +#define UNREFERENCED_PARAMETER(P) +#endif + +IPCAAppHandle g_ipcaAppHandle; +std::recursive_mutex g_globalMutex; + +// Key is resource URI. Value is array of resource types. +typedef std::map> ResourceTypeList; + +// Key is resource URI. Value is array of resource interfaces. +typedef std::map> ResourceInterfaceList; + +// Key is resource URI, Values is array of property name & property value type pair. +typedef std::map> ResourceProperties; + +size_t g_idPool = 1; +size_t GenerateUniqueId() +{ + std::lock_guard lock(g_globalMutex); + return g_idPool++; +} + +class OCFDevice +{ +public: + typedef std::shared_ptr Ptr; + size_t m_localId; // used as context to async IPCA call so there's no need to take a + // ref count. + + OCFDevice(std::string id); + ~OCFDevice(); + void GetDeviceDetails(std::string deviceName, const char** deviceUris, size_t count); + void DisplayDevice(); + void GetPropertiesCallback(IPCAStatus result, const void* context, + IPCAPropertyBagHandle propertyBagHandle); + +private: + IPCADeviceInfo* GetDeviceInfo(); + IPCAPlatformInfo* GetPlatformInfo(); + ResourceTypeList GetResourceTypeInfo(); + ResourceInterfaceList GetResourceInterfaceInfo(); + +private: + IPCAStatus OpenDevice(); + std::string m_deviceId; + std::string m_deviceName; + IPCADeviceHandle m_deviceHandle; // from IPCAOpenDevice(); + IPCADeviceInfo* m_deviceInfo; + IPCAPlatformInfo* m_platformInfo; + std::vector m_deviceUris; // Uris of device. + ResourceTypeList m_resourceTypeList; + ResourceInterfaceList m_resourceInterfaceList; + ResourceProperties m_resourceProperties; +}; + +OCFDevice::OCFDevice(std::string id) : + m_deviceId (id), + m_deviceHandle(nullptr), + m_deviceInfo(nullptr), + m_platformInfo(nullptr), + m_localId(GenerateUniqueId()) +{ +} + +OCFDevice::~OCFDevice() +{ + if (m_deviceHandle != nullptr) + { + IPCACloseDevice(m_deviceHandle); + } + + if (m_deviceInfo != nullptr) + { + IPCAFreeDeviceInfo(m_deviceInfo); + } + + if (m_platformInfo != nullptr) + { + IPCAFreePlatformInfo(m_platformInfo); + } +} + +IPCAStatus OCFDevice::OpenDevice() +{ + if (m_deviceHandle != nullptr) + { + return IPCA_OK; + } + else + { + return IPCAOpenDevice(g_ipcaAppHandle, m_deviceId.c_str(), &m_deviceHandle); + } +} + +IPCADeviceInfo* OCFDevice::GetDeviceInfo() +{ + return m_deviceInfo; +} + +IPCAPlatformInfo* OCFDevice::GetPlatformInfo() +{ + return m_platformInfo; +} + +void C_GetPropertiesCallback(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + std::lock_guard lock(g_globalMutex); + + size_t localId = reinterpret_cast(context); + OCFDevice::Ptr device = nullptr; + + extern std::map g_OCFDeviceList; + + // Find the device. + for (const auto& ocfDevice : g_OCFDeviceList) + { + if (ocfDevice.second->m_localId == localId) + { + device = ocfDevice.second; + break; + } + } + + if (device) + { + device->GetPropertiesCallback(result, context, propertyBagHandle); + } +} + +void OCFDevice::GetPropertiesCallback(IPCAStatus result, + const void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(result); + + std::lock_guard lock(g_globalMutex); + + IPCAStatus status; + char* resourcePath; + char** allKeys; + IPCAValueType* allValueTypes; + size_t count; + + status = IPCAPropertyBagGetResourcePath(propertyBagHandle, &resourcePath); + if ((status != IPCA_OK) || (resourcePath == nullptr)) + { + return; + } + + status = IPCAPropertyBagGetAllKeyValuePairs(propertyBagHandle, + &allKeys, &allValueTypes, &count); + if (status != IPCA_OK) + { + IPCAPropertyBagFreeString(resourcePath); + return; + } + + std::map properties; + for (int i = 0 ; i < count ; i++) + { + properties[allKeys[i]] = allValueTypes[i]; + } + + // Trigger new display if there's any new info. + std::map knownProperties = m_resourceProperties[resourcePath]; + + for (auto& newProperty : properties) + { + if ((knownProperties.size() == 0) || + (knownProperties.find(newProperty.first) == knownProperties.end())) + { + // At least one new property is not in known properties. + // Replace known properties & display device. + m_resourceProperties[resourcePath] = properties; + std::cout << "=== Updated info on device properties: === " << std::endl << std::endl; + DisplayDevice(); + break; + } + } + + IPCAPropertyBagFreeString(resourcePath); + IPCAPropertyBagFreeStringArray(allKeys, count); + IPCAPropertyBagFreeIPCAValueTypeArray(allValueTypes); +} + +ResourceTypeList OCFDevice::GetResourceTypeInfo() +{ + return m_resourceTypeList; +} + +ResourceInterfaceList OCFDevice::GetResourceInterfaceInfo() +{ + return m_resourceInterfaceList; +} + +std::string MapIPCAValueTypeToString(IPCAValueType type) +{ + switch(type) + { + case IPCA_INTEGER: + return "integer"; + + case IPCA_DOUBLE: + return "double"; + + case IPCA_BOOLEAN: + return "boolean"; + + case IPCA_STRING: + return "string"; + + case IPCA_ARRAY: + return "array"; + + case IPCA_PROPERTY_BAG: + return "property bag"; + + case IPCA_VALUE_TYPE_NOT_SUPPORTED: + return "not supported"; + + default: + return "unknown"; + } +} + +void OCFDevice::DisplayDevice() +{ + IPCADeviceInfo* di = GetDeviceInfo(); + + if (di != nullptr) + { + std::cout << "Device Info: " << std::endl; + std::cout << std::endl; + if (m_deviceUris.size() != 0) + { + int i = 0; + for (auto& uri : m_deviceUris) + { + if (i++ == 0) + { + + std::cout << " Device URI . . . . . . . : " << uri << std::endl; + } + else + { + std::cout << " " << uri << std::endl; + } + } + } + + std::cout << " Device ID . . . . . . . : " << (di->deviceId ? di->deviceId : ""); + std::cout << std::endl; + std::cout << " Device Name . . . . . . : " << (di ->deviceName ? di->deviceName : ""); + std::cout << std::endl; + std::cout << " Device Software Version : "; + std::cout << (di->deviceSoftwareVersion ? di->deviceSoftwareVersion : "") << std::endl; + std::cout << std::endl; + } + else + { + std::cout << "Device Info: Not available." << std::endl << std::endl; + } + + IPCAPlatformInfo* pi = GetPlatformInfo(); + + if (pi != nullptr) + { + std::cout << "Platform Info:" << std::endl; + std::cout << std::endl; + std::cout << " Plaform ID . . . . . . . : "; + std::cout << (pi->platformId ? pi->platformId : "") << std::endl; + std::cout << " Manufacturer Name . . . : "; + std::cout << (pi->manufacturerName ? pi->manufacturerName : "") << std::endl; + std::cout << " Manufacturer URL . . . . : "; + std::cout << (pi->manufacturerURL ? pi->manufacturerURL : "") << std::endl; + std::cout << " Model Number . . . . . . : "; + std::cout << (pi->modelNumber ? pi->modelNumber : "") << std::endl; + std::cout << " Manufacturing Date . . . : "; + std::cout << (pi->manufacturingDate ? pi->manufacturingDate : "") << std::endl; + std::cout << " Platform Version . . . . : "; + std::cout << (pi->platformVersion ? pi->platformVersion : "") << std::endl; + std::cout << " OS Version . . . . . . . : "; + std::cout << (pi->osVersion ? pi->osVersion : "") << std::endl; + std::cout << " Hardware Version . . . . : "; + std::cout << (pi->hardwareVersion ? pi->hardwareVersion : "") << std::endl; + std::cout << " Firmware Version . . . . : "; + std::cout << (pi->firmwareVersion ? pi->firmwareVersion : "") << std::endl; + std::cout << " Manufacturer Support URL : "; + std::cout << (pi->manufacturerSupportURL ? pi->manufacturerSupportURL : "") << std::endl; + std::cout << " Reference Time . . . . . : "; + std::cout << (pi->referenceTime ? pi->referenceTime : "") << std::endl; + std::cout << std::endl; + } + else + { + std::cout << "Platform Info: Not available." << std::endl << std::endl; + } + + std::cout << "Resource List:" << std::endl; + ResourceTypeList resourceTypeList = GetResourceTypeInfo(); + ResourceInterfaceList resourceInterfaceList = GetResourceInterfaceInfo(); + for (auto const& resourceType : resourceTypeList) + { + bool firstEntry = true; + std::cout << " Resource URI . . . . . : " << resourceType.first << std::endl; + std::cout << " Resource Types . . . : "; + for (auto const& rt : resourceType.second) + { + if (firstEntry == true) + { + std::cout << rt << std::endl; + firstEntry = false; + } + else + { + std::cout << " " << rt << std::endl; + } + } + + firstEntry = true; + std::cout << " Resource Interfaces . : "; + for (auto const& resourceInterface : resourceInterfaceList[resourceType.first]) + { + if (firstEntry == true) + { + std::cout << resourceInterface << std::endl; + firstEntry = false; + } + else + { + std::cout << " " << resourceInterface << std::endl; + } + } + + std::map properties = m_resourceProperties[resourceType.first]; + if (properties.size() != 0) + { + std::cout << " Properties . . . . . : "; + firstEntry = true; + for (auto const& property : properties) + { + if (firstEntry == true) + { + firstEntry = false; + } + else + { + std::cout << " "; + } + + std::cout << property.first << " ("; + std::cout << MapIPCAValueTypeToString(property.second) << ")" << std::endl; + } + } + + std::cout << std::endl; + } + + std::cout << std::endl << std::endl; +} + +void OCFDevice::GetDeviceDetails(std::string deviceName, const char** deviceUris, size_t count) +{ + std::lock_guard lock(g_globalMutex); + + m_deviceName = deviceName; + + for (size_t i = 0; i < count; i++) + { + if (std::find(m_deviceUris.begin(), m_deviceUris.end(), deviceUris[i]) == m_deviceUris.end()) + { + m_deviceUris.push_back(deviceUris[i]); + } + } + + IPCAStatus status; + + if (OpenDevice() != IPCA_OK) + { + return; + } + + // Get device info, platform info, and resources incl. resource types & property types. + if (m_deviceInfo == nullptr) + { + status = IPCAGetDeviceInfo(m_deviceHandle, &m_deviceInfo); + if ((status != IPCA_INFORMATION_NOT_AVAILABLE) && (status != IPCA_OK)) + { + std::cout << "Failed IPCAGetDeviceInfo() status: " << status << std::endl; + } + } + + if (m_platformInfo == nullptr) + { + status = IPCAGetPlatformInfo(m_deviceHandle, &m_platformInfo); + if ((status != IPCA_INFORMATION_NOT_AVAILABLE) && (status != IPCA_OK)) + { + std::cout << "Failed IPCAGetPlatformInfo() status: " << status << std::endl; + } + } + + char** resourcePathList; + size_t resourceListCount; + status = IPCAGetResources(m_deviceHandle, + nullptr, nullptr, &resourcePathList, &resourceListCount); + if (IPCA_OK != status) + { + std::cout << "Failed IPCAGetResourceURIs() status: " << status << std::endl; + } + + m_resourceTypeList.clear(); + m_resourceInterfaceList.clear(); + + for (size_t i = 0 ; i < resourceListCount ; i++) + { + // Get resource types for each resource. + char** resourceTypes; + size_t resourceTypeCount; + status = IPCAGetResourceTypes(m_deviceHandle, + resourcePathList[i], &resourceTypes, &resourceTypeCount); + if (IPCA_OK == status) + { + for (size_t j = 0 ; j < resourceTypeCount; j++) + { + m_resourceTypeList[resourcePathList[i]].push_back(resourceTypes[j]); + } + IPCAFreeStringArray(resourceTypes, resourceTypeCount); + } + else + { + std::cout << "Failed IPCAGetResourceTypes() for resource: "; + std::cout << resourcePathList[i] << std::endl; + } + + // Get resource interfaces for each resource. + char** resourceInterfaces; + size_t resourceInterfaceCount; + status = IPCAGetResourceInterfaces(m_deviceHandle, + resourcePathList[i], &resourceInterfaces, &resourceInterfaceCount); + if (IPCA_OK == status) + { + for (size_t j = 0 ; j < resourceInterfaceCount; j++) + { + m_resourceInterfaceList[resourcePathList[i]].push_back(resourceInterfaces[j]); + } + IPCAFreeStringArray(resourceInterfaces, resourceInterfaceCount); + } + else + { + std::cout << "Failed IPCAGetResourceInterfaces() for resource: "; + std::cout << resourcePathList[i] << std::endl; + } + + // Get the resource properties. + status = IPCAGetProperties(m_deviceHandle, + &C_GetPropertiesCallback, + reinterpret_cast(m_localId), + resourcePathList[i], + nullptr, + nullptr, + nullptr); + + if (status != IPCA_OK) + { + std::cout << "Failed IPCAGetProperties() status: " << status << std::endl; + } + } + + IPCAFreeStringArray(resourcePathList, resourceListCount); +} + +// Key is device id. Value is pointer to OCFDevice. +std::map g_OCFDeviceList; + +// Callback when device is discovered. +void DiscoverDevicesCallback(void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* deviceInfo) +{ + UNREFERENCED_PARAMETER(context); + + std::lock_guard lock(g_globalMutex); + + std::string deviceId = deviceInfo->deviceId; + std::string deviceName = deviceInfo->deviceName; + + if (g_OCFDeviceList.find(deviceId) == g_OCFDeviceList.end()) + { + OCFDevice::Ptr ocfDevice = std::shared_ptr(new OCFDevice(deviceId)); + if (ocfDevice == nullptr) + { + std::cout << "Out of memory" << std::endl; + return; + } + g_OCFDeviceList[deviceId] = ocfDevice; + } + + OCFDevice::Ptr ocfDevice = g_OCFDeviceList[deviceId]; + + ocfDevice->GetDeviceDetails(deviceName, deviceInfo->deviceUris, deviceInfo->deviceUriCount); + + if (deviceStatus == IPCA_DEVICE_DISCOVERED) + { + std::cout << "*** New Device. Device ID: [" << deviceId << "] ***"; + std::cout << std::endl << std::endl; + } + else + if (deviceStatus == IPCA_DEVICE_UPDATED_INFO) + { + std::cout << "+++ Updated Info. Device ID: [" << deviceId << "] +++"; + std::cout << std::endl << std::endl; + } + else + { + std::cout << "--- Device no longer discoverable. Device ID: [" << deviceId << "] ---"; + std::cout << std::endl << std::endl; + g_OCFDeviceList.erase(deviceId); + return; + } + + ocfDevice->DisplayDevice(); +} + +IPCAStatus IPCA_CALL PasswordInputCallback(void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + char* passwordBuffer, + size_t passwordBufferSize) +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(deviceInformation); + UNREFERENCED_PARAMETER(platformInformation); + UNREFERENCED_PARAMETER(type); + UNREFERENCED_PARAMETER(passwordBuffer); + UNREFERENCED_PARAMETER(passwordBufferSize); + + // @todo: collect the password from the user + + // Refuse authentication for ownership transfer. + return IPCA_FAIL; +} + +IPCAStatus IPCA_CALL PasswordDisplayCallback(void* context, + const IPCADeviceInfo* deviceInformation, + const IPCAPlatformInfo* platformInformation, + IPCAOwnershipTransferType type, + const char* password) +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(deviceInformation); + UNREFERENCED_PARAMETER(platformInformation); + UNREFERENCED_PARAMETER(type); + UNREFERENCED_PARAMETER(password); + + // @todo: display the password and ask for confirmation from the user + + // Refuse authentication for ownership transfer. + return IPCA_FAIL; +} + +int main() +{ + // @future: + // possible options: + // ipcapp [-o] | [-rt] | [-g] | [-s] + // -r + // -rt + // -g + // -s + + IPCAHandle discoverDeviceHandle; + IPCAStatus status; + + // Initialize IPCA. + IPCAUuid appId = {0x37, 0x9d, 0xf2, 0xf2, 0x7e, 0xf7, 0x11, 0xe6, + 0xae, 0x22, 0x56, 0xb6, 0xb6, 0x49, 0x96, 0x11}; + IPCAAppInfo ipcaAppInfo = { appId, "IPCAAPP", "1.0.0", "Microsoft" }; + + status = IPCAOpen(&ipcaAppInfo, IPCA_VERSION_1, &g_ipcaAppHandle); + if (status != IPCA_OK) + { + std::cout << "IPCAOpen failed, status = " << status << std::endl; + } + + // Get ready for Ownership Transfer. + IPCASetPasswordCallbacks(g_ipcaAppHandle, + PasswordInputCallback, PasswordDisplayCallback, nullptr); + + // Start discovering devices. + char* resourceTypes[] = { + "" /* any resource type */ + }; + + const int ResourceTypeCount = sizeof(resourceTypes)/sizeof(char*); + + status = IPCADiscoverDevices( + g_ipcaAppHandle, + &DiscoverDevicesCallback, + nullptr, + resourceTypes, + ResourceTypeCount, + &discoverDeviceHandle); + + + int userInput; + std::cin >> userInput; + + g_OCFDeviceList.clear(); + + IPCACloseHandle(discoverDeviceHandle); + IPCAClose(g_ipcaAppHandle); +} diff --git a/resource/IPCA/src/SConscript b/resource/IPCA/src/SConscript new file mode 100644 index 0000000..c07ebdb --- /dev/null +++ b/resource/IPCA/src/SConscript @@ -0,0 +1,96 @@ +#****************************************************************** +# +# Copyright 2017 Microsoft +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +# +# 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. +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +## +# ipca lib (shared library) build script +## +Import('ipca_env') +target_os = ipca_env.get('TARGET_OS') +ipca_lib_env = ipca_env.Clone() +rd_mode = ipca_lib_env.get('RD_MODE') + +###################################################################### +# Build flags +###################################################################### +ipca_lib_env.PrependUnique(CPPPATH = [ + '../inc', + 'inc', + '#/resource/include', + '#/resource/oc_logger/include', + '#/resource/csdk/include', + '#/resource/csdk/stack/include', + '#/resource/csdk/security/include', + '#/resource/csdk/security/provisioning/include', + '#/resource/csdk/security/provisioning/include/internal', + '#/resource/csdk/connectivity/api', + '#/resource/csdk/connectivity/external/inc', + '#/resource/csdk/connectivity/lib/libcoap-4.1.1/include', + '#/resource/csdk/ocsocket/include', + '#/resource/csdk/logger/include', + '#/resource/c_common/ocrandom/include', + '#/extlibs/boost/boost', + '#/extlibs/cjson', + ]) + +ipca_lib_env.Replace(WINDOWS_INSERT_DEF = ['1']) + +ipca_lib_env.PrependUnique(LIBS = [ + 'oc', + 'connectivity_abstraction', + 'coap', + 'octbstack', + 'oc_logger' + ]) + +if ipca_env.get('SECURED') == '1': + ipca_lib_env.PrependUnique(LIBS = [ + 'mbedtls', + 'mbedx509', + 'mbedcrypto', + 'ocprovision' + ]) + +if 'CLIENT' in rd_mode or 'SERVER' in rd_mode: + ipca_lib_env.PrependUnique(LIBS = ['resource_directory']) + +###################################################################### +# Source files and Targets +###################################################################### +ipca_lib_src = [ + 'app.cpp', + 'ipca.cpp', + 'callback.cpp', + 'ocfframework.cpp', + 'device.cpp', + 'ipcavariant.cpp', + 'common.cpp' + ] + +if ipca_lib_env.get('SECURED') != '1': + ipca_lib_src.append('pretendocprovision.cpp') + +ipca_shared_lib = ipca_lib_env.SharedLibrary('ipca', ipca_lib_src) +ipca_lib = Flatten(ipca_shared_lib) + +ipca_static_lib = ipca_lib_env.StaticLibrary('ipca_static', ipca_lib_src) + +ipca_lib_env.UserInstallTargetHeader('../inc/ipca.h', 'resource', 'ipca.h') +ipca_lib_env.UserInstallTargetLib(ipca_lib, 'ipca') +ipca_lib_env.UserInstallTargetLib(ipca_static_lib, 'ipca') \ No newline at end of file diff --git a/resource/IPCA/src/app.cpp b/resource/IPCA/src/app.cpp new file mode 100644 index 0000000..278b4ab --- /dev/null +++ b/resource/IPCA/src/app.cpp @@ -0,0 +1,740 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "oic_time.h" +#include "ipcainternal.h" + +// Object that implements interface to IoTivity. +// @future: Consider having an instance of this per app when there's mechanism to unregister +// from IoTivity and IoTivity supports multiple apps in same process. +OCFFramework ocfFramework; + +#define TAG "IPCA_App" + +App::App(const IPCAAppInfo* ipcaAppInfo, IPCAVersion ipcaVersion) : + m_isStopped(false), + m_passwordInputCallbackHandle(nullptr), + m_passwordDisplayCallbackHandle(nullptr), + m_ipcaVersion(ipcaVersion) +{ + m_ipcaAppInfo.appId = ipcaAppInfo->appId; + m_ipcaAppInfo.appName = ipcaAppInfo->appName; + m_ipcaAppInfo.appSoftwareVersion = ipcaAppInfo->appSoftwareVersion; + m_ipcaAppInfo.appCompanyName = ipcaAppInfo->appCompanyName; +} + +App::~App() +{ +} + +IPCAStatus App::Start(bool unitTestMode) +{ + m_callback = std::shared_ptr(new Callback(this)); + if (m_callback == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + // Start OCFFramework + IPCAStatus status = ocfFramework.Start(m_ipcaAppInfo, unitTestMode); + if (status != IPCA_OK) + { + m_callback = nullptr; + return status; + } + + // Register app's callback with OCFFramework. + if (ocfFramework.RegisterAppCallbackObject(m_callback) != IPCA_OK) + { + ocfFramework.Stop(m_passwordInputCallbackHandle, m_passwordDisplayCallbackHandle); + m_callback = nullptr; + return IPCA_FAIL; + } + + // Start periodic discovery thread. + m_appWorkerThread = std::thread(&App::AppWorkerThread, this); + return IPCA_OK; +} + +void App::Stop() +{ + ocfFramework.UnregisterAppCallbackObject(m_callback); + + if (m_isStopped) + { + assert(false); // code fault in ipca.cpp if this happens. + return; + } + + // Stop the discovery thread. + m_isStopped = true; + m_discoveryThreadCV.notify_all(); // Wake discovery thread and wait for it to quit. + if (m_appWorkerThread.joinable()) + { + m_appWorkerThread.join(); + } + + // Wait for all in progress callbacks are completed. + m_callback->Stop(); + m_callback = nullptr; + + // Force close devices that are still open. Caller continues to own the deviceWrapper memory + // but with device handle closed. + for(auto& it : m_openedDevices) + { + it.second->device->Close(); + it.second->device = nullptr; // releases reference to device. + } + + // Stop the OCFFramework. + ocfFramework.Stop(m_passwordInputCallbackHandle, m_passwordDisplayCallbackHandle); + m_passwordInputCallbackHandle = nullptr; + m_passwordDisplayCallbackHandle = nullptr; +} + +void App::AppWorkerThread(App* app) +{ + const uint64_t FastDiscoveryCount = 4; // First 4 periodic discovery requests use fast period. + const uint64_t SlowDiscoveryPeriodMs = 30000; + const uint64_t FastDiscoveryPeriodMs = 2000; + + const uint64_t PingPeriodMS = 30000; // Do device ping for Observed devices every 30 seconds. + + // Outstanding requests should time out in 2 seconds per rfc 7252. + // Wake up every second to check. + const size_t AppThreadSleepTimeSeconds = 1; + std::chrono::seconds appThreadSleepTime(AppThreadSleepTimeSeconds); + + std::unique_lock appWorkerLock(app->m_appWorkerThreadMutex); + + OIC_LOG_V(INFO, TAG, "+AppWorkerThread started."); + + while (false == app->m_isStopped) + { + uint64_t currentTime = OICGetCurrentTime(TIME_IN_MS); + + // Do periodic discovery for active IPCADiscoverDevices() requests. + std::map> resourceTypesToDiscover; + { + std::lock_guard lock(app->m_appMutex); + for (auto& entry : app->m_discoveryList) + { + DiscoveryDetails::Ptr discoveryDetails = entry.second; + + if (discoveryDetails->discoveryCount < FastDiscoveryCount) + { + if (currentTime - discoveryDetails->lastDiscoveryTime > FastDiscoveryPeriodMs) + { + resourceTypesToDiscover[entry.first] = + discoveryDetails->resourceTypesToDiscover; + + discoveryDetails->lastDiscoveryTime = currentTime; + discoveryDetails->discoveryCount++; + } + } + else + { + if (currentTime - discoveryDetails->lastDiscoveryTime > SlowDiscoveryPeriodMs) + { + resourceTypesToDiscover[entry.first] = + discoveryDetails->resourceTypesToDiscover; + + discoveryDetails->lastDiscoveryTime = currentTime; + discoveryDetails->discoveryCount++; + } + } + } + } + + for (auto& resourceTypes : resourceTypesToDiscover) + { + ocfFramework.DiscoverResources(resourceTypes.second); + } + + // Do callbacks for expired outstanding requests. + std::vector expiredCallbacks; + app->m_callback->CompleteAndRemoveExpiredCallbackInfo(expiredCallbacks); + + // Get oustanding Observe requests and ping the device every PingPeriodMS. + std::vector observeCallbacks; + app->m_callback->GetCallbackInfoList(CallbackType_ResourceChange, observeCallbacks); + for (auto& cbInfo : observeCallbacks) + { + uint64_t lastPingTime; + if ((IPCA_OK == cbInfo->device->GetLastPingTime(lastPingTime)) && + (currentTime - lastPingTime > PingPeriodMS)) + { + cbInfo->device->Ping(); + } + } + + app->m_discoveryThreadCV.wait_for(appWorkerLock, + appThreadSleepTime, + [app]() + { + return app->m_isStopped; + }); + } + + OIC_LOG_V(INFO, TAG, "-AppWorkerThread exit."); +} + +IPCAStatus App::OpenDevice(const char* deviceId, IPCADeviceHandle* deviceHandle) +{ + *deviceHandle = nullptr; + + std::unique_ptr deviceWrapper(new DeviceWrapper); + if (deviceWrapper == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + Device::Ptr device = std::shared_ptr(new Device(deviceId, &ocfFramework, this)); + if (device == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = device->Open(); + if (status != IPCA_OK) + { + return status; + } + + deviceWrapper->app = this; + deviceWrapper->device = device; + *deviceHandle = reinterpret_cast(deviceWrapper.get()); + m_openedDevices[deviceWrapper.get()] = deviceWrapper.get(); // Take a device reference. + deviceWrapper.release(); + return IPCA_OK; +} + +void App::CloseDevice(IPCADeviceHandle deviceHandle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + if (m_openedDevices.find(deviceWrapper) == m_openedDevices.end()) + { + return; + } + + if (deviceWrapper->device != nullptr) + { + deviceWrapper->device->Close(); + deviceWrapper->device = nullptr; // Release reference to device. + } + + m_openedDevices.erase(deviceWrapper); + delete deviceWrapper; +} + +IPCAStatus App::DiscoverDevices( + IPCADiscoverDeviceCallback callback, + const void* context, + const char* const* resourceTypeList, + int resourceTypeCount, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + // Discovery must have a callback. + if (callback == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + DiscoveryDetails::Ptr discoveryDetails = std::shared_ptr + (new(DiscoveryDetails)); + + if (discoveryDetails == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + nullptr, // No device handle, discovery is a multicast. + &cbInfo, + CallbackType_Discovery, + context, + callback, + resourceTypeList, + resourceTypeCount, + nullptr, // Not Get/Set/Create/Observe request. + nullptr, // Resource interface is not needed. + nullptr, // Resource path is not needed. + nullptr); // Similarly resource type is also not needed. + + if (status != IPCA_OK) + { + return status; + } + + // Start discovery. + discoveryDetails->lastDiscoveryTime = OICGetCurrentTime(TIME_IN_MS); + discoveryDetails->discoveryCount = 1; + discoveryDetails->resourceTypesToDiscover = cbInfo->resourceTypeList; + status = ocfFramework.DiscoverResources(cbInfo->resourceTypeList); + + if (status == IPCA_OK) + { + // Add it to the periodic discovery list. + std::lock_guard lock(m_appMutex); + m_discoveryList[cbInfo->mapKey] = discoveryDetails; + } + else + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::SetPasswordCallbacks( + IPCAProvidePasswordCallback inputCallback, + IPCADisplayPasswordCallback displayCallback, + void* context) +{ + if ((inputCallback == nullptr) || (displayCallback == nullptr)) + { + return IPCA_INVALID_ARGUMENT; + } + + CallbackInfo::Ptr inputCallbackInfo = nullptr; + CallbackInfo::Ptr displayCallbackInfo = nullptr; + + inputCallbackInfo = m_callback->CreatePasswordCallbackInfo( + CallbackType_PasswordInputCallback, + context, + inputCallback, + nullptr); + + if (inputCallbackInfo == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = m_callback->AddCallbackInfo(inputCallbackInfo); + if (status != IPCA_OK) + { + return status; + } + + displayCallbackInfo = m_callback->CreatePasswordCallbackInfo( + CallbackType_PasswordDisplayCallback, + context, + nullptr, + displayCallback); + + if (displayCallbackInfo == nullptr) + { + DeleteAndUnregisterCallbackInfo(inputCallbackInfo->mapKey); + return IPCA_OUT_OF_MEMORY; + } + + status = m_callback->AddCallbackInfo(displayCallbackInfo); + if (status != IPCA_OK) + { + // Failed to add displayCallbackInfo to the callback list, deregister inputCallbackInfo. + // Note that: Failure to add displayCallbackInfo to the list means no ref count is taken on + // displayCallbackInfo. Therefore it will automatically be deleted outside the + // scope of this function. + DeleteAndUnregisterCallbackInfo(inputCallbackInfo->mapKey); + return status; + } + + ocfFramework.SetInputPasswordCallback(inputCallbackInfo, &m_passwordInputCallbackHandle); + ocfFramework.SetDisplayPasswordCallback(displayCallbackInfo, &m_passwordDisplayCallbackHandle); + + return IPCA_OK; +} + +IPCAStatus App::RequestAccess( + Device::Ptr device, + const char* resourcePath, + IPCARequestAccessCompletionCallback completionCallback, + void* context, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo; + CallbackInfo::Ptr passwordInputCbInfo; + + if (handle != nullptr) + { + *handle = nullptr; + } + + cbInfo = m_callback->CreateRequestAccessCompletionCallbackInfo( + device, + context, + resourcePath, + completionCallback); + + if (cbInfo == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = m_callback->AddCallbackInfo(cbInfo); + if (status == IPCA_OK) + { + if (handle != nullptr) + { + *handle = reinterpret_cast(cbInfo->mapKey); + } + } + else + { + return status; + } + + passwordInputCbInfo = m_callback->GetPasswordInputCallbackInfo(); + if (passwordInputCbInfo == nullptr) + { + // App has not registered for password callback. + // Delete the request access callback completion. + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + return IPCA_FAIL; + } + + status = device->RequestAccess(cbInfo, passwordInputCbInfo); + if (status != IPCA_OK) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::GetProperties( + Device::Ptr device, + IPCAGetPropertiesComplete callback, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + device, + &cbInfo, + CallbackType_GetPropertiesComplete, + context, + nullptr, // Not discovery request. + nullptr, // Therefore resourceTypeList is not needed. + 0, + callback, + resourcePath, + resourceInterface, + resourceType); + + if (status != IPCA_OK) + { + return status; + } + + status = device->GetProperties(cbInfo); + + if (status != IPCA_OK) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::SetProperties( + Device::Ptr device, + IPCASetPropertiesComplete callback, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + OC::OCRepresentation* rep, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + device, + &cbInfo, + CallbackType_SetPropertiesComplete, + context, + nullptr, // Not discovery request. + nullptr, // Therefore resourceTypeList is not needed. + 0, + callback, + resourcePath, + resourceInterface, + resourceType); + + if (status != IPCA_OK) + { + return status; + } + + status = device->SetProperties(cbInfo, rep); + + if ((status != IPCA_OK) && (cbInfo != nullptr)) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::ObserveResource( + Device::Ptr device, + IPCAResourceChangeCallback callback, + const void* context, + const char* resourcePath, + const char* resourceType, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + // ObserveResource must have a callback. + if (callback == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + device, + &cbInfo, + CallbackType_ResourceChange, + context, + nullptr, // Not a discovery request. + nullptr, // Therefore resourceTypeList is not needed. + 0, + callback, + resourcePath, + nullptr, + resourceType); + + if (status != IPCA_OK) + { + return status; + } + + status = device->ObserveResource(cbInfo); + + if ((status != IPCA_OK) && (cbInfo != nullptr)) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::CreateResource( + Device::Ptr device, + IPCACreateResourceComplete createResourceCb, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + OC::OCRepresentation* rep, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + device, + &cbInfo, + CallbackType_CreateResourceComplete, + context, + nullptr, // Not a discovery request. + nullptr, // Therefore resourceTypeList is not needed. + 0, + reinterpret_cast(createResourceCb), + resourcePath, + resourceInterface, + resourceType); + + if (status != IPCA_OK) + { + return status; + } + + status = device->CreateResource(cbInfo, rep); + + if ((status != IPCA_OK) && (cbInfo != nullptr)) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +IPCAStatus App::DeleteResource( + Device::Ptr device, + IPCADeleteResourceComplete deleteResourceCb, + const void* context, + const char* resourcePath, + IPCAHandle* handle) +{ + CallbackInfo::Ptr cbInfo = nullptr; + + IPCAStatus status = CreateAndRegisterNewCallbackInfo( + handle, + device, + &cbInfo, + CallbackType_DeleteResourceComplete, + context, + nullptr, // Not a discovery request. + nullptr, // Therefore resourceTypeList is not needed. + 0, + reinterpret_cast(deleteResourceCb), + resourcePath, + nullptr, + nullptr); + + if (status != IPCA_OK) + { + return status; + } + + status = device->DeleteResource(cbInfo); + + if ((status != IPCA_OK) && (cbInfo != nullptr)) + { + if (handle != nullptr) + { + *handle = nullptr; + } + + DeleteAndUnregisterCallbackInfo(cbInfo->mapKey); + } + + return status; +} + +void App::CloseIPCAHandle(IPCAHandle handle) +{ + size_t mapKey = reinterpret_cast(handle); + + CallbackInfo::Ptr cbInfo = m_callback->GetCallbackInfo(mapKey); + + if (cbInfo != nullptr) + { + if (cbInfo->type == CallbackType_Discovery) + { + // Stop periodic discovery of these resource types. + std::lock_guard lock(m_appMutex); + m_discoveryList.erase(cbInfo->mapKey); + } + else + if (cbInfo->type == CallbackType_ResourceChange) + { + cbInfo->device->StopObserve(cbInfo); + } + } + + DeleteAndUnregisterCallbackInfo(mapKey); +} + +IPCAStatus App::CreateAndRegisterNewCallbackInfo( + IPCAHandle* handle, + Device::Ptr device, + CallbackInfo::Ptr* cbInfo, + CallbackType cbType, + const void* context, + IPCADiscoverDeviceCallback discoverDeviceCallback, + const char* const* resourceTypeList, + int resourceTypeCount, + GenericAppCallback appCallback, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType) +{ + if (handle != nullptr) + { + *handle = nullptr; + } + + *cbInfo = m_callback->CreateCallbackInfo( + device, + cbType, + context, + discoverDeviceCallback, + resourceTypeList, + resourceTypeCount, + appCallback, + resourcePath, + resourceInterface, + resourceType); + + if (*cbInfo == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = m_callback->AddCallbackInfo(*cbInfo); + + if (status == IPCA_OK) + { + if (handle != nullptr) + { + *handle = reinterpret_cast((*cbInfo)->mapKey); + } + } + + return status; +} + +void App::DeleteAndUnregisterCallbackInfo(size_t mapKey) +{ + m_callback->RemoveCallbackInfo(mapKey); +} diff --git a/resource/IPCA/src/callback.cpp b/resource/IPCA/src/callback.cpp new file mode 100644 index 0000000..d602a34 --- /dev/null +++ b/resource/IPCA/src/callback.cpp @@ -0,0 +1,849 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "oic_time.h" +#include "ipcainternal.h" +#include +#include +#include + +#define TAG "IPCA_Callback" + +extern OCFFramework ocfFramework; + +Callback::Callback(App* app) : + m_nextKey(0), + m_app(app), + m_stopCalled(false), + m_expiredCallbacksInprogress(0) +{ +} + +Callback::~Callback() +{ +} + +// Exception when Stop() times out waiting for pending callback to complete. +class StopTimeout : public std::exception +{ + virtual const char* what() const throw() + { + return "Time out waiting for callback to complete in Callback::Stop()."; + } +} timeoutException; + +void Callback::Stop() +{ + bool allStopped = false; + + // No more callbacks from here on as SetCallbackInProgress() and + // CompleteAndRemoveExpiredCallbackInfo() check for this flag. + m_stopCalled = true; + + // Wait some amount of time for all callbacks in progress to complete. + const int WaitTimeSeconds = 3; + int i = 0; + while (i < WaitTimeSeconds) + { + if (m_callbackInfoList.size() != 0) + { + std::lock_guard lock(m_callbackMutex); + for (auto it = m_callbackInfoList.cbegin(); + it != m_callbackInfoList.cend(); + /* increment inside loop */) + { + if (it->second->callbackInProgressCount == 0) + { + m_callbackInfoList.erase(it++); + } + else + { + ++it; + } + } + } + + // There are 2 group of callbacks. + // One tracked by m_callbackInfoList and the other tracked by m_expiredCallbacksInprogress. + if ((m_callbackInfoList.size() == 0) && (m_expiredCallbacksInprogress == 0)) + { + allStopped = true; + break; + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + i++; + } + + if (allStopped == false) + { + OIC_LOG_V(WARNING, TAG, "Stop() time out waiting for pending callbacks to complete."); + throw timeoutException; + } + +} + +CallbackInfo::Ptr Callback::CreatePasswordCallbackInfo( + CallbackType cbType, + const void* context, + IPCAProvidePasswordCallback passwordInputCb, + IPCADisplayPasswordCallback passwordDisplayCb) +{ + if ((cbType != CallbackType_PasswordInputCallback) && + (cbType != CallbackType_PasswordDisplayCallback)) + { + assert(false); + return nullptr; + } + + CallbackInfo::Ptr cbInfo = std::shared_ptr(new(CallbackInfo)); + + if (cbInfo == nullptr) + { + return nullptr; + } + + cbInfo->app = m_app; + cbInfo->type = cbType; + cbInfo->callbackContext = context; + cbInfo->callbackInProgressCount = 0; + cbInfo->markedToBeRemoved = false; + + switch (cbType) + { + case CallbackType_PasswordInputCallback: + cbInfo->passwordInputCallback = passwordInputCb; + break; + + case CallbackType_PasswordDisplayCallback: + cbInfo->passwordDisplayCallback = passwordDisplayCb; + break; + } + + return cbInfo; +} + +CallbackInfo::Ptr Callback::CreateRequestAccessCompletionCallbackInfo( + DevicePtr device, + const void* context, + const char* resourcePath, + IPCARequestAccessCompletionCallback completionCallback) +{ + CallbackInfo::Ptr cbInfo = std::shared_ptr(new(CallbackInfo)); + + if (cbInfo == nullptr) + { + return nullptr; + } + + cbInfo->app = m_app; + cbInfo->device = device; + cbInfo->type = Callbacktype_RequestAccessCompletionCallback; + cbInfo->callbackContext = context; + cbInfo->callbackInProgressCount = 0; + cbInfo->markedToBeRemoved = false; + cbInfo->requestAccessCompletionCallback = completionCallback; + + if (resourcePath != nullptr) + { + cbInfo->resourcePath = resourcePath; + } + + return cbInfo; +} + +CallbackInfo::Ptr Callback::CreateCallbackInfo( + DevicePtr device, + CallbackType cbType, + const void* context, + IPCADiscoverDeviceCallback discoverDeviceCallback, + const char* const* resourceTypeList, + int resourceTypeCount, + GenericAppCallback appCallback, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType) +{ + CallbackInfo::Ptr cbInfo = std::shared_ptr(new(CallbackInfo)); + + if (cbInfo == nullptr) + { + return nullptr; + } + + cbInfo->app = m_app; + cbInfo->device = device; + cbInfo->type = cbType; + cbInfo->callbackContext = context; + cbInfo->callbackInProgressCount = 0; + cbInfo->markedToBeRemoved = false; + cbInfo->requestSentTimestamp = 0; + + cbInfo->resourcePath = std::string(resourcePath ? resourcePath : ""); + cbInfo->resourceInterface = std::string(resourceInterface ? resourceInterface : ""); + cbInfo->resourceType = std::string(resourceType ? resourceType : ""); + + switch (cbType) + { + case CallbackType_Discovery: + { + cbInfo->discoveryCallback = discoverDeviceCallback; + int i = 0; + while (i < resourceTypeCount) + { + cbInfo->resourceTypeList.push_back(resourceTypeList[i++]); + } + break; + } + + case CallbackType_ResourceChange: + { + cbInfo->resourceChangeCallback= appCallback; + break; + } + + case CallbackType_GetPropertiesComplete: + { + cbInfo->getCallback = appCallback; + break; + } + + case CallbackType_SetPropertiesComplete: + { + cbInfo->setCallback = appCallback; + break; + } + + case CallbackType_CreateResourceComplete: + { + cbInfo->createResourceCallback= + reinterpret_cast(appCallback); + break; + } + + case CallbackType_DeleteResourceComplete: + { + cbInfo->deleteResourceCallback= + reinterpret_cast(appCallback); + break; + } + + default: + { + // Must be new CallbackType definition that needs new code. + assert(false); + break; + } + } + + return cbInfo; +} + +IPCAStatus Callback::AddCallbackInfo(CallbackInfo::Ptr cbInfo) +{ + std::lock_guard lock(m_callbackMutex); + + // App has called IPCAClose(). Stop taking CallbackInfo. + if (m_stopCalled == true) + { + return IPCA_FAIL; + } + + uint32_t i = 0; + while (i++ < UINT32_MAX) + { + uint32_t newKey = m_nextKey++; + if (m_callbackInfoList.find(newKey) == m_callbackInfoList.end()) + { + OIC_LOG_V(INFO, TAG, "AddCallbackInfo() with key: %d", newKey); + cbInfo->mapKey = newKey; + m_callbackInfoList[newKey] = cbInfo; + return IPCA_OK; + } + } + + // All map entries are filled. It's a large table (32 bit)! + return IPCA_OUT_OF_MEMORY; +} + +CallbackInfo::Ptr Callback::GetCallbackInfo(size_t mapKey) +{ + std::lock_guard lock(m_callbackMutex); + + // App has called IPCAClose(). + if (m_stopCalled == true) + { + return nullptr; + } + + if (m_callbackInfoList.find(mapKey) != m_callbackInfoList.end()) + { + return m_callbackInfoList[mapKey]; + } + else + { + return nullptr; + } +} + +CallbackInfo::Ptr Callback::GetPasswordInputCallbackInfo() +{ + std::lock_guard lock(m_callbackMutex); + + // App has called IPCAClose(). + if (m_stopCalled == true) + { + return nullptr; + } + + // Go through the CallbackInfo list to find the PasswordInputCallback CallbackInfo. + // PasswordInputCallback is only registered once by the app so there can only be one result. + for (auto const& entry : m_callbackInfoList) + { + if (entry.second->type == CallbackType_PasswordInputCallback) + { + return entry.second; + } + } + + return nullptr; +} + +bool Callback::SetCallbackInProgress(size_t mapKey) +{ + std::lock_guard lock(m_callbackMutex); + + // App has called IPCAClose(). Pending callbacks are automatically cleared. + if (m_stopCalled == true) + { + return false; + } + + if (m_callbackInfoList.find(mapKey) != m_callbackInfoList.end()) + { + CallbackInfo::Ptr callbackInfo = m_callbackInfoList[mapKey]; + + // This callback is marked for removal. + if (callbackInfo->markedToBeRemoved) + { + return false; + } + + // Indicate that callback is in progress. + callbackInfo->callbackInProgressCount++; + return true; + } + else + { + // Callback handle is already closed by app. + return false; + } +} + +bool Callback::ClearCallbackInProgress(size_t mapKey) +{ + std::lock_guard lock(m_callbackMutex); + + // This function does not check m_stopCalled, because any call already in progress + // must complete. + if (m_callbackInfoList.find(mapKey) != m_callbackInfoList.end()) + { + m_callbackInfoList[mapKey]->callbackInProgressCount--; + return true; + } + + OIC_LOG_V(INFO, TAG, "ClearCallbackInProgress() mapKey [%d] is not found", mapKey); + assert(false); // In progress callback is not expected to be removed from the list. + return false; +} + +void Callback::RemoveCallbackInfo(size_t mapKey) +{ + std::lock_guard lock(m_callbackMutex); + + if (m_callbackInfoList.find(mapKey) != m_callbackInfoList.end()) + { + if (m_callbackInfoList[mapKey]->callbackInProgressCount == 0) + { + m_callbackInfoList.erase(mapKey); + } + else + { + m_callbackInfoList[mapKey]->markedToBeRemoved = true; + } + } +} + +void Callback::CompleteAndRemoveExpiredCallbackInfo(std::vector& cbInfoList) +{ + // @tbd: determine a good value for response timeout. + const int RequestTimeoutMs = 2000; // 2 seconds for request timeout. + + uint64_t currentTime = OICGetCurrentTime(TIME_IN_MS); + + { + std::lock_guard lock(m_callbackMutex); + + if (m_stopCalled == true) + { + return; + } + + // Collect the expired requests. + for(auto const& entry : m_callbackInfoList) + { + // Opportunistic removal of callback that couldn't be removed during + // RemoveCallbackInfo(). + if (entry.second->markedToBeRemoved == true) + { + cbInfoList.push_back(entry.second); + continue; + } + + // Look for outstanding get, set, create requests that have been sent + // (time stamp is not 0), have timed out and not already in the middle of callback. + if ((entry.second->requestSentTimestamp != 0) && + (entry.second->callbackInProgressCount == 0) && + ((entry.second->type == CallbackType_GetPropertiesComplete) || + (entry.second->type == CallbackType_SetPropertiesComplete) || + (entry.second->type == CallbackType_CreateResourceComplete) || + (entry.second->type == CallbackType_DeleteResourceComplete))) + { + if ((currentTime - entry.second->requestSentTimestamp) > RequestTimeoutMs) + { + m_expiredCallbacksInprogress++; + cbInfoList.push_back(entry.second); + } + } + } + + // Remove them from the list. + for (auto const& entry : cbInfoList) + { + m_callbackInfoList.erase(entry->mapKey); + } + } + + // Complete the callback for each. + for (auto const& cbInfo : cbInfoList) + { + if (cbInfo->getCallback != nullptr) + { + std::thread thrd; + switch(cbInfo->type) + { + case CallbackType_GetPropertiesComplete: + thrd = std::thread(cbInfo->getCallback, + IPCA_REQUEST_TIMEOUT, + const_cast(cbInfo->callbackContext), + nullptr); + thrd.detach(); + break; + + case CallbackType_SetPropertiesComplete: + thrd = std::thread(cbInfo->setCallback, + IPCA_REQUEST_TIMEOUT, + const_cast(cbInfo->callbackContext), + nullptr); + thrd.detach(); + break; + + case CallbackType_CreateResourceComplete: + thrd = std::thread(cbInfo->createResourceCallback, + IPCA_REQUEST_TIMEOUT, + const_cast(cbInfo->callbackContext), + nullptr, + nullptr); + thrd.detach(); + break; + + case CallbackType_DeleteResourceComplete: + thrd = std::thread(cbInfo->deleteResourceCallback, + IPCA_REQUEST_TIMEOUT, + const_cast(cbInfo->callbackContext)); + thrd.detach(); + break; + + default: + assert(false); // check the filter code above to match the handling here. + break; + } + } + + { + std::lock_guard lock(m_callbackMutex); + m_expiredCallbacksInprogress--; + } + } + +} + +void Callback::GetCallbackInfoList(CallbackType type, std::vector& filteredList) +{ + std::lock_guard lock(m_callbackMutex); + + for(auto const& entry : m_callbackInfoList) + { + if (entry.second->type == type) + { + filteredList.push_back(entry.second); + } + } +} + +bool Callback::MatchAllRequiredResourceTypes( + std::vector& requiredResourceTypes, + std::vector& deviceResourceTypes) +{ + for (auto const& targetResourceType : requiredResourceTypes) + { + if (targetResourceType.length() == 0) + { + OIC_LOG_V(INFO, TAG, + "MatchAllRequiredResourceTypes(): app has nullptr target function. returning true"); + return true; + } + + if (std::find(deviceResourceTypes.begin(), + deviceResourceTypes.end(), + targetResourceType) == deviceResourceTypes.end()) + { + OIC_LOG_V(INFO, TAG, + "MatchAllRequiredResourceTypes(): return false. Resource Type not found: [%s]", + targetResourceType.c_str()); + return false; + } + } + + OIC_LOG_V(INFO, TAG, "MatchAllRequiredResourceTypes(): returning true. "); + return true; +} + +void Callback::DeviceDiscoveryCallback( + bool deviceResponding, + bool newInfoLearntAboutDevice, + InternalDeviceInfo deviceInfo, + std::vector deviceResourceTypeList) +{ + // Create IPCADiscoveredDeviceInfo object for callback. + IPCADiscoveredDeviceInfo deviceInfoUsedForCallback = {0}; + + if (deviceInfo.deviceUris.size() != 0) + { + deviceInfoUsedForCallback.deviceUris = + static_cast(OICCalloc(deviceInfo.deviceUris.size(), sizeof(char*))); + + if (deviceInfoUsedForCallback.deviceUris == nullptr) + { + OIC_LOG_V(WARNING, TAG, "Callback::DeviceDiscoveryCallback: out of memory."); + return; + } + + int i = 0; + for (auto const& deviceUri : deviceInfo.deviceUris) + { + deviceInfoUsedForCallback.deviceUris[i++] = deviceUri.c_str(); + } + } + + deviceInfoUsedForCallback.deviceId = deviceInfo.deviceId.c_str(); + deviceInfoUsedForCallback.deviceName = deviceInfo.deviceName.c_str(); + deviceInfoUsedForCallback.deviceUriCount = deviceInfo.deviceUris.size(); + + // 2 groups of callbacks are interested in device discovery status. + // One is outstanding Observe requests. The other is outstanding IPCADiscoveryDevices() + // requests. + + // Outstanding observe requests are interested in IPCA_DEVICE_APPEAR_OFFLINE. + if (false == deviceResponding) + { + // Call Observe callback with IPCA_DEVICE_APPEAR_OFFLINE if this is the device whose + // resource it is observing. + std::vector observeCallbackinfoList; + GetCallbackInfoList(CallbackType_ResourceChange, observeCallbackinfoList); + + for(auto const& cbInfo : observeCallbackinfoList) + { + // if device id matches and not in shut down already + if ((cbInfo->device->GetDeviceId().compare(deviceInfo.deviceId) == 0) && + (SetCallbackInProgress(cbInfo->mapKey) == true)) + { + std::thread exec( + cbInfo->resourceChangeCallback, + IPCA_DEVICE_APPEAR_OFFLINE, + const_cast(cbInfo->callbackContext), + nullptr); + exec.detach(); + ClearCallbackInProgress(cbInfo->mapKey); + } + } + } + + // IPCADiscoverDevices() requests are interested in IPCADeviceStatus callback. + std::vector discoveryCallbackInfoList; + GetCallbackInfoList(CallbackType_Discovery, discoveryCallbackInfoList); + + // Synchronize discovery callback to app to ensure IPCA_DEVICE_DISCOVERED is always first. + m_discoverDeviceCallbackMutex.lock(); + + // Callback if it matches resource types requested by app in IPCADiscoverDevices(). + for(auto const& cbInfo : discoveryCallbackInfoList) + { + if (MatchAllRequiredResourceTypes(cbInfo->resourceTypeList, deviceResourceTypeList)) + { + // Indicate that callback is in progress and call the app's callback. + if (SetCallbackInProgress(cbInfo->mapKey) == true) + { + m_callbackMutex.lock(); + auto it = std::find(cbInfo->discoveredDevicesList.begin(), + cbInfo->discoveredDevicesList.end(), + deviceInfo.deviceId); + + if (it == cbInfo->discoveredDevicesList.end()) + { + // App has never received IPCA_DEVICE_DISCOVERED for this device. + if (deviceResponding) // New device for this callback if device is responding, + // otherwise it's a nop. + { + // Track that app receives IPCA_DEVICE_DISCOVERED callback for this device. + cbInfo->discoveredDevicesList.push_back(deviceInfo.deviceId); + + // Release lock to call out to app. + m_callbackMutex.unlock(); + (cbInfo->discoveryCallback)(const_cast(cbInfo->callbackContext), + IPCA_DEVICE_DISCOVERED, &deviceInfoUsedForCallback); + m_callbackMutex.lock(); + } + } + else + { + // App has received IPCA_DEVICE_DISCOVERED for this device in the past. + if (deviceResponding) + { + // Callback to app if there's new info. + if (newInfoLearntAboutDevice == true) + { + m_callbackMutex.unlock(); + (cbInfo->discoveryCallback)(const_cast(cbInfo->callbackContext), + IPCA_DEVICE_UPDATED_INFO, &deviceInfoUsedForCallback); + m_callbackMutex.lock(); + } + } + else + { + // Remove the device from discovered device list of this callback. + // Next time the same device shows up, this callback will get + // IPCA_DEVICE_DISCOVERED. + cbInfo->discoveredDevicesList.erase(it); + + m_callbackMutex.unlock(); + (cbInfo->discoveryCallback)(const_cast(cbInfo->callbackContext), + IPCA_DEVICE_STOPPED_RESPONDING, &deviceInfoUsedForCallback); + m_callbackMutex.lock(); + } + } + m_callbackMutex.unlock(); + + ClearCallbackInProgress(cbInfo->mapKey); + } + } + } + + m_discoverDeviceCallbackMutex.unlock(); + + if (deviceInfoUsedForCallback.deviceUris != nullptr) + { + OICFree(deviceInfoUsedForCallback.deviceUris); + deviceInfoUsedForCallback.deviceUris = nullptr; + } +} + +void Callback::GetCallback(IPCAStatus status, const OCRepresentation& rep, CallbackInfo::Ptr cbInfo) +{ + // Check if cbInfo is for this app and mark that callback in progress. + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + if (cbInfo->getCallback != nullptr) + { + cbInfo->getCallback( + status, + const_cast(cbInfo->callbackContext), + (IPCAPropertyBagHandle)&rep); + } + + ClearCallbackInProgress(cbInfo->mapKey); + RemoveCallbackInfo(cbInfo->mapKey); +} + +void Callback::SetCallback(IPCAStatus status, const OCRepresentation& rep, CallbackInfo::Ptr cbInfo) +{ + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + if (cbInfo->type == CallbackType_CreateResourceComplete) + { + cbInfo->createResourceCallback( + status, + const_cast(cbInfo->callbackContext), + NULL, /* tbd: no info on new resource URI. */ + /* See https://jira.iotivity.org/browse/IOT-1819 */ + (IPCAPropertyBagHandle)&rep); + } + else + { + cbInfo->setCallback( + status, + const_cast(cbInfo->callbackContext), + (IPCAPropertyBagHandle)&rep); + } + + ClearCallbackInProgress(cbInfo->mapKey); + RemoveCallbackInfo(cbInfo->mapKey); +} + +void Callback::ObserveCallback(IPCAStatus status, + const OCRepresentation& rep, + CallbackInfo::Ptr cbInfo) +{ + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + cbInfo->resourceChangeCallback( + status, + const_cast(cbInfo->callbackContext), + (IPCAPropertyBagHandle)&rep); + + ClearCallbackInProgress(cbInfo->mapKey); +} + +void Callback::DeleteResourceCallback(IPCAStatus status, CallbackInfo::Ptr cbInfo) +{ + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + cbInfo->deleteResourceCallback( + status, + const_cast(cbInfo->callbackContext)); + + ClearCallbackInProgress(cbInfo->mapKey); +} + +void Callback::PasswordInputCallback(std::string deviceId, + IPCAOwnershipTransferType type, + char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr cbInfo) +{ + IPCADeviceInfo* deviceInfo = nullptr; + IPCAPlatformInfo* platformInfo = nullptr; + + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + IPCAStatus status = ocfFramework.CopyDeviceInfo(deviceId, &deviceInfo); + if (IPCA_OK == status) + { + status = ocfFramework.CopyPlatformInfo(deviceId, &platformInfo); + if (IPCA_OK != status) + { + OIC_LOG_V(INFO, TAG, + "Callback::PasswordInfoCallback: Failed to retrieve platform information."); + } + } + else + { + OIC_LOG_V(INFO, TAG, + "Callback::PasswordInfoCallback: Failed to retrieve device information."); + } + + if (cbInfo->passwordInputCallback != nullptr) + { + cbInfo->passwordInputCallback( + const_cast(cbInfo->callbackContext), + deviceInfo, + platformInfo, + type, + passwordBuffer, + passwordBufferSize); + } + + ClearCallbackInProgress(cbInfo->mapKey); + + if (deviceInfo != nullptr) + { + OCFFramework::FreeDeviceInfo(deviceInfo); + deviceInfo = nullptr; + } + + if (platformInfo != nullptr) + { + OCFFramework::FreePlatformInfo(platformInfo); + platformInfo = nullptr; + } +} + +void Callback::PasswordDisplayCallback( + std::string deviceId, + IPCAOwnershipTransferType type, + const char* passwordBuffer, + CallbackInfo::Ptr cbInfo) +{ + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + if (cbInfo->passwordDisplayCallback != nullptr) + { + cbInfo->passwordDisplayCallback( + const_cast(cbInfo->callbackContext), + nullptr, + nullptr, + type, + passwordBuffer); + } + + ClearCallbackInProgress(cbInfo->mapKey); +} + +void Callback::RequestAccessCompletionCallback(IPCAStatus status, CallbackInfo::Ptr cbInfo) +{ + if ((cbInfo->app != m_app) || (SetCallbackInProgress(cbInfo->mapKey) == false)) + { + return; + } + + if (cbInfo->requestAccessCompletionCallback != nullptr) + { + cbInfo->requestAccessCompletionCallback(status, const_cast(cbInfo->callbackContext)); + } + + ClearCallbackInProgress(cbInfo->mapKey); +} diff --git a/resource/IPCA/src/common.cpp b/resource/IPCA/src/common.cpp new file mode 100644 index 0000000..a011f56 --- /dev/null +++ b/resource/IPCA/src/common.cpp @@ -0,0 +1,313 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include + +#include "oic_malloc.h" +#include "ipca.h" +#include "ipcainternal.h" + +// Copy from std::string to char array. Return true if source is truncated at dest. +bool CopyStringToBufferAllowTruncate(const std::string& source, char* dest, size_t destSize) +{ + if ((dest == nullptr) || (destSize == 0)) + { + return false; + } + + bool isTruncated = false; + size_t copied = source.copy(dest, destSize, 0); + if (copied == destSize) + { + copied--; // make room for null + isTruncated = true; + } + + // std::string copy does not include null. + dest[copied] = '\0'; + return isTruncated; +} + +bool CopyStringToFlatBuffer(const std::string& source, char* dest, size_t* destBufferSize) +{ + if (dest == nullptr) + { + return false; + } + + size_t sourceLength = source.length(); // excl. null. + if (sourceLength + 1 > *destBufferSize) + { + *destBufferSize = sourceLength + 1; // the required buffer size. + return false; + } + + source.copy(dest, sourceLength, 0); + dest[sourceLength] = '\0'; + return true; +} + +IPCAStatus AllocateAndCopyStringToFlatBuffer(const std::string& source, char** dest) +{ + if (dest == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + size_t bufferSize = source.length() + 1; // incl. null. + *dest = static_cast(OICMalloc(bufferSize)); + if (*dest == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + if (CopyStringToFlatBuffer(source, *dest, &bufferSize) == false) + { + OICFreeAndSetToNull(reinterpret_cast(dest)); + return IPCA_FAIL; + } + + return IPCA_OK; +} + +IPCAStatus AllocateAndCopyStringVectorToArrayOfCharPointers( + const std::vector& source, + char*** dest, + size_t* count) +{ + if ((count == nullptr) || (dest == nullptr)) + { + return IPCA_INVALID_ARGUMENT; + } + + size_t stringCount = source.size(); + + if (stringCount == 0) + { + *count = 0; + *dest = nullptr; + return IPCA_OK; + } + + *count = stringCount; + *dest = static_cast(OICCalloc(stringCount, sizeof(char*))); + if (*dest == nullptr) + { + *count = 0; + return IPCA_OUT_OF_MEMORY; + } + + size_t i = 0; + IPCAStatus status = IPCA_FAIL; + for (auto& sourceString : source) + { + status = AllocateAndCopyStringToFlatBuffer(sourceString, &((*dest)[i])); + if (status != IPCA_OK) + { + break; + } + + i++; + } + + // Rollback if any failure. + if (i != stringCount) + { + FreeArrayOfCharPointers(*dest, i); + *count = 0; + *dest = nullptr; + return status; + } + + return IPCA_OK; +} + +void FreeArrayOfCharPointers(char** array, size_t count) +{ + if ((array == nullptr) || (count == 0)) + { + return; + } + + for (int i = 0; i < count; i++) + { + OICFree((void*)(array[i])); + } + + OICFree(array); +} + +bool IsStringInList(const std::string& string, const std::vector& list) +{ + return (std::find(list.begin(), list.end(), string) != list.end()); +} + +bool AddNewStringsToTargetList(const std::vector& newList, + std::vector& targetList) +{ + bool foundNewEntry = false; + for (auto const& newString : newList) + { + if (!IsStringInList(newString, targetList)) + { + targetList.push_back(newString.c_str()); + foundNewEntry = true; + } + } + + return foundNewEntry; +} + +void PrintMargin(size_t marginDepth) +{ + std::cout << std::string(3 * marginDepth, ' '); +} + +template +void PrintVectorValues(std::vector<_T> vector, size_t marginDepth) +{ + OC_UNUSED(marginDepth); + + for (auto value : vector) + { + std::cout << value << std::endl; + } +} + +void PrintOCRep(const OCRepresentation& rep, size_t marginDepth) +{ + OCRepresentation::const_iterator itr= rep.begin(); + OCRepresentation::const_iterator endItr = rep.end(); + + PrintMargin(marginDepth); + std::cout << "{" << std::endl; + marginDepth++; + + for (; itr != endItr; ++itr) + { + PrintMargin(marginDepth); + std::cout << "\"" << itr->attrname() << "\" : "; + + switch(itr->type()) + { + case AttributeType::Null: + std::cout << "Null" << std::endl; + break; + + case AttributeType::Integer: + std::cout << (*itr).getValue() << std::endl; + break; + + case AttributeType::Double: + std::cout << (*itr).getValue() << std::endl; + break; + + case AttributeType::Boolean: + std::cout << (*itr).getValue() << std::endl; + break; + + case AttributeType::String: + std::cout << "\"" << ((*itr).getValue()).c_str() << "\"" << std::endl; + break; + + case AttributeType::OCRepresentation: + { + OC::OCRepresentation repValue = (*itr).getValue(); + std::cout << std::endl; + PrintOCRep(repValue, marginDepth); + break; + } + + case AttributeType::Vector: + { + AttributeType vectorBaseType = itr->base_type(); + std::cout << std::endl; + PrintMargin(marginDepth); + std::cout << "[" << std::endl; + + if (itr->depth() != 1) + { + PrintMargin(marginDepth); + std::cout << "PrintOCRep works with 1 level depth vector. " << std::endl; + continue; + } + + switch (vectorBaseType) + { + case AttributeType::Integer: + { + std::vector vec = (*itr).getValue>(); + PrintVectorValues(vec, marginDepth); + break; + } + + case AttributeType::Double: + { + std::vector vec = (*itr).getValue>(); + PrintVectorValues(vec, marginDepth); + break; + } + + case AttributeType::Boolean: + { + std::vector vec = (*itr).getValue>(); + PrintVectorValues(vec, marginDepth); + break; + } + + case AttributeType::String: + { + std::vector vec = (*itr).getValue>(); + PrintVectorValues(vec, marginDepth); + break; + } + + case AttributeType::OCRepresentation: + { + std::vector vec = + (*itr).getValue>(); + for (auto& value : vec) + { + PrintOCRep(value, (marginDepth+1)); + } + break; + } + + default: + PrintMargin(marginDepth); + std::cout << "Unhandled vector base type: " << vectorBaseType << std::endl; + break; + } + + PrintMargin(marginDepth); + std::cout << "]" << std::endl; + break; + } + + default: + PrintMargin(marginDepth); + std::cout << "Value type not handled: " << itr->type() << std::endl; + break; + } + } + + marginDepth--; + PrintMargin(marginDepth); + std::cout << "}" << std::endl; +} diff --git a/resource/IPCA/src/device.cpp b/resource/IPCA/src/device.cpp new file mode 100644 index 0000000..65dc6a6 --- /dev/null +++ b/resource/IPCA/src/device.cpp @@ -0,0 +1,218 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "ipca.h" +#include "ipcainternal.h" +#include "oic_malloc.h" + +Device::Device(const char* deviceId, OCFFramework* ocf, App* app) : + m_deviceId(deviceId), + m_app(app), + m_ocfFramework(ocf), + m_isClosed(false) +{ +} + +Device::~Device() +{ +} + +IPCAStatus Device::Open() +{ + return m_ocfFramework->IPCADeviceOpenCalled(m_deviceId); +} + +IPCAStatus Device::Close() +{ + m_isClosed = true; + return m_ocfFramework->IPCADeviceCloseCalled(m_deviceId); +} + +IPCAStatus Device::GetDeviceInfo(IPCADeviceInfo** callerDeviceInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->CopyDeviceInfo(m_deviceId, callerDeviceInfo); +} + +IPCAStatus Device::GetPlatformInfo(IPCAPlatformInfo** callerPlatformInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->CopyPlatformInfo(m_deviceId, callerPlatformInfo); +} + +IPCAStatus Device::GetResourcePathList(const std::string& resourceInterface, + const std::string& resourceType, + char*** resourcePathList, + size_t* resourcePathCount) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + *resourcePathList = nullptr; + *resourcePathCount = 0; + + std::vector resourcePaths; + IPCAStatus status = m_ocfFramework->CopyResourcePaths( + resourceInterface, resourceType, m_deviceId, resourcePaths); + if (status != IPCA_OK) + { + return status; + } + + return AllocateAndCopyStringVectorToArrayOfCharPointers( + resourcePaths, resourcePathList, resourcePathCount); +} + +IPCAStatus Device::GetResourceInfo(const char* resourcePath, + ResourceInfoType resourceInfoType, + char*** stringArray, + size_t* stringArrayCount) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + *stringArray = nullptr; + *stringArrayCount = 0; + + std::vector resourceInfo; + std::string resURI = ""; + if (resourcePath) + { + resURI = resourcePath; + } + + IPCAStatus status = m_ocfFramework->CopyResourceInfo( + m_deviceId, resURI, resourceInfoType, resourceInfo); + if (status != IPCA_OK) + { + return status; + } + + return AllocateAndCopyStringVectorToArrayOfCharPointers( + resourceInfo, stringArray, stringArrayCount); +} + +IPCAStatus Device::GetProperties(CallbackInfo::Ptr callbackInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->SendCommandToDevice(m_deviceId, callbackInfo, nullptr); +} + +IPCAStatus Device::SetProperties(CallbackInfo::Ptr callbackInfo, OC::OCRepresentation* rep) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->SendCommandToDevice(m_deviceId, callbackInfo, rep); +} + +IPCAStatus Device::CreateResource(CallbackInfo::Ptr callbackInfo, OC::OCRepresentation* rep) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->SendCommandToDevice(m_deviceId, callbackInfo, rep); +} + +IPCAStatus Device::DeleteResource(CallbackInfo::Ptr callbackInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->SendCommandToDevice(m_deviceId, callbackInfo, nullptr); +} + +IPCAStatus Device::ObserveResource(CallbackInfo::Ptr callbackInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->SendCommandToDevice(m_deviceId, callbackInfo, nullptr); +} + +void Device::StopObserve(CallbackInfo::Ptr cbInfo) +{ + return m_ocfFramework->StopObserve(cbInfo); +} + +void Device::IsResourceObservable(const char* resourcePath, bool* isObservable) +{ + if (m_isClosed) + { + *isObservable = false; + return; + } + + m_ocfFramework->IsResourceObservable(m_deviceId, resourcePath, isObservable); +} + +IPCAStatus Device::Ping() +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->PingDevice(m_deviceId); +} + +IPCAStatus Device::GetLastPingTime(uint64_t& lastPingTime) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->GetLastPingTime(m_deviceId, lastPingTime); +} + +IPCAStatus Device::RequestAccess(CallbackInfo::Ptr callbackInfo, + CallbackInfo::Ptr passwordInputCallbackInfo) +{ + if (m_isClosed) + { + return IPCA_FAIL; + } + + return m_ocfFramework->RequestAccess(m_deviceId, callbackInfo, passwordInputCallbackInfo); +} diff --git a/resource/IPCA/src/inc/app.h b/resource/IPCA/src/inc/app.h new file mode 100644 index 0000000..59deed0 --- /dev/null +++ b/resource/IPCA/src/inc/app.h @@ -0,0 +1,182 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef APP_H_ +#define APP_H_ + +typedef std::shared_ptr CallbackInfoPtr; + +// Internal version of IPCAAppInfo. +struct IPCAAppInfoInternal +{ + IPCAUuid appId; + std::string appName; + std::string appSoftwareVersion; + std::string appCompanyName; +}; + +// Housekeeping for periodic discovery. +// The lastDiscoveryTime & discoveryCount are used to schedule more frequent discovery in +// the beginning. +typedef struct _DiscoveryDetails +{ + typedef std::shared_ptr<_DiscoveryDetails> Ptr; + std::vector resourceTypesToDiscover; // list of resource types + uint64_t lastDiscoveryTime; // last time discovery request was sent for this DiscoveryDetails. + size_t discoveryCount; // and how many times already. +} DiscoveryDetails; + +// One App object per IPCAOpen() +class App +{ + public: + App(const IPCAAppInfo* ipcaAppInfo, IPCAVersion ipcaVersion); + ~App(); + + // Application calls IPCAOpen()/IPCAClose(). + IPCAStatus Start(bool unitTestMode); + void Stop(); + + // Application calls IPCADiscoverDevices(). + IPCAStatus DiscoverDevices( + IPCADiscoverDeviceCallback callback, + const void* context, + const char* const* resourceTypeList, + int resourceTypeCount, + IPCAHandle* handle); + + // Application calls IPCAOpenDevice(). + IPCAStatus OpenDevice(const char* deviceId, IPCADeviceHandle* deviceHandle); + void CloseDevice(IPCADeviceHandle deviceHandle); + + // Application calls IPCAGetProperties(). + IPCAStatus GetProperties( + Device::Ptr device, + IPCAGetPropertiesComplete getPropertiesCb, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAHandle* handle); + + // Application calls IPCASetProperties(). + IPCAStatus SetProperties( + Device::Ptr device, + IPCAGetPropertiesComplete getPropertiesCb, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + OC::OCRepresentation* rep, + IPCAHandle* handle); + + // Application calls IPCAObserveResource(). + IPCAStatus ObserveResource( + Device::Ptr device, + IPCAResourceChangeCallback resourceChangeCb, + const void* context, + const char* resourcePath, + const char* resourceType, + IPCAHandle* handle); + + // Application calls IPCACreateResource(). + IPCAStatus CreateResource( + Device::Ptr device, + IPCACreateResourceComplete createResourceCb, + const void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + OC::OCRepresentation* rep, + IPCAHandle* handle); + + // Application calls IPCADeleteResource() + IPCAStatus DeleteResource( + Device::Ptr device, + IPCADeleteResourceComplete deleteResourceCb, + const void* context, + const char* resourcePath, + IPCAHandle* handle); + + /* Security/Ownership */ + IPCAStatus SetPasswordCallbacks( + IPCAProvidePasswordCallback inputCallback, + IPCADisplayPasswordCallback displayCallback, + void* context); + + IPCAStatus RequestAccess( + Device::Ptr device, + const char* resourcePath, + IPCARequestAccessCompletionCallback completionCallback, + void* context, + IPCAHandle* handle); + + + // Close handle returned in DiscoverDevices(), GetProperties(), SetProperties(), or + // ObserveResource(). + void CloseIPCAHandle(IPCAHandle handle); + + private: + std::mutex m_appMutex; + volatile bool m_isStopped; // set to true when Stop() is called. + + IPCAAppInfoInternal m_ipcaAppInfo; + IPCAVersion m_ipcaVersion; // IPCA version requested in the call to IPCAOpen(). + + // Object that implements callbacks to the app. + std::shared_ptr m_callback; + + // Devices this app opened. + std::map m_openedDevices; + + // Thread that performs periodic discovery. + std::thread m_appWorkerThread; + std::condition_variable m_discoveryThreadCV; + std::mutex m_appWorkerThreadMutex; + + // Create and register CallbackInfo with the Callback object. + IPCAStatus CreateAndRegisterNewCallbackInfo( + IPCAHandle* handle, + Device::Ptr device, + CallbackInfo::Ptr* cbInfo, + CallbackType cbType, + const void* context, + IPCADiscoverDeviceCallback discoverDeviceCallback, + const char* const* resourceTypeList, + int resourceTypeCount, + GenericAppCallback appCallback, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType); + + // Delete the CallbackInfo and unregister from Callback object list. + void DeleteAndUnregisterCallbackInfo(size_t mapKey); + + // Thread performing periodic discovery. + static void AppWorkerThread(App* app); + + // List of resource types to discover periodically. + // Key is cbInfo->mapKey of each IPCADiscoverDevices() request. + std::map m_discoveryList; + + // Password callback registration + InputPinCallbackHandle m_passwordInputCallbackHandle; + DisplayPinCallbackHandle m_passwordDisplayCallbackHandle; +}; +#endif // APP_H_ diff --git a/resource/IPCA/src/inc/callback.h b/resource/IPCA/src/inc/callback.h new file mode 100644 index 0000000..2f11231 --- /dev/null +++ b/resource/IPCA/src/inc/callback.h @@ -0,0 +1,215 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef CALLBACK_H_ +#define CALLBACK_H_ + +typedef enum +{ + CallbackType_Discovery = 1, + CallbackType_ResourceChange, + CallbackType_GetPropertiesComplete, + CallbackType_SetPropertiesComplete, + CallbackType_CreateResourceComplete, + CallbackType_DeleteResourceComplete, + CallbackType_PasswordInputCallback, + CallbackType_PasswordDisplayCallback, + Callbacktype_RequestAccessCompletionCallback, + CallbackType_InvalidType +} CallbackType; + +class App; +class Device; +class OC::OCResource; +typedef std::shared_ptr DevicePtr; + +// Structure contains information to make callbacks to app. +struct CallbackInfo +{ + public: + CallbackInfo() { /* std::cout << "+ CallbackInfo " << this << std::endl; */ } + ~CallbackInfo() { /* std::cout << "- CallbackInfo " << this << " mapKey: "; + std::cout << mapKey << std::endl; */ } + typedef std::shared_ptr Ptr; + size_t mapKey; // key to m_callbackInfoList map. Initialized in AddCallbackInfo(). + App* app; // the app that creates this callback. + DevicePtr device; // the device expected to respond to the callback. + CallbackType type; + union + { + IPCADiscoverDeviceCallback discoveryCallback; + IPCAResourceChangeCallback resourceChangeCallback; + IPCAGetPropertiesComplete getCallback; + IPCASetPropertiesComplete setCallback; + IPCACreateResourceComplete createResourceCallback; + IPCADeleteResourceComplete deleteResourceCallback; + IPCAProvidePasswordCallback passwordInputCallback; + IPCADisplayPasswordCallback passwordDisplayCallback; + IPCARequestAccessCompletionCallback requestAccessCompletionCallback; + }; + const void* callbackContext; // app's callback context. + std::vector resourceTypeList; // Parameter for Discovery. + std::string resourcePath; // Parameters for for Get, Set, Observe request. + std::string resourceInterface; + std::string resourceType; + size_t callbackInProgressCount; // Non zero if callback is in progress. + bool markedToBeRemoved; // Set to true when this object can't be removed in + // RemoveCallbackInfo(). It'll be removed opportunistically. + std::vector discoveredDevicesList; // List of device ids that were indicated to + // app with IPCA_DEVICE_DISCOVERED. + std::shared_ptr ocResource; // The OCResource this callback works with. + + uint64_t requestSentTimestamp; // when the request was sent to the server. +}; + +// Represent IPCAResourceChangeCallback, IPCAGetPropertiesComplete, IPCASetPropertiesComplete. +typedef void (IPCA_CALL *GenericAppCallback)(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); + +// One Callback object per App. One app per IPCAOpen(). +class Callback +{ + public: + typedef std::shared_ptr Ptr; + Callback(App* app); + ~Callback(); + + // Preparation to shut down. + // Function returns when there's no pending callback. + // No callback is allowed after returning from this call. + void Stop(); + + CallbackInfo::Ptr CreateCallbackInfo( + DevicePtr device, + CallbackType cbType, + const void* context, + IPCADiscoverDeviceCallback discoveryCallback,/* discovery */ + /* callback */ + const char* const* resourceTypeList, + int resourceTypeCount, + GenericAppCallback callback, /* get, set, observe, create & + delete resource callbacks */ + const char* resourcePath, + const char* resourceInterface, + const char* resourceType); + + // Password display or password input callback. + CallbackInfo::Ptr CreatePasswordCallbackInfo( + CallbackType cbType, + const void* context, + IPCAProvidePasswordCallback passwordInputCallback, + IPCADisplayPasswordCallback passwordDisplayCallback); + + // Auth completion callback. + CallbackInfo::Ptr CreateRequestAccessCompletionCallbackInfo( + DevicePtr device, + const void* context, + const char* resourceURI, + IPCARequestAccessCompletionCallback completionCallback); + + // Add CallbackInfo to the list of pending callbacks. + IPCAStatus AddCallbackInfo(CallbackInfo::Ptr cbInfo); + + // Return pointer to CallbackInfo matching mapKey. + CallbackInfo::Ptr GetCallbackInfo(size_t mapKey); + + // Return a pointer to PasswordInputCallback CallbackInfo + CallbackInfo::Ptr GetPasswordInputCallbackInfo(); + + // Remove the CallbackInfo matching mapKey. This function sets markedToBeRemoved if the + // callback is in the middle of calling back. + void RemoveCallbackInfo(size_t mapKey); + + // Complete the callback for expired CallbackInfo and remove them from the + // m_callbackInfoList. Caller receives a list of them. + void CompleteAndRemoveExpiredCallbackInfo(std::vector& cbInfoList); + + // Return a list of CallbackInfo object matching the type. + void GetCallbackInfoList(CallbackType type, std::vector& cbInfoList); + + // Device discovery related. + void DeviceDiscoveryCallback(bool deviceResponding, + bool newInfoLearntAboutDevice, + InternalDeviceInfo deviceInfo, + std::vector resourceTypes); + + // resource->get() callback. + void GetCallback(IPCAStatus status, + const OC::OCRepresentation& rep, + CallbackInfo::Ptr cbInfo); + + // resource->put() callback. + void SetCallback(IPCAStatus status, + const OC::OCRepresentation& rep, + CallbackInfo::Ptr cbInfo); + + // resource->observe() callback. + void ObserveCallback(IPCAStatus status, + const OC::OCRepresentation& rep, + CallbackInfo::Ptr cbInfo); + + // resource->deleteResource() callback. + void DeleteResourceCallback(IPCAStatus status, CallbackInfo::Ptr cbInfo); + + // Security/Auth + void PasswordInputCallback( + std::string deviceId, + IPCAOwnershipTransferType type, + char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr cbInfo); + + void PasswordDisplayCallback( + std::string deviceId, + IPCAOwnershipTransferType type, + const char* password, + CallbackInfo::Ptr cbInfo); + + void RequestAccessCompletionCallback(IPCAStatus status, CallbackInfo::Ptr cbInfo); + + private: + bool MatchAllRequiredResourceTypes(std::vector& requiredResourceTypes, + std::vector& deviceResourceTypes); + + private: + // Mutex for synchronization use. + std::mutex m_callbackMutex; + + // Mutex used for synchronizing discovery callback to app. + std::mutex m_discoverDeviceCallbackMutex; + + // Table of CallbackInfo. Key is autogenerated. + std::atomic m_nextKey; // next key for the m_callbackInfoList map. + std::map m_callbackInfoList; // List of expected callbacks. + App* m_app; // Callback object is per app. + volatile bool m_stopCalled; // Set to true when Stop() is called. + + // Number of expired callbacks in progress. + size_t m_expiredCallbacksInprogress; + + // Indicate that callback is in progress for callbackInfo matching mapKey. + // Return false if the callback is already cancelled by app. + bool SetCallbackInProgress(size_t mapKey); + + // Indicate that callback is not in progress. + bool ClearCallbackInProgress(size_t mapKey); +}; + +#endif // CALLBACK_H_ diff --git a/resource/IPCA/src/inc/common.h b/resource/IPCA/src/inc/common.h new file mode 100644 index 0000000..8ed2497 --- /dev/null +++ b/resource/IPCA/src/inc/common.h @@ -0,0 +1,92 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef COMMON_H_ +#define COMMON_H_ + +#if defined(_MSC_VER) +#define ARRAY_SIZE(a) _countof(a) +#else +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + +// Copy the source std string to dest. +bool CopyStringToBufferAllowTruncate(const std::string& source, char* dest, size_t destSize); +bool CopyStringToFlatBuffer(const std::string& source, char* dest, size_t* destBufferSize); + +// Allocate the dest buffer and copy the source std string to dest. +IPCAStatus AllocateAndCopyStringToFlatBuffer(const std::string& source, char** dest); + +// Allocate the dest buffer of string and copy from source std string to dest. +IPCAStatus AllocateAndCopyStringVectorToArrayOfCharPointers( + const std::vector& source, + char*** dest, + size_t* count); + +// Free array of string. +void FreeArrayOfCharPointers(char** array, size_t count); + +// Return true if string is in list. +bool IsStringInList(const std::string& string, const std::vector& list); + +// Add strings in newList that is not in targetList. Return true if there's new entry added. +bool AddNewStringsToTargetList(const std::vector& newList, + std::vector& targetList); + +// Same information as IPCADeviceInfo in ipca.h but with std::string. +typedef struct +{ + std::string deviceId; + std::string platformIndependentId; + std::vector deviceUris; + std::string deviceName; + std::string deviceSoftwareVersion; + std::vector dataModelVersions; +} InternalDeviceInfo; + +// Same information as IPCAPlatformInfo in ipca.h, but with std::string. +typedef struct +{ + std::string platformId; + std::string manufacturerName; + std::string manufacturerURL; + std::string modelNumber; + std::string manufacturingDate; + std::string platformVersion; + std::string osVersion; + std::string hardwareVersion; + std::string firmwareVersion; + std::string manufacturerSupportURL; + std::string referenceTime; +} InternalPlatformInfo; + +// Security information +typedef struct +{ + bool subowner; + bool isStarted; + std::shared_ptr device; + std::thread requestAccessThread; + std::mutex requestAccessThreadMutex; + std::condition_variable requestAccessThreadCV; +} InternalSecurityInfo; + +// Debug helpers +void PrintOCRep(const OC::OCRepresentation& rep, size_t marginDepth = 0); +#endif // COMMON_H_ diff --git a/resource/IPCA/src/inc/device.h b/resource/IPCA/src/inc/device.h new file mode 100644 index 0000000..29d1763 --- /dev/null +++ b/resource/IPCA/src/inc/device.h @@ -0,0 +1,109 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef DEVICE_H_ +#define DEVICE_H_ + +class App; +class OCFFramework; + +/* Type of resource info requested in GetResourceInfo() */ +enum class ResourceInfoType +{ + ResourceType, + ResourceInterface +}; + +class Device +{ + public: + typedef std::shared_ptr Ptr; + + Device(const char* deviceId, OCFFramework* ocf, App* app); + ~Device(); + + App* GetApp() { return m_app; } + std::string GetDeviceId() { return m_deviceId; } + + // Make sure OCFFramework has found this device and let it know that it's opened. + IPCAStatus Open(); + IPCAStatus Close(); + + IPCAStatus GetDeviceInfo(IPCADeviceInfo** callerDeviceInfo); + IPCAStatus GetPlatformInfo(IPCAPlatformInfo** callerPlatformInfo); + + // Return array of resource paths (examples of resource paths: "/oic/d", "/oem/elevator"). + IPCAStatus GetResourcePathList(const std::string& resourceInterface, + const std::string& resourceType, + char*** resourcePathList, + size_t* resourcePathCount); + + // Depending on resourceInfoType, return an array of: + // Resource types (examples of resource types: "oic.wk.d", "x.oem.elevator.status"). + // Resource interfaces (e.g., "oic.if.baseline", "oic.if.r"). + IPCAStatus GetResourceInfo(const char* resourcePath, + ResourceInfoType resourceInfoType, + char*** stringArray, + size_t* stringCount); + + // Send Get request to device. + IPCAStatus GetProperties(CallbackInfo::Ptr callbackInfo); + + // Send Post request to device. + IPCAStatus SetProperties(CallbackInfo::Ptr callbackInfo, OC::OCRepresentation* rep); + + // Subscribe to resource change notifications. + IPCAStatus ObserveResource(CallbackInfo::Ptr callbackInfo); + void StopObserve(CallbackInfo::Ptr callbackInfo); + void IsResourceObservable(const char* resourcePath, bool* isObservable); + + // Send Post for IPCA_RELATIVE_RESOURCE_PATH and Put for IPCA_EXPLICIT_RESOURCE_PATH. + IPCAStatus CreateResource(CallbackInfo::Ptr callbackInfo, OC::OCRepresentation* rep); + + // Send Delete request to device. + IPCAStatus DeleteResource(CallbackInfo::Ptr callbackInfo); + + // Ping device to determine that it's still there. + // If device responds, OCFFramework will update the device's lastResponseTimeToDiscovery. + IPCAStatus Ping(); + IPCAStatus GetLastPingTime(uint64_t& lastPingTime); + + // Request access to a device + IPCAStatus RequestAccess(CallbackInfo::Ptr callbackInfo, + CallbackInfo::Ptr passwordInputCallbackInfo); + + private: + IPCAStatus ToArrayOfCharPointers(std::vector& stringList, + char*** callerPointer, size_t* arraySize); + + private: + std::string m_deviceId; // from IPCAOpenDevice() + App* m_app; + OCFFramework* m_ocfFramework; + bool m_isClosed; // set to true after Close(). +}; + +// returned as IPCAHandle to the app. +typedef struct +{ + App* app; + Device::Ptr device; +} DeviceWrapper; + +#endif // DEVICE_H_ diff --git a/resource/IPCA/src/inc/ipcainternal.h b/resource/IPCA/src/inc/ipcainternal.h new file mode 100644 index 0000000..8c01fae --- /dev/null +++ b/resource/IPCA/src/inc/ipcainternal.h @@ -0,0 +1,49 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef IPCA_INTERNAL_H_ +#define IPCA_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "OCPlatform.h" +#include "OCApi.h" +#include "OCProvisioningManager.hpp" +#include "logger.h" + +#include "oic_malloc.h" +#include "ipca.h" +#include "common.h" +#include "callback.h" +#include "device.h" +#include "ocfframework.h" +#include "app.h" + +#endif // IPCA_INTERNAL_H_ diff --git a/resource/IPCA/src/inc/ocfframework.h b/resource/IPCA/src/inc/ocfframework.h new file mode 100644 index 0000000..dbc1a1f --- /dev/null +++ b/resource/IPCA/src/inc/ocfframework.h @@ -0,0 +1,280 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef OCF_FRAMEWORK_H_ +#define OCF_FRAMEWORK_H_ + +using namespace OC; + +struct IPCAAppInfoInternal; + +// Map's key is resource path obtained from resource->uri() (e.g.: /oic/d). +typedef std::map> ResourceMap; + +// Some information about an OCF device. +typedef struct DeviceDetails +{ + typedef std::shared_ptr Ptr; + + // Value is set to value returned by OICGetCurrentTime(TIME_IN_MS). + uint64_t lastResponseTimeToDiscovery; + + // Indicate device not responding 1 time. + bool deviceNotRespondingIndicated; + + // How many IPCAOpenDevice() on this device. + size_t deviceOpenCount; + + // Timestamp of final close. + uint64_t lastCloseDeviceTime; + + // Timestamp of last ping call to device. + uint64_t lastPingTime; + + // Device ID in OnResourceFound(). + std::string deviceId; + + // Device Uri in OnResourceFound(). A device can have multiple device uris + // (e.g. ipv4 and ipv6). + std::vector deviceUris; + + // Device info + size_t deviceInfoRequestCount; // Number of requests sent for /oic/d + bool deviceInfoAvailable; + InternalDeviceInfo deviceInfo; + + // Platform info + size_t platformInfoRequestCount; // Number of requests sent for /oic/p + bool platformInfoAvailable; + InternalPlatformInfo platformInfo; + + // Maintenance resource. + size_t maintenanceResourceRequestCount; // Number of requests sent for /oic/mnt + bool maintenanceResourceAvailable; // Set to true if device returns resource for rt: oic.wk.mnt. + + // Security Info + bool securityInfoAvailable; + InternalSecurityInfo securityInfo; + + // List of resources, not necessarily complete list, depending on the resource type requested + // in the discovery. + ResourceMap resourceMap; + + // List of resource types, not necessarily a complete list, depending on the resource type + // in discovery. + std::vector discoveredResourceTypes; + + // List of resource interfaces, not necessarily a complete list, depending on the resource + // type in discovery. + std::vector discoveredResourceInterfaces; +} DeviceDetails; + +typedef struct RequestAccessContext +{ + std::string deviceId; + OCFFramework* ocfFramework; + CallbackInfo::Ptr callbackInfo; + CallbackInfo::Ptr passwordInputCallbackInfo; +} RequestAccessContext; + +// Implements OCF related functions. +class OCFFramework +{ + public: + OCFFramework(); + ~OCFFramework(); + + // When app calls IPCAOpen()/IPCAClose(). + IPCAStatus Start(const IPCAAppInfoInternal& ipcaAppInfo, bool isUnitTestMode); + IPCAStatus Stop(InputPinCallbackHandle passwordInputCallbackHandle, + DisplayPinCallbackHandle passwordDisplayCallbackHandle); + + IPCAStatus RegisterAppCallbackObject(Callback::Ptr cb); + void UnregisterAppCallbackObject(Callback::Ptr cb); + + // Discover on network, resources that match resource type. + IPCAStatus DiscoverResources(std::vector& resourceTypeList); + + // Discover all the resources for specific device. + IPCAStatus DiscoverAllResourcesGivenHost(std::string deviceUri); + + // App calls IPCAOpenDevice() on deviceId. + IPCAStatus IPCADeviceOpenCalled(std::string& deviceId); + IPCAStatus IPCADeviceCloseCalled(std::string& deviceId); + + // Information pertaining to the device. + IPCAStatus CopyDeviceInfo(std::string& deviceId, IPCADeviceInfo** callerDeviceInfo); + IPCAStatus CopyPlatformInfo(std::string& deviceId, IPCAPlatformInfo** callerPlatformInfo); + static void FreeDeviceInfo(IPCADeviceInfo* deviceInfo); + static void FreePlatformInfo(IPCAPlatformInfo* platformInfo); + + // Resource and resource information. + IPCAStatus CopyResourcePaths( + const std::string& resourceInterface, + const std::string& resourceType, + std::string& deviceId, + std::vector& resourcePathList); + + IPCAStatus CopyResourceInfo( + const std::string& deviceId, + const std::string& resourcePath, + ResourceInfoType resourceInfoType, + std::vector& resourceInfo); + + // Get/Set/Observe requests. + IPCAStatus OCFFramework::SendCommandToDevice( + std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + OCRepresentation* rep); + + IPCAStatus GetProperties(std::string& deviceId, CallbackInfo::Ptr callbackInfo); + + IPCAStatus SetProperties(std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + OCRepresentation* rep); + + IPCAStatus CreateResource(std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + OCRepresentation* rep); + + IPCAStatus StartObserve(std::string& deviceId, CallbackInfo::Ptr callbackInfo); + void StopObserve(CallbackInfo::Ptr callbackInfo); + + void IsResourceObservable(std::string& deviceId, + const char* resourcePath, + bool* isObservable); + + // Ping specific device for the given resource type. + // The function sends oic/res request with rt = oic.wk.d. + IPCAStatus PingDevice(std::string& deviceId); + IPCAStatus GetLastPingTime(std::string& deviceId, uint64_t& lastPingTime); + + // Security requests. + IPCAStatus SetInputPasswordCallback(CallbackInfo::Ptr callbackInfo, + InputPinCallbackHandle* passwordInputCallbackHandle); + + IPCAStatus SetDisplayPasswordCallback(CallbackInfo::Ptr callbackInfo, + DisplayPinCallbackHandle* passwordDisplayCallbackHandle); + + IPCAStatus RequestAccess(std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + CallbackInfo::Ptr passwordInputCallbackInfo); + + private: + // Callback from OCF for OCResource->get() + void OnObserve(const HeaderOptions headerOptions, + const OCRepresentation &rep, + const int &eCode, + const int &sequenceNumber, + CallbackInfo::Ptr callbackInfo); + + void OnGet(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode, + CallbackInfo::Ptr callbackInfo); + + void OnDelete(const HeaderOptions& headerOptions, + const int eCode, + CallbackInfo::Ptr callbackInfo); + + void OnPostPut(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode, + CallbackInfo::Ptr callbackInfo); + + // Callback from OCF when a resource is found. + void OnResourceFound(std::shared_ptr resource); + + // Callback from OCF for requested device info. + void OnDeviceInfoCallback(const OCRepresentation& rep); + + // Callback from OCF for requested platform info. + void OnPlatformInfoCallback(const OCRepresentation& rep); + + // Callback from OCF for MOT completion + void OnMultipleOwnershipTransferCompleteCallback(PMResultList_t* result, + bool error, + std::string deviceId, + CallbackInfo::Ptr callbackInfo); + + // Callback from OCF for password input + void OnPasswordInputCallback(OicUuid_t deviceId, + char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr callbackInfo); + + // Callback from OCF to display a password + void OnPasswordDisplayCallback(char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr callbackInfo); + + // A device is formed when all its device info and platform info are known. + IPCAStatus GetCommonResources(DeviceDetails::Ptr deviceDetails); + + // See m_workerThread variable below. + static void WorkerThread(OCFFramework* ocfFramework); + + // Entry point for the thread that will request access to a device. + static void RequestAccessWorkerThread(RequestAccessContext* requestContext); + + // Get DeviceDetails for devieId. + IPCAStatus FindDeviceDetails(const std::string& deviceId, + DeviceDetails::Ptr& deviceDetails); + + // Get the first OCResource that implements the resource type. + std::shared_ptr FindOCResource(const DeviceDetails::Ptr& deviceDetails, + const std::string& resourcePath, + const std::string& resourceType); + + // Cleanup RequestAccess calls + void CleanupRequestAccessDevices(); + + // Some helper functions to help debugging. + void DebugOutputOCFDevices(); + void DebugOutputOCRep(const OCRepresentation& rep); + + private: + // Lock for sync access to protected members in OCFFramework. + std::recursive_mutex m_OCFFrameworkMutex; + + // A list of devices and their resources. + // Key to the map is device ID (which is a UUID) + std::map m_OCFDevices; + + // Fast look up for DeviceDetails given device's URI. + // Key to the map is device URI (e.g., "coap://[fe80::5828:93a8:d53e:4222%7]:62744"). + // A device may have multiple URIs (e.g., ipv4 and ipv6). + std::map m_OCFDevicesIndexedByDeviceURI; + + // A list of RequestAccess contexts. + // Key to the map is the device ID (which is a UUID) + std::map m_OCFRequestAccessContexts; + + // One Callback per App. One App per IPCAOpen(). + std::vector m_callbacks; + std::thread m_workerThread; + std::condition_variable m_workerThreadCV; + std::mutex m_workerThreadMutex; + + // Synchronize Start()/Stop() + std::mutex m_startStopMutex; + bool m_isStarted; + bool m_isStopping; +}; +#endif // OCF_FRAMEWORK_H_ diff --git a/resource/IPCA/src/ipca.cpp b/resource/IPCA/src/ipca.cpp new file mode 100644 index 0000000..4e39e6b --- /dev/null +++ b/resource/IPCA/src/ipca.cpp @@ -0,0 +1,427 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "oic_malloc.h" +#include "ipca.h" +#include "ipcainternal.h" + +#define OCFFactoryReset "fr" +#define OCFReboot "rb" + +// As in proc there's no reason for multiple IPCAOpen(). +// There's no ref count for g_app. Therefore must hold g_ipcaAppMutex when using g_app. +App* g_app = nullptr; +std::mutex g_ipcaAppMutex; + +bool g_unitTestMode = false; + +IPCAStatus IPCA_CALL IPCAOpen(const IPCAAppInfo* ipcaAppInfo, + IPCAVersion ipcaVersion, + IPCAAppHandle* ipcaAppHandle) +{ + std::lock_guard lock(g_ipcaAppMutex); + + if (ipcaVersion != IPCA_VERSION_1) + { + return IPCA_INVALID_ARGUMENT; + } + + if (g_app != nullptr) + { + return IPCA_ALREADY_OPENED; + } + + if (ipcaAppInfo == nullptr || + ipcaAppInfo->appName == nullptr || + ipcaAppInfo->appSoftwareVersion == nullptr || + ipcaAppInfo->appCompanyName == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + g_app= new App(ipcaAppInfo, ipcaVersion); + + if (g_app == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + IPCAStatus status = g_app->Start(g_unitTestMode); + if (status != IPCA_OK) + { + delete g_app; + g_app = nullptr; + return status; + } + + *ipcaAppHandle = reinterpret_cast(g_app); + return IPCA_OK; +} + +void IPCA_CALL IPCAClose(IPCAAppHandle ipcaAppHandle) +{ + App* app = reinterpret_cast(ipcaAppHandle); + std::lock_guard lock(g_ipcaAppMutex); + if (g_app == nullptr) + { + return; + } + + app->Stop(); + delete app; + g_app = nullptr; +} + +IPCAStatus IPCA_CALL IPCADiscoverDevices(IPCAAppHandle ipcaAppHandle, + IPCADiscoverDeviceCallback callback, + void* context, + const char* const* resourceTypeList, + int resourceTypeCount, + IPCAHandle* handle) +{ + App* app = reinterpret_cast(ipcaAppHandle); + return app->DiscoverDevices(callback, context, resourceTypeList, resourceTypeCount, handle); +} + +IPCAStatus IPCA_CALL IPCAOpenDevice(IPCAAppHandle ipcaAppHandle, + const char* deviceId, + IPCADeviceHandle* deviceHandle) +{ + App* app = reinterpret_cast(ipcaAppHandle); + return app->OpenDevice(deviceId, deviceHandle); +} + +void IPCA_CALL IPCACloseDevice(IPCADeviceHandle deviceHandle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + App* app = deviceWrapper->device->GetApp(); + app->CloseDevice(deviceHandle); +} + +IPCAStatus IPCAGetDeviceInfo(IPCADeviceHandle deviceHandle, IPCADeviceInfo** deviceInfo) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->device->GetDeviceInfo(deviceInfo); +} + +void IPCAFreeDeviceInfo(IPCADeviceInfo* deviceInfo) +{ + OCFFramework::FreeDeviceInfo(deviceInfo); +} + +IPCAStatus IPCAGetPlatformInfo(IPCADeviceHandle deviceHandle, IPCAPlatformInfo** platformInfo) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->device->GetPlatformInfo(platformInfo); +} + +void IPCAFreePlatformInfo(IPCAPlatformInfo* platformInfo) +{ + OCFFramework::FreePlatformInfo(platformInfo); +} + +IPCAStatus IPCA_CALL IPCAGetResources(IPCADeviceHandle deviceHandle, + const char* resourceInterface, + const char* resourceType, + char*** resourcePathList, + size_t* resourcePathCount) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->device->GetResourcePathList( + std::string(resourceInterface ? resourceInterface : ""), + std::string(resourceType ? resourceType : ""), + resourcePathList, + resourcePathCount); +} + +IPCAStatus IPCA_CALL IPCAGetResourceTypes(IPCADeviceHandle deviceHandle, + const char* resourcePath, + char*** resourceTypeList, + size_t* resourceTypeCount) +{ + DeviceWrapper* deviceWrapper = (DeviceWrapper*)deviceHandle; + return deviceWrapper->device->GetResourceInfo(resourcePath, + ResourceInfoType::ResourceType, resourceTypeList, resourceTypeCount); +} + +IPCAStatus IPCA_CALL IPCAGetResourceInterfaces(IPCADeviceHandle deviceHandle, + const char* resourcePath, + char*** resourceInterfaceList, + size_t* resourceInterfaceCount) +{ + DeviceWrapper* deviceWrapper = (DeviceWrapper*)deviceHandle; + return deviceWrapper->device->GetResourceInfo(resourcePath, + ResourceInfoType::ResourceInterface, resourceInterfaceList, resourceInterfaceCount); +} + +void IPCA_CALL IPCAFreeStringArray(char** stringArray, size_t stringCount) +{ + FreeArrayOfCharPointers(stringArray, stringCount); +} + +IPCAStatus IPCA_CALL IPCAGetProperties(IPCADeviceHandle deviceHandle, + IPCAGetPropertiesComplete getPropertiesCb, + void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAHandle* handle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->GetProperties( + deviceWrapper->device, + getPropertiesCb, + context, + resourcePath, + resourceInterface, + resourceType, + handle); +} + +IPCAStatus IPCA_CALL IPCASetProperties(IPCADeviceHandle deviceHandle, + IPCASetPropertiesComplete setPropertiesCb, + void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAPropertyBagHandle propertyBagHandle, + IPCAHandle* handle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->SetProperties( + deviceWrapper->device, + setPropertiesCb, + context, + resourcePath, + resourceInterface, + resourceType, + (OC::OCRepresentation*)propertyBagHandle, + handle); +} + +void IPCA_CALL IPCAIsResourceObservable(IPCADeviceHandle deviceHandle, + const char* resourcePath, + bool* isObservable) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + deviceWrapper->device->IsResourceObservable(resourcePath, isObservable); + return; +} + +IPCAStatus IPCA_CALL IPCAObserveResource(IPCADeviceHandle deviceHandle, + IPCAResourceChangeCallback resourceChangeCb, + void* context, + const char* resourcePath, + const char* resourceType, + IPCAHandle* handle) +{ + if (handle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->ObserveResource( + deviceWrapper->device, + resourceChangeCb, + context, + resourcePath, + resourceType, + handle); +} + +IPCAStatus IPCA_CALL IPCACreateResource(IPCADeviceHandle deviceHandle, + IPCACreateResourceComplete createResourceCb, + void* context, + const char* resourcePath, + const char* resourceInterface, + const char* resourceType, + IPCAPropertyBagHandle propertyBagHandle, + IPCAHandle* handle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->CreateResource( + deviceWrapper->device, + createResourceCb, + context, + resourcePath, + resourceInterface, + resourceType, + (OC::OCRepresentation*)propertyBagHandle, + handle); +} + +IPCAStatus IPCA_CALL IPCADeleteResource(IPCADeviceHandle deviceHandle, + IPCADeleteResourceComplete deleteResourceCb, + void* context, + const char* resourcePath, + IPCAHandle* handle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->DeleteResource( + deviceWrapper->device, + deleteResourceCb, + context, + resourcePath, + handle); +} + +void IPCA_CALL IPCACloseHandle(IPCAHandle handle) +{ + std::lock_guard lock(g_ipcaAppMutex); + if (g_app != nullptr) + { + g_app->CloseIPCAHandle(handle); + } +} + +typedef struct +{ + std::mutex syncMutex; + std::condition_variable condVar; + IPCAStatus result; +} AsyncContext; + +void AsyncCallback(IPCAStatus result, void* context, IPCAPropertyBagHandle propertyBagHandle) +{ + OC_UNUSED(propertyBagHandle); + + AsyncContext* async = reinterpret_cast(context); + async->result = result; + async->condVar.notify_all(); +} + +IPCAStatus IPCA_CALL IPCAFactoryReset(IPCADeviceHandle deviceHandle) +{ + IPCAStatus status; + + AsyncContext factoryResetContext; + std::unique_lock lock { factoryResetContext.syncMutex }; + + IPCAPropertyBagHandle propertyBagHandle; + status = IPCAPropertyBagCreate(&propertyBagHandle); + if (status != IPCA_OK) + { + return status; + } + + status = IPCAPropertyBagSetValueBool(propertyBagHandle, OCFFactoryReset, true); + if (status != IPCA_OK) + { + IPCAPropertyBagDestroy(propertyBagHandle); + return status; + } + + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + status = deviceWrapper->app->SetProperties( + deviceWrapper->device, + &AsyncCallback, + &factoryResetContext, + nullptr, + nullptr, + OC_RSRVD_RESOURCE_TYPE_MAINTENANCE, + (OC::OCRepresentation*)propertyBagHandle, + nullptr); + + if (status == IPCA_OK) + { + factoryResetContext.condVar.wait_for(lock, std::chrono::milliseconds{ INT_MAX }); + IPCAPropertyBagDestroy(propertyBagHandle); + return factoryResetContext.result; + } + + IPCAPropertyBagDestroy(propertyBagHandle); + return status; +} + +IPCAStatus IPCA_CALL IPCAReboot(IPCADeviceHandle deviceHandle) +{ + IPCAStatus status; + + AsyncContext rebootContext; + std::unique_lock lock { rebootContext.syncMutex }; + + IPCAPropertyBagHandle propertyBagHandle; + status = IPCAPropertyBagCreate(&propertyBagHandle); + if (status != IPCA_OK) + { + return status; + } + + status = IPCAPropertyBagSetValueBool(propertyBagHandle, OCFReboot, true); + if (status != IPCA_OK) + { + IPCAPropertyBagDestroy(propertyBagHandle); + return status; + } + + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + status = deviceWrapper->app->SetProperties( + deviceWrapper->device, + &AsyncCallback, + &rebootContext, + nullptr, + nullptr, + OC_RSRVD_RESOURCE_TYPE_MAINTENANCE, + (OC::OCRepresentation*)propertyBagHandle, + nullptr); + + if (status == IPCA_OK) + { + rebootContext.condVar.wait_for(lock, std::chrono::milliseconds{ INT_MAX }); + IPCAPropertyBagDestroy(propertyBagHandle); + return rebootContext.result; + } + + IPCAPropertyBagDestroy(propertyBagHandle); + return status; +} + +IPCAStatus IPCA_CALL IPCASetPasswordCallbacks(IPCAAppHandle ipcaAppHandle, + IPCAProvidePasswordCallback inputCallback, + IPCADisplayPasswordCallback displayCallback, + void* context) +{ + App* app = reinterpret_cast(ipcaAppHandle); + return app->SetPasswordCallbacks(inputCallback, displayCallback, context); +} + +IPCAStatus IPCA_CALL IPCARequestAccess(IPCADeviceHandle deviceHandle, + const char* resourcePath, + IPCARequestAccessCompletionCallback completionCallback, + void* context, + IPCAHandle* handle) +{ + DeviceWrapper* deviceWrapper = reinterpret_cast(deviceHandle); + return deviceWrapper->app->RequestAccess( + deviceWrapper->device, + resourcePath, + completionCallback, + context, + handle); +} + +/*********************/ +/* Utility functions */ +/*********************/ + +void IPCA_CALL IPCASetUnitTestMode() +{ + g_unitTestMode = true; +} diff --git a/resource/IPCA/src/ipca.def b/resource/IPCA/src/ipca.def new file mode 100644 index 0000000..6c6c410 --- /dev/null +++ b/resource/IPCA/src/ipca.def @@ -0,0 +1,68 @@ +LIBRARY ipca + +EXPORTS + +IPCAClose +IPCACloseDevice +IPCACloseHandle +IPCACreateResource +IPCADiscoverDevices +IPCADeleteResource +IPCAFactoryReset +IPCAFreeDeviceInfo +IPCAFreePlatformInfo +IPCAFreeStringArray +IPCAGetProperties +IPCAGetDeviceInfo +IPCAGetResourceInterfaces +IPCAGetResourceTypes +IPCAGetPlatformInfo +IPCAGetResources +IPCAIsResourceObservable +IPCAObserveResource +IPCAOpen +IPCAOpenDevice +IPCAReboot +IPCASetProperties + +; Property bag functions + +IPCAPropertyBagCreate +IPCAPropertyBagDestroy + +IPCAPropertyBagFreeBoolArray +IPCAPropertyBagFreeDoubleArray +IPCAPropertyBagFreeIntArray +IPCAPropertyBagFreeIPCAValueTypeArray +IPCAPropertyBagFreeString +IPCAPropertyBagFreeStringArray +IPCAPropertyBagFreePropertyBagArray + +IPCAPropertyBagGetAllKeyValuePairs +IPCAPropertyBagGetResourcePath +IPCAPropertyBagGetValueBool +IPCAPropertyBagGetValueBoolArray +IPCAPropertyBagGetValueDouble +IPCAPropertyBagGetValueDoubleArray +IPCAPropertyBagGetValueInt +IPCAPropertyBagGetValueIntArray +IPCAPropertyBagGetValuePropertyBag +IPCAPropertyBagGetValuePropertyBagArray +IPCAPropertyBagGetValueString +IPCAPropertyBagGetValueStringArray + +IPCAPropertyBagSetValueBool +IPCAPropertyBagSetValueBoolArray +IPCAPropertyBagSetValueDouble +IPCAPropertyBagSetValueDoubleArray +IPCAPropertyBagSetValueInt +IPCAPropertyBagSetValueIntArray +IPCAPropertyBagSetValuePropertyBag +IPCAPropertyBagSetValuePropertyBagArray +IPCAPropertyBagSetValueString +IPCAPropertyBagSetValueStringArray + +; Security + +IPCASetPasswordCallbacks +IPCARequestAccess diff --git a/resource/IPCA/src/ipcavariant.cpp b/resource/IPCA/src/ipcavariant.cpp new file mode 100644 index 0000000..3cf955f --- /dev/null +++ b/resource/IPCA/src/ipcavariant.cpp @@ -0,0 +1,649 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include + +#include "oic_malloc.h" +#include "ipca.h" +#include "ipcainternal.h" + +#define TAG "IPCA_Variant" + +IPCAStatus IPCAPropertyBagCreate(IPCAPropertyBagHandle* propertyBagHandle) +{ + OC::OCRepresentation* rep = new OC::OCRepresentation(); + + if (rep == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + *propertyBagHandle = reinterpret_cast(rep); + return IPCA_OK; +} + +void IPCAPropertyBagDestroy(IPCAPropertyBagHandle propertyBagHandle) +{ + delete(reinterpret_cast(propertyBagHandle)); +} + +template +IPCAStatus IPCAPropertySetValue(IPCAPropertyBagHandle propertyBagHandle, const char* key, _T value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + try + { + (reinterpret_cast(propertyBagHandle))->setValue(key, value); + return IPCA_OK; + } + catch (std::exception &e) + { + OC_UNUSED(e); + OIC_LOG_V(WARNING, TAG, e.what()); + return IPCA_FAIL; + } +} + +IPCAStatus IPCAPropertyBagSetValueInt(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int value) +{ + return IPCAPropertySetValue(propertyBagHandle, key, value); +} + +IPCAStatus IPCAPropertyBagSetValueDouble(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double value) +{ + return IPCAPropertySetValue(propertyBagHandle, key, value); +} + +IPCAStatus IPCAPropertyBagSetValueBool(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + bool value) +{ + return IPCAPropertySetValue(propertyBagHandle, key, value); +} + +IPCAStatus IPCAPropertyBagSetValueString(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const char* value) +{ + std::string valueString = value; + return IPCAPropertySetValue(propertyBagHandle, key, valueString); +} + +IPCAStatus IPCAPropertyBagSetValuePropertyBag(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const IPCAPropertyBagHandle value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* ocRep = reinterpret_cast(propertyBagHandle); + + try + { + (*ocRep)[key] = *(reinterpret_cast(value)); + return IPCA_OK; + } + catch (std::exception &e) + { + OC_UNUSED(e); + OIC_LOG_V(WARNING, TAG, e.what()); + return IPCA_FAIL; + } +} + +template +IPCAStatus IPCAPropertyBagSetValueArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const _T* valueArray, + size_t arrayCount) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* ocRep = reinterpret_cast(propertyBagHandle); + + std::vector<_T> array; + for (int i = 0; i < arrayCount; i++) + { + array.insert(array.end(), valueArray[i]); + } + + try + { + (*ocRep)[key] = array; + return IPCA_OK; + } + catch (std::exception &e) + { + OC_UNUSED(e); + OCLog(WARNING, TAG, e.what()); + return IPCA_FAIL; + } +} + +IPCAStatus IPCAPropertyBagSetValueIntArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const int* valueArray, + size_t arrayCount) +{ + return IPCAPropertyBagSetValueArray(propertyBagHandle, key, valueArray, arrayCount); +} + +IPCAStatus IPCAPropertyBagSetValueDoubleArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const double* valueArray, + size_t arrayCount) +{ + return IPCAPropertyBagSetValueArray(propertyBagHandle, key, valueArray, arrayCount); + +} + +IPCAStatus IPCAPropertyBagSetValueBoolArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const bool* valueArray, + size_t arrayCount) +{ + return IPCAPropertyBagSetValueArray(propertyBagHandle, key, valueArray, arrayCount); +} + +IPCAStatus IPCAPropertyBagSetValueStringArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const char** valueArray, + size_t arrayCount) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* ocRep = reinterpret_cast(propertyBagHandle); + + std::vector array; + for (int i = 0; i < arrayCount; i++) + { + array.insert(array.end(), valueArray[i]); + } + + try + { + (*ocRep)[key] = array; + return IPCA_OK; + } + catch (std::exception &e) + { + OC_UNUSED(e); + OIC_LOG_V(WARNING, TAG, e.what()); + return IPCA_FAIL; + } +} + +IPCAStatus IPCAPropertyBagSetValuePropertyBagArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + const IPCAPropertyBagHandle* valueArray, + size_t valueCount) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* ocRep = reinterpret_cast(propertyBagHandle); + + std::vector ocRepArray; + for (int i = 0; i < valueCount; i++) + { + ocRepArray.insert(ocRepArray.end(), + *(reinterpret_cast((valueArray[i])))); + } + + try + { + (*ocRep)[key] = ocRepArray; + return IPCA_OK; + } + catch (std::exception &e) + { + OC_UNUSED(e); + OIC_LOG_V(WARNING, TAG, e.what()); + return IPCA_FAIL; + } +} + +template +IPCAStatus AllocateAndCopyTypeVectorToArrayOfType(std::vector<_T> array, _T** dest, size_t* count) +{ + _T* buffer; + size_t arraySize = array.size(); + buffer = static_cast<_T*>(OICCalloc(arraySize, sizeof(_T))); + if (buffer == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + int i = 0; + for (auto x : array) + { + buffer[i] = x; + i++; + } + + *dest = buffer; + *count = arraySize; + + return IPCA_OK; +} + +template +IPCAStatus IPCAPropertyBagGetValueArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, _T** value, + size_t* valueCount) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + std::vector<_T> array; + + if ((reinterpret_cast(propertyBagHandle))->getValue(key, array) == false) + { + return IPCA_FAIL; + } + + return AllocateAndCopyTypeVectorToArrayOfType(array, value, valueCount); +} + +IPCAStatus IPCAPropertyBagGetValueIntArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int** value, + size_t* valueCount) +{ + return IPCAPropertyBagGetValueArray(propertyBagHandle, key, value, valueCount); +} + +void IPCAPropertyBagFreeIntArray(int* valueArray) +{ + OICFree((void*)valueArray); +} + +IPCAStatus IPCAPropertyBagGetValueDoubleArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double** value, + size_t* valueCount) +{ + return IPCAPropertyBagGetValueArray(propertyBagHandle, key, value, valueCount); +} + +void IPCAPropertyBagFreeDoubleArray(double* valueArray) +{ + OICFree((void*)valueArray); +} + +IPCAStatus IPCAPropertyBagGetValueBoolArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + bool** value, + size_t* valueCount) +{ + return IPCAPropertyBagGetValueArray(propertyBagHandle, key, value, valueCount); +} + +void IPCAPropertyBagFreeBoolArray(bool* valueArray) +{ + OICFree((void*)valueArray); +} + +IPCAStatus IPCAPropertyBagGetValueStringArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + char*** value, + size_t* valueCount) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + std::vector array; + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, array) == false) + { + return IPCA_FAIL; + } + + return AllocateAndCopyStringVectorToArrayOfCharPointers(array, value, valueCount); +} + +void IPCAPropertyBagFreeStringArray(char** valueArray, size_t valueCount) +{ + FreeArrayOfCharPointers(valueArray, valueCount); +} + +IPCAStatus IPCAPropertyBagGetValuePropertyBagArray(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + IPCAPropertyBagHandle** value, + size_t* count) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + std::vector array; + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, array) == false) + { + return IPCA_FAIL; + } + + if (count == nullptr || value == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + size_t ocrepCount = array.size(); + + if (ocrepCount == 0) + { + *count = 0; + *value = nullptr; + return IPCA_OK; + } + + *count = ocrepCount; + *value = static_cast + (OICCalloc(ocrepCount, sizeof(IPCAPropertyBagHandle))); + if (*value == nullptr) + { + *count = 0; + return IPCA_OUT_OF_MEMORY; + } + + int i = 0; + IPCAStatus status = IPCA_FAIL; + for (auto& ocrep : array) + { + IPCAPropertyBagHandle newPropertyBag; + status = IPCAPropertyBagCreate(&newPropertyBag); + if (status != IPCA_OK) + { + break; + } + + *(OC::OCRepresentation*)newPropertyBag = ocrep; + (*value)[i] = newPropertyBag; + i++; + } + + // rollback if any failure. + if (i != ocrepCount) + { + for (int x = 0; x < i; x++) + { + IPCAPropertyBagDestroy(*value[x]); + } + OICFree((void*)(*value)); + *count = 0; + *value = nullptr; + return status; + } + + return IPCA_OK; +} + +void IPCAPropertyBagFreePropertyBagArray(IPCAPropertyBagHandle* valueArray, size_t valueCount) +{ + for (int i = 0; i < valueCount; i++) + { + IPCAPropertyBagDestroy(valueArray[i]); + } + + OICFree(valueArray); +} + +IPCAStatus IPCAPropertyBagGetValueInt(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + int* value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, *value) == false) + { + return IPCA_FAIL; + } + + return IPCA_OK; +} + +IPCAStatus IPCAPropertyBagGetValueDouble(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + double* value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, *value) == false) + { + return IPCA_FAIL; + } + + return IPCA_OK; +} + +IPCAStatus IPCAPropertyBagGetValueBool(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + bool* value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, *value) == false) + { + return IPCA_FAIL; + } + + return IPCA_OK; +} + +IPCAStatus IPCAPropertyBagGetValueString(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + char** buffer) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + std::string stringValue; + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, stringValue) == false) + { + return IPCA_FAIL; + } + + return AllocateAndCopyStringToFlatBuffer(stringValue, buffer); +} + +void IPCAPropertyBagFreeString(char* buffer) +{ + OICFree(static_cast(buffer)); +} + +IPCAStatus IPCAPropertyBagGetValuePropertyBag(IPCAPropertyBagHandle propertyBagHandle, + const char* key, + IPCAPropertyBagHandle* value) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* rep = new OC::OCRepresentation(); + if (rep == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + if ((reinterpret_cast + (propertyBagHandle))->getValue(key, *rep) == false) + { + delete rep; + return IPCA_FAIL; + } + + *value = (IPCAPropertyBagHandle)rep; + return IPCA_OK; +} + +IPCAStatus IPCAPropertyBagGetResourcePath(IPCAPropertyBagHandle propertyBagHandle, + char** resourcePath) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + return AllocateAndCopyStringToFlatBuffer( + (reinterpret_cast(propertyBagHandle))->getUri(), + resourcePath); +} + +IPCAValueType AttributeTypeToIPCAValueType(AttributeType type) +{ + IPCAValueType ipcaValueType; + + switch(type) + { + case AttributeType::Integer: + ipcaValueType = IPCA_INTEGER; + break; + + case AttributeType::Double: + ipcaValueType = IPCA_DOUBLE; + break; + + case AttributeType::Boolean: + ipcaValueType = IPCA_BOOLEAN; + break; + + case AttributeType::String: + ipcaValueType = IPCA_STRING; + break; + + case AttributeType::OCRepresentation: + ipcaValueType = IPCA_PROPERTY_BAG; + break; + + case AttributeType::Vector: + ipcaValueType = IPCA_ARRAY; + break; + + case AttributeType::Binary: + ipcaValueType = IPCA_ARRAY; + break; + + case AttributeType::OCByteString: + ipcaValueType = IPCA_ARRAY; + break; + + default: + ipcaValueType = IPCA_VALUE_TYPE_NOT_SUPPORTED; + break; + } + + return ipcaValueType; +} + +IPCAStatus IPCAPropertyBagGetAllKeyValuePairs(IPCAPropertyBagHandle propertyBagHandle, + char*** keys, + IPCAValueType** valueTypes, + size_t* count) +{ + if (propertyBagHandle == nullptr) + { + return IPCA_INVALID_ARGUMENT; + } + + OC::OCRepresentation* rep = (reinterpret_cast(propertyBagHandle)); + + size_t propertyCount = rep->numberOfAttributes(); + + std::vector repKeys; + std::vector repValueTypes; + + OCRepresentation::const_iterator itr= rep->begin(); + OCRepresentation::const_iterator endItr = rep->end(); + for(; itr!=endItr; ++itr) + { + repKeys.push_back(itr->attrname()); + repValueTypes.push_back(AttributeTypeToIPCAValueType(itr->type())); + } + + IPCAStatus status = AllocateAndCopyStringVectorToArrayOfCharPointers(repKeys, keys, count); + + if (status != IPCA_OK) + { + return status; + } + + status = AllocateAndCopyTypeVectorToArrayOfType(repValueTypes, valueTypes, count); + + if (status != IPCA_OK) + { + FreeArrayOfCharPointers(*keys, propertyCount); + *keys = nullptr; + *count = 0; + return status; + } + + return status; +} + +void IPCAPropertyBagFreeIPCAValueTypeArray(IPCAValueType* valueArray) +{ + OICFree((void*)valueArray); +} + +void IPCAPropertyBagDebugDump(IPCAPropertyBagHandle propertyBagHandle) +{ + OC::OCRepresentation* rep = (reinterpret_cast(propertyBagHandle)); + PrintOCRep(*rep); +} \ No newline at end of file diff --git a/resource/IPCA/src/ocfframework.cpp b/resource/IPCA/src/ocfframework.cpp new file mode 100644 index 0000000..0fc0a27 --- /dev/null +++ b/resource/IPCA/src/ocfframework.cpp @@ -0,0 +1,1760 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "ipcainternal.h" + +using namespace std; +using namespace std::placeholders; + +#include "oic_malloc.h" +#include "oic_time.h" +#include "ocapi.h" +#include "pinoxmcommon.h" +#include "srmutility.h" +#include "ocrandom.h" + +#define TAG "IPCA_OcfFramework" +#define DO_DEBUG 0 + +const unsigned short c_discoveryTimeout = 5; // Max number of seconds to discover + // security information for a device + +// Initialize Persistent Storage for security database +FILE* server_fopen(const char *path, const char *mode) +{ + return fopen(path, mode); +} + +OCPersistentStorage ps = {server_fopen, fread, fwrite, fclose, unlink}; + +OCFFramework::OCFFramework() : + m_isStarted(false), + m_isStopping(false) +{ +} + +OCFFramework::~OCFFramework() +{ +} + +IPCAStatus OCFFramework::Start(const IPCAAppInfoInternal& appInfo, bool isUnitTestMode) +{ + std::lock_guard lock(m_startStopMutex); + + if (m_isStarted) + { + // it's already started. + return IPCA_OK; + } + + PlatformConfig Configuration { + ServiceType::InProc, + ModeType::Both, // Server mode is required for security provisioning. + "0.0.0.0", // By setting to "0.0.0.0", it binds to all available interfaces + 0, // Uses randomly available port + QualityOfService::NaQos, + &ps}; + + OCPlatform::Configure(Configuration); + + // Initialize the database that will be used for provisioning + if (OCSecure::provisionInit("") != OC_STACK_OK) + { + OIC_LOG_V(FATAL, TAG, "Failed provisionInit()"); + return IPCA_FAIL; + } + + // Device Info. + char deviceName[256]; + char deviceSoftwareVersion[256]; + char manufacturerName[256]; + OCStringLL types { nullptr, nullptr }; // no vertical resource type. + + CopyStringToBufferAllowTruncate( + appInfo.appName, deviceName, ARRAY_SIZE(deviceName)); + CopyStringToBufferAllowTruncate( + appInfo.appSoftwareVersion, deviceSoftwareVersion, ARRAY_SIZE(deviceSoftwareVersion)); + CopyStringToBufferAllowTruncate( + appInfo.appCompanyName, manufacturerName, ARRAY_SIZE(manufacturerName)); + + OCDeviceInfo deviceInfo = { deviceName, &types, deviceSoftwareVersion, nullptr }; + + // Platform Info + IPCAUuid platformUUID; + char platformId[UUID_STRING_SIZE] = { 0 }; + char platformManufacturerName[256] = ""; + char manufacturerUrl[256] = ""; + char modelNumber[] = ""; + char dateManufacture[] = ""; + char platformVersion[] = ""; + char osVersion[] = ""; + char hardwareVersion[] = ""; + char firmwareVersion[] = ""; + char supportURL[] = ""; + +#if defined(_WIN32) + // @todo: generate per platform UUID (e.g. using input such as hostname). + platformUUID = {0xd9, 0x9c, 0x23, 0x50, 0xd9, 0x5e, 0x11, 0xe6, + 0xbf, 0x26, 0xce, 0xc0, 0xc9, 0x32, 0xce, 0x01}; + std::string platformName = "Microsoft"; + std::string platformUrl = "http://www.microsoft.com"; +#else + platformUUID = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + std::string platformName = ""; + std::string platformUrl = ""; +#endif + + CopyStringToBufferAllowTruncate(platformName, + platformManufacturerName, ARRAY_SIZE(platformManufacturerName)); + + CopyStringToBufferAllowTruncate(platformUrl, + manufacturerUrl, ARRAY_SIZE(manufacturerUrl)); + + OCConvertUuidToString(platformUUID.uuid, platformId); + + OCPlatformInfo platformInfo = { + platformId, + platformManufacturerName, + manufacturerUrl, + modelNumber, + dateManufacture, + platformVersion, + osVersion, + hardwareVersion, + firmwareVersion, + supportURL, + nullptr}; + + if (!isUnitTestMode) + { + if (OC_STACK_OK != OCPlatform::registerPlatformInfo(platformInfo)) + { + return IPCA_FAIL; + } + + if (OC_STACK_OK != OCPlatform::registerDeviceInfo(deviceInfo)) + { + return IPCA_FAIL; + } + } + + // Start the worker thread that performs periodic check on device status. + m_workerThread = std::thread(&OCFFramework::WorkerThread, this); + m_isStarted = true; + return IPCA_OK; +} + +IPCAStatus OCFFramework::Stop(InputPinCallbackHandle passwordInputCallbackHandle, + DisplayPinCallbackHandle passwordDisplayCallbackHandle) +{ + std::lock_guard lock(m_startStopMutex); + + if (m_isStarted == false) + { + // not started yet. + return IPCA_OK; + } + + CleanupRequestAccessDevices(); + + OCSecure::deregisterInputPinCallback(passwordInputCallbackHandle); + OCSecure::deregisterDisplayPinCallback(passwordDisplayCallbackHandle); + + + m_isStopping = true; + + m_workerThreadCV.notify_all(); + if (m_workerThread.joinable()) + { + m_workerThread.join(); + } + +// @future: OCFFramework can't shut down because there's no cancellation for all underlying apis +// like OCPlatform::findResource, etc. +#if 0 + m_OCFDevices.clear(); + m_OCFDevicesIndexedByDeviceURI.clear(); +#endif + + m_isStopping = false; + m_isStarted = false; + + return IPCA_OK; +} + +void OCFFramework::WorkerThread(OCFFramework* ocfFramework) +{ + std::unique_lock workerThreadLock(ocfFramework->m_workerThreadMutex); + + const size_t WorkerThreadSleepTimeSeconds = 2; + std::chrono::seconds workerThreadSleepTime(WorkerThreadSleepTimeSeconds); + + while (false == ocfFramework->m_isStopping) + { + uint64_t currentTime = OICGetCurrentTime(TIME_IN_MS); + std::vector devicesThatAreNotResponding; + std::vector devicesThatAreNotOpened; + std::vector devicesToGetCommonResources; + + // Collect devices that are not used, i.e. discovered a while back and those that are not + // used by app for a while. + { + std::lock_guard lock(ocfFramework->m_OCFFrameworkMutex); + const unsigned int AllowedTimeSincLastCloseMs = 300000; + const unsigned int AllowedTimeSinceLastDiscoveryResponseMs = 60000; + + // Walk through each device. + for (auto const& device : ocfFramework->m_OCFDevices) + { + // Is device opened by app? + if ((device.second->deviceOpenCount == 0) && + (currentTime - device.second->lastCloseDeviceTime > AllowedTimeSincLastCloseMs)) + { + devicesThatAreNotOpened.push_back(device.second); + continue; // device details is about to be deleted. + } + + // Has device responded to Discovery? + if ((device.second->deviceNotRespondingIndicated == false) && + (currentTime - device.second->lastResponseTimeToDiscovery > AllowedTimeSinceLastDiscoveryResponseMs)) + { + device.second->deviceNotRespondingIndicated = true; + devicesThatAreNotResponding.push_back(device.second); + } + + // Are there common resources that are not yet obtained. + if (!device.second->deviceInfoAvailable || + !device.second->platformInfoAvailable || + !device.second->maintenanceResourceAvailable) + { + devicesToGetCommonResources.push_back(device.second); + } + } + + // Erase unopened devices from the m_OCFDevices. + for (auto& device : devicesThatAreNotOpened) + { + for (auto const& deviceUri : ocfFramework->m_OCFDevices[device->deviceId]->deviceUris) + { + ocfFramework->m_OCFDevicesIndexedByDeviceURI.erase(deviceUri); + } + + ocfFramework->m_OCFDevices.erase(device->deviceId); + OIC_LOG_V(INFO, TAG, "Device deleted from m_OCFDevices: %s", + device->deviceId.c_str()); + } + } + + // Get common resources. + for (const auto& device : devicesToGetCommonResources) + { + ocfFramework->GetCommonResources(device); + } + + // Make a snapshot of all callbacks. + std::vector callbackSnapshot; + { + std::lock_guard lock(ocfFramework->m_OCFFrameworkMutex); + callbackSnapshot = ocfFramework->m_callbacks; + } + + // Callback to apps. + for (const auto& device : devicesThatAreNotResponding) + { + for (const auto& callback : callbackSnapshot) + { + callback->DeviceDiscoveryCallback( + false, /* device is no longer responding to discovery */ + false, + device->deviceInfo, + device->discoveredResourceTypes); + } + } + + ocfFramework->m_workerThreadCV.wait_for( + workerThreadLock, + workerThreadSleepTime, + [ocfFramework]() { return ocfFramework->m_isStopping; }); + } +} + + +IPCAStatus OCFFramework::IPCADeviceOpenCalled(std::string& deviceId) +{ + // Has the app discovered the device? + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return IPCA_DEVICE_NOT_DISCOVERED; + } + + deviceDetails->deviceOpenCount++; + return IPCA_OK; +} + +IPCAStatus OCFFramework::IPCADeviceCloseCalled(std::string& deviceId) +{ + // Has the app discovered the device? + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return IPCA_DEVICE_NOT_DISCOVERED; + } + + { + std::lock_guard lock(m_OCFFrameworkMutex); + if (--deviceDetails->deviceOpenCount == 0) + { + deviceDetails->lastCloseDeviceTime = OICGetCurrentTime(TIME_IN_MS); + } + } + + assert(deviceDetails->deviceOpenCount >= 0); + return IPCA_OK; +} + +IPCAStatus OCFFramework::RegisterAppCallbackObject(Callback::Ptr cb) +{ + std::lock_guard lock(m_OCFFrameworkMutex); + m_callbacks.push_back(cb); + return IPCA_OK; +} + +void OCFFramework::UnregisterAppCallbackObject(Callback::Ptr cb) +{ + std::lock_guard lock(m_OCFFrameworkMutex); + for (size_t i = 0 ; i < m_callbacks.size() ; i++) + { + if (m_callbacks[i] == cb) + { + m_callbacks.erase(m_callbacks.begin() + i); + break; + } + } +} + +void OCFFramework::OnResourceFound(std::shared_ptr resource) +{ + bool newDevice = false; // set to true if the resource is from new device. + bool updatedDeviceInformation = false; // set to true when device information is updated + // (e.g. new resource, new resource type, etc.) + + OIC_LOG_V(INFO, TAG, "OCFFramework::OnResourceFound: sid: [%s] uri[%s]", + resource->sid().c_str(), resource->uri().c_str()); + + std::string resourcePath = resource->uri(); + DeviceDetails::Ptr deviceDetails; + + { + std::lock_guard lock(m_OCFFrameworkMutex); + + // Create new DeviceDetails if it's a newly found device id. + if (m_OCFDevices.find(resource->sid()) == m_OCFDevices.end()) + { + // New device. + newDevice = true; + deviceDetails = std::shared_ptr(new DeviceDetails()); + if (deviceDetails == nullptr) + { + OIC_LOG_V(WARNING, TAG, "OnResourceFound:: out of memory."); + return; // system is out of memory, this device won't show up in app. + } + + deviceDetails->deviceId = resource->sid(); + deviceDetails->deviceInfoRequestCount = 0; + deviceDetails->deviceInfoAvailable = false; // set to true in OnDeviceInfoCallback() + deviceDetails->platformInfoRequestCount = 0; + deviceDetails->platformInfoAvailable = false; // set to true in OnPlatformInfoCallback() + deviceDetails->maintenanceResourceRequestCount = 0; + deviceDetails->maintenanceResourceAvailable = false; + deviceDetails->securityInfoAvailable = false; // set to true in + // RequestAccessWorkerThread() + deviceDetails->securityInfo.isStarted = false; // set to true in RequestAccess() + deviceDetails->deviceOpenCount = 0; + deviceDetails->lastPingTime = 0; + + // Device is not opened at this time. + deviceDetails->lastCloseDeviceTime = OICGetCurrentTime(TIME_IN_MS); + + // Device ID is known at this time. + deviceDetails->deviceInfo.deviceId = resource->sid(); + + // Add to list of devices. + m_OCFDevices[resource->sid()] = deviceDetails; + + OIC_LOG_V(INFO, TAG, "Added device ID: [%s]", resource->sid().c_str()); + OIC_LOG_V(INFO, TAG, "m_OCFDevices count = [%d]", m_OCFDevices.size()); + } + + // Populate the details about the device. + deviceDetails = m_OCFDevices[resource->sid()]; + + // Device is discovered. + deviceDetails->deviceNotRespondingIndicated = false; + deviceDetails->lastResponseTimeToDiscovery = OICGetCurrentTime(TIME_IN_MS); + + if (deviceDetails->resourceMap.find(resourcePath) == deviceDetails->resourceMap.end()) + { + updatedDeviceInformation = true; // new resource. + } + + // Add (or replace with this latest) resource for the resource path. + deviceDetails->resourceMap[resourcePath] = resource; + + // Add the device uri if it's new. + if (std::find(deviceDetails->deviceUris.begin(), + deviceDetails->deviceUris.end(), + resource->host()) == deviceDetails->deviceUris.end()) + { + deviceDetails->deviceUris.push_back(resource->host()); + m_OCFDevicesIndexedByDeviceURI[resource->host()] = deviceDetails; + updatedDeviceInformation = true; // new device uri. + } + + // Add the resource types to global list of this device. Overlapped resource types among + // resources will be collapsed. + if (AddNewStringsToTargetList(resource->getResourceTypes(), + deviceDetails->discoveredResourceTypes)) + { + updatedDeviceInformation = true; // new resource type. + } + + if (AddNewStringsToTargetList(resource->getResourceInterfaces(), + deviceDetails->discoveredResourceInterfaces)) + { + updatedDeviceInformation = true; // new resource interface. + } + } + + if (newDevice) + { + // Discover all the resources of this device. + DiscoverAllResourcesGivenHost(resource->host()); + + // Get device & platform info for the new device URI. + GetCommonResources(deviceDetails); + } + + // Inform apps. If new device, the device info may come in subsequent discovery callbacks with + // IPCA_DEVICE_UPDATED_INFO status. Make a snapshot of all callbacks. + std::vector callbackSnapshot; + { + std::lock_guard lock(m_OCFFrameworkMutex); + callbackSnapshot = m_callbacks; + } + + // Indicate discovery to apps. + for (const auto& callback : callbackSnapshot) + { + callback->DeviceDiscoveryCallback( + true, + updatedDeviceInformation, + deviceDetails->deviceInfo, + deviceDetails->discoveredResourceTypes); + } + + + DebugOutputOCFDevices(); + +} + +IPCAStatus OCFFramework::DiscoverAllResourcesGivenHost(std::string hostAddress) +{ + std::ostringstream resourceUri; + OCConnectivityType connectivityType = CT_DEFAULT; + + // Request for all resources. + resourceUri << OC_RSRVD_WELL_KNOWN_URI; + OCStackResult result = OCPlatform::findResource( + hostAddress, + resourceUri.str(), + connectivityType, + std::bind(&OCFFramework::OnResourceFound, this, _1)); + + if (result != OC_STACK_OK) + { + return IPCA_FAIL; + } + + return IPCA_OK; +} + +IPCAStatus OCFFramework::DiscoverResources(std::vector& resourceTypeList) +{ + for (auto& resourceType : resourceTypeList) + { + std::ostringstream resourceUri; + OCConnectivityType connectivityType = CT_DEFAULT; + + resourceUri << OC_RSRVD_WELL_KNOWN_URI; + + if (resourceType != "") + { + resourceUri << "?rt=" << resourceType; + } + + OCStackResult result = OCPlatform::findResource( + "", + resourceUri.str(), + connectivityType, + std::bind(&OCFFramework::OnResourceFound, this, _1)); + + if (result != OC_STACK_OK) + { + return IPCA_FAIL; + } + } + + return IPCA_OK; +} + +void OCFFramework::OnDeviceInfoCallback(const OCRepresentation& rep) +{ + DeviceDetails::Ptr deviceDetails; + + { + std::lock_guard lock(m_OCFFrameworkMutex); + + if (m_OCFDevicesIndexedByDeviceURI.find(rep.getHost()) == m_OCFDevicesIndexedByDeviceURI.end()) + { + OIC_LOG_V(WARNING, TAG, + "OCFFramework::OnDeviceInfoCallback: Unknown device URI: [%s]", + rep.getHost().c_str()); + return; + } + + deviceDetails = m_OCFDevicesIndexedByDeviceURI[rep.getHost()]; + OCFFramework::DebugOutputOCRep(rep); + + if (deviceDetails == nullptr) + { + return; // there's no longer interest in this information. + } + + if (deviceDetails->deviceInfoAvailable == true) + { + return; // device info was processed before. + } + + // Not reading "di" because it's already known in OnResourceFound(). + std::array keys = { {"n", "icv", "dmv"} }; + std::string dataModelVersion; + std::vector Values = + { + &(deviceDetails->deviceInfo.deviceName), + &(deviceDetails->deviceInfo.deviceSoftwareVersion), + &dataModelVersion + }; + + for (size_t i = 0; i < keys.size(); i++) + { + rep.getValue(keys[i], *Values[i]); + } + + // Add the device uri if it's new. + if (std::find(deviceDetails->deviceUris.begin(), + deviceDetails->deviceUris.end(), + rep.getHost()) == deviceDetails->deviceUris.end()) + { + deviceDetails->deviceUris.push_back(rep.getHost()); + m_OCFDevicesIndexedByDeviceURI[rep.getHost()] = deviceDetails; + } + + deviceDetails->deviceInfo.deviceUris = deviceDetails->deviceUris; + + OCPlatform::getPropertyValue( + PAYLOAD_TYPE_DEVICE, + OC_RSRVD_DATA_MODEL_VERSION, + deviceDetails->deviceInfo.dataModelVersions); + + OCPlatform::getPropertyValue( + PAYLOAD_TYPE_DEVICE, + OC_RSRVD_PROTOCOL_INDEPENDENT_ID, + deviceDetails->deviceInfo.platformIndependentId); + + deviceDetails->deviceInfoAvailable = true; + } + + // Inform apps. + // Make a snapshot of all callbacks. + std::vector callbackSnapshot; + { + std::lock_guard lock(m_OCFFrameworkMutex); + callbackSnapshot = m_callbacks; + } + + // Indicate discovery to apps. + for (const auto& callback : callbackSnapshot) + { + callback->DeviceDiscoveryCallback( + true, /* device is responding */ + true, /* this is an updated device info */ + deviceDetails->deviceInfo, + deviceDetails->discoveredResourceTypes); + } + + DebugOutputOCFDevices(); +} + +void OCFFramework::OnPlatformInfoCallback(const OCRepresentation& rep) +{ + std::lock_guard lock(m_OCFFrameworkMutex); + + OCFFramework::DebugOutputOCRep(rep); + + if (m_OCFDevicesIndexedByDeviceURI.find(rep.getHost()) == m_OCFDevicesIndexedByDeviceURI.end()) + { + OIC_LOG_V(WARNING, TAG, "OCFFramework::OnDeviceInfoCallback: Unknown device URI: [%s]", + rep.getHost().c_str()); + return; + } + + DeviceDetails::Ptr deviceDetails = m_OCFDevicesIndexedByDeviceURI[rep.getHost()]; + + if (deviceDetails == nullptr) + { + return; // there's no longer interest in this information. + } + + if (deviceDetails->platformInfoAvailable == true) + { + return; // multiple platform info received. + } + + std::array keys = + { + {"pi", "mnmn", "mnml", "mnmo", "mndt", "mnpv", "mnos", "mnhw", "mnfv", "mnsl", "st"} + }; + + std::vector Values = + { + &(deviceDetails->platformInfo.platformId), + &(deviceDetails->platformInfo.manufacturerName), + &(deviceDetails->platformInfo.manufacturerURL), + &(deviceDetails->platformInfo.modelNumber), + &(deviceDetails->platformInfo.manufacturingDate), + &(deviceDetails->platformInfo.platformVersion), + &(deviceDetails->platformInfo.osVersion), + &(deviceDetails->platformInfo.hardwareVersion), + &(deviceDetails->platformInfo.firmwareVersion), + &(deviceDetails->platformInfo.manufacturerSupportURL), + &(deviceDetails->platformInfo.referenceTime) + }; + + for (size_t i = 0; i < keys.size(); i++) + { + rep.getValue(keys[i], *Values[i]); + } + + deviceDetails->platformInfoAvailable = true; + DebugOutputOCFDevices(); +} + +IPCAStatus OCFFramework::GetCommonResources(DeviceDetails::Ptr deviceDetails) +{ + const int MAX_REQUEST_COUNT = 3; + + OCStackResult result; + + // Get platform info if device hasn't responded to earlier request. + if ((deviceDetails->platformInfoAvailable == false) && + (deviceDetails->platformInfoRequestCount < MAX_REQUEST_COUNT)) + { + // Use host address of oic/p if the resource is returned by oic/res. + std::string platformResourcePath(OC_RSRVD_PLATFORM_URI); + std::string resourceType; + std::shared_ptr platformResource = FindOCResource(deviceDetails, + platformResourcePath, resourceType); + + result = OCPlatform::getPlatformInfo( + platformResource ? platformResource->host() : deviceDetails->deviceUris[0], + OC_RSRVD_PLATFORM_URI, + CT_DEFAULT, + std::bind(&OCFFramework::OnPlatformInfoCallback, this, _1)); + + if (result != OC_STACK_OK) + { + OIC_LOG_V(WARNING, TAG, "Failed getPlatformInfo() for: [%s] OC result: [%d]", + deviceDetails->deviceUris[0], result); + } + + deviceDetails->platformInfoRequestCount++; + } + + // Get device info. + if ((deviceDetails->deviceInfoAvailable == false) && + (deviceDetails->deviceInfoRequestCount < MAX_REQUEST_COUNT)) + { + // Use host address of oic/d if the resource is returned by oic/res. + std::string deviceResourcePath(OC_RSRVD_DEVICE_URI); + std::string resourceType; + std::shared_ptr deviceResource = FindOCResource( + deviceDetails, + deviceResourcePath, + resourceType); + + result = OCPlatform::getDeviceInfo( + deviceResource ? deviceResource->host() : + deviceDetails->deviceUris[0], + OC_RSRVD_DEVICE_URI, + CT_DEFAULT, + std::bind(&OCFFramework::OnDeviceInfoCallback, this, _1)); + if (result != OC_STACK_OK) + { + OIC_LOG_V(WARNING, TAG, "Failed getDeviceInfo() for [%s] OC result: [%d]", + deviceDetails->deviceUris[0], result); + } + + deviceDetails->deviceInfoRequestCount++; + } + + // Get maintenance resource. + if ((deviceDetails->maintenanceResourceAvailable == false) && + (deviceDetails->maintenanceResourceRequestCount < MAX_REQUEST_COUNT)) + { + std::ostringstream deviceUri; + OCConnectivityType connectivityType = CT_DEFAULT; + deviceUri << OC_RSRVD_WELL_KNOWN_URI; + deviceUri << "?rt=" << OC_RSRVD_RESOURCE_TYPE_MAINTENANCE; + + result = OCPlatform::findResource( + deviceDetails->deviceUris[0], + deviceUri.str(), + connectivityType, + std::bind(&OCFFramework::OnResourceFound, this, _1)); + + if (result != OC_STACK_OK) + { + OIC_LOG_V(WARNING, TAG, "Failed findResource() for oic/mnt OC result: [%d]", result); + } + + deviceDetails->maintenanceResourceRequestCount++; + } + + return IPCA_OK; +} + +IPCAStatus MapOCStackResultToIPCAStatus(OCStackResult result) +{ + IPCAStatus status; + + switch(result) + { + case OC_STACK_OK: + case OC_STACK_CONTINUE: + case OC_STACK_RESOURCE_CHANGED: + status = IPCA_OK; + break; + + case OC_STACK_UNAUTHORIZED_REQ: + status = IPCA_ACCESS_DENIED; + break; + + case OC_STACK_RESOURCE_CREATED: + status = IPCA_RESOURCE_CREATED; + break; + + case OC_STACK_RESOURCE_DELETED: + status = IPCA_RESOURCE_DELETED; + break; + + default: + status = IPCA_FAIL; + break; + } + + return status; +} + +// Callback handler on PUT request +void OCFFramework::OnPostPut(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode, + CallbackInfo::Ptr callbackInfo) +{ + OC_UNUSED(headerOptions); + + IPCAStatus status = MapOCStackResultToIPCAStatus((OCStackResult)eCode); + + for (const auto& callback : m_callbacks) + { + callback->SetCallback(status, rep, callbackInfo); + } +} + +// Callback handler on GET request +void OCFFramework::OnGet(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode, + CallbackInfo::Ptr callbackInfo) +{ + headerOptions; + + IPCAStatus status = IPCA_OK; + + if (eCode > OCStackResult::OC_STACK_RESOURCE_CHANGED) + { + status = IPCA_FAIL; + } + + for (const auto& callback : m_callbacks) + { + callback->GetCallback(status, rep, callbackInfo); + } +} + +void OCFFramework::OnObserve( + const HeaderOptions headerOptions, + const OCRepresentation &rep, + const int &eCode, + const int &sequenceNumber, + CallbackInfo::Ptr callbackInfo) +{ + OC_UNUSED(sequenceNumber); + + IPCAStatus status = IPCA_OK; + if (eCode > OCStackResult::OC_STACK_RESOURCE_CHANGED) + { + status = IPCA_FAIL; + } + + for (const auto& callback : m_callbacks) + { + callback->ObserveCallback(status, rep, callbackInfo); + } +} + +void OCFFramework::OnDelete(const HeaderOptions& headerOptions, + const int eCode, + CallbackInfo::Ptr callbackInfo) +{ + OC_UNUSED(headerOptions); + + IPCAStatus status = MapOCStackResultToIPCAStatus((OCStackResult)eCode); + + for (const auto& callback : m_callbacks) + { + callback->DeleteResourceCallback(status, callbackInfo); + } +} + +IPCAStatus OCFFramework::SendCommandToDevice(std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + OCRepresentation* rep) +{ + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + std::shared_ptr ocResource = FindOCResource( + deviceDetails, + callbackInfo->resourcePath, + callbackInfo->resourceType); + if (ocResource == nullptr) + { + return IPCA_RESOURCE_NOT_FOUND; + } + + QueryParamsMap queryParamsMap; + if (callbackInfo->resourceType.empty() == false) + { + queryParamsMap[OC::Key::RESOURCETYPESKEY] = callbackInfo->resourceType; + } + + if (callbackInfo->resourceInterface.empty() == false) + { + queryParamsMap[OC::Key::INTERFACESKEY] = callbackInfo->resourceInterface; + } + + OCStackResult result = OC_STACK_ERROR; + switch (callbackInfo->type) + { + case CallbackType_GetPropertiesComplete: + { + result = ocResource->get( + queryParamsMap, + std::bind(&OCFFramework::OnGet, this, _1, _2, _3, callbackInfo)); + break; + } + + case CallbackType_SetPropertiesComplete: + { + result = ocResource->post( + *rep, + queryParamsMap, + std::bind(&OCFFramework::OnPostPut, this, _1, _2, _3, callbackInfo)); + break; + } + + case CallbackType_CreateResourceComplete: + { + result = ocResource->post( + *rep, + queryParamsMap, + std::bind(&OCFFramework::OnPostPut, this, _1, _2, _3, callbackInfo)); + break; + } + + case CallbackType_DeleteResourceComplete: + { + result = ocResource->deleteResource( + std::bind(&OCFFramework::OnDelete, this, _1, _2, callbackInfo)); + break; + } + + case CallbackType_ResourceChange: + { + callbackInfo->ocResource = ocResource; + result = ocResource->observe( + ObserveType::Observe, + queryParamsMap, + std::bind(&OCFFramework::OnObserve, this, + _1, _2, _3, _4, callbackInfo)); + break; + } + } + + if (result == OC_STACK_OK) + { + callbackInfo->requestSentTimestamp = OICGetCurrentTime(TIME_IN_MS); + return IPCA_OK; + } + else + { + return IPCA_FAIL; + } +} + +void OCFFramework::StopObserve(CallbackInfo::Ptr cbInfo) +{ + std::shared_ptr ocResource = cbInfo->ocResource; + ocResource->cancelObserve(); +} + +void OCFFramework::IsResourceObservable(std::string& deviceId, + const char* resourcePath, + bool* isObservable) +{ + *isObservable = false; + + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return; + } + + if (deviceDetails->resourceMap.find(resourcePath) == deviceDetails->resourceMap.end()) + { + return; + } + + std::shared_ptr ocResource = deviceDetails->resourceMap[resourcePath]; + *isObservable = ocResource->isObservable(); +} + +IPCAStatus OCFFramework::PingDevice(std::string& deviceId) +{ + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + std::ostringstream resourceUri; + resourceUri << OC_RSRVD_WELL_KNOWN_URI; + resourceUri << "?rt=" << OC_RSRVD_RESOURCE_TYPE_DEVICE; + + assert(deviceDetails->deviceUris.size() > 0); + OCConnectivityType connectivityType = CT_DEFAULT; + OCStackResult result = OCPlatform::findResource( + deviceDetails->deviceUris[0], + resourceUri.str(), + connectivityType, + std::bind(&OCFFramework::OnResourceFound, this, _1)); + + if (result != OC_STACK_OK) + { + return IPCA_FAIL; + } + + deviceDetails->lastPingTime = OICGetCurrentTime(TIME_IN_MS); + return IPCA_OK; +} + +IPCAStatus OCFFramework::GetLastPingTime(std::string& deviceId, uint64_t& lastPingTime) +{ + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + lastPingTime = deviceDetails->lastPingTime; + return IPCA_OK; +} + +IPCAStatus OCFFramework::FindDeviceDetails(const std::string& deviceId, + DeviceDetails::Ptr& deviceDetails) +{ + std::lock_guard lock(m_OCFFrameworkMutex); + + auto device = m_OCFDevices.find(deviceId); + if (device == m_OCFDevices.end()) + { + return IPCA_FAIL; + } + + deviceDetails = device->second; + return IPCA_OK; +} + +std::shared_ptr OCFFramework::FindOCResource( + const DeviceDetails::Ptr& deviceDetails, + const std::string& targetResourcePath, + const std::string& targetRT) +{ + // Return resource matching resource path. + if (deviceDetails->resourceMap.find(targetResourcePath) != deviceDetails->resourceMap.end()) + { + return deviceDetails->resourceMap[targetResourcePath]; + } + + // No matching resource path. Return first resource that implements target resource type. + for (auto const& resource : deviceDetails->resourceMap) + { + for (auto const& rt : resource.second->getResourceTypes()) + { + if (rt.compare(targetRT) == 0) + { + return resource.second; + } + } + } + + return nullptr; +} + +IPCAStatus OCFFramework::CopyDeviceInfo(std::string& deviceId, IPCADeviceInfo** callerDeviceInfo) +{ + *callerDeviceInfo = nullptr; + + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + // Determine if server has responded to OCPlatform::getDeviceInfo(). + if (deviceDetails->deviceInfoAvailable == false) + { + return IPCA_INFORMATION_NOT_AVAILABLE; + } + + IPCADeviceInfo* deviceInfo = static_cast(OICMalloc(sizeof(IPCADeviceInfo))); + if (deviceInfo == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + memset(deviceInfo, 0, sizeof(IPCADeviceInfo)); + + // @future: versionRequested determines what's copied to the caller buffer. + deviceInfo->version = IPCA_VERSION_1; + + if (IPCA_OK != AllocateAndCopyStringVectorToArrayOfCharPointers( + deviceDetails->deviceUris, + const_cast(&deviceInfo->deviceUris), + &deviceInfo->deviceUriCount)) + { + OICFree(deviceInfo); + return IPCA_OUT_OF_MEMORY; + } + + if (IPCA_OK != AllocateAndCopyStringVectorToArrayOfCharPointers( + deviceDetails->deviceInfo.dataModelVersions, + const_cast(&deviceInfo->dataModelVersions), + &deviceInfo->dataModelVersionCount)) + { + FreeArrayOfCharPointers(const_cast(deviceInfo->deviceUris), deviceInfo->deviceUriCount); + OICFree(deviceInfo); + return IPCA_OUT_OF_MEMORY; + } + + if ((IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->deviceId, + const_cast(&deviceInfo->deviceId))) || + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->deviceInfo.platformIndependentId, + const_cast(&deviceInfo->protocolIndependentId))) || + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->deviceInfo.deviceName, + const_cast(&deviceInfo->deviceName))) || + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->deviceInfo.deviceSoftwareVersion, + const_cast(&deviceInfo->deviceSoftwareVersion)))) + { + FreeDeviceInfo(deviceInfo); + return IPCA_OUT_OF_MEMORY; + } + + *callerDeviceInfo = deviceInfo; + return IPCA_OK; +} + +void OCFFramework::FreeDeviceInfo(IPCADeviceInfo* deviceInfo) +{ + FreeArrayOfCharPointers(const_cast + (deviceInfo->deviceUris), deviceInfo->deviceUriCount); + FreeArrayOfCharPointers(const_cast + (deviceInfo->dataModelVersions), deviceInfo->dataModelVersionCount); + OICFree(const_cast(reinterpret_cast(deviceInfo->deviceId))); + OICFree(const_cast(reinterpret_cast(deviceInfo->protocolIndependentId))); + OICFree(const_cast(reinterpret_cast(deviceInfo->deviceName))); + OICFree(const_cast(reinterpret_cast(deviceInfo->deviceSoftwareVersion))); + OICFree(const_cast(reinterpret_cast(deviceInfo))); +} + +IPCAStatus OCFFramework::CopyPlatformInfo(std::string& deviceId, + IPCAPlatformInfo** callerPlatformInfo) +{ + *callerPlatformInfo = nullptr; + + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + if (deviceDetails->platformInfoAvailable == false) + { + return IPCA_INFORMATION_NOT_AVAILABLE; + } + + IPCAPlatformInfo* platformInfo = static_cast + (OICMalloc(sizeof(IPCAPlatformInfo))); + if (platformInfo == nullptr) + { + return IPCA_OUT_OF_MEMORY; + } + + // @future: versionRequested determines what's copied to the caller buffer. + platformInfo->version = IPCA_VERSION_1; + + if ((IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.platformId, + const_cast(&platformInfo->platformId))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.manufacturerName, + const_cast(&platformInfo->manufacturerName))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.manufacturerURL, + const_cast(&platformInfo->manufacturerURL))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.modelNumber, + const_cast(&platformInfo->modelNumber))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.manufacturingDate, + const_cast(&platformInfo->manufacturingDate))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.platformVersion, + const_cast(&platformInfo->platformVersion)) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.osVersion, + const_cast(&platformInfo->osVersion))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.hardwareVersion, + const_cast(&platformInfo->hardwareVersion))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.firmwareVersion, + const_cast(&platformInfo->firmwareVersion))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.manufacturerSupportURL, + const_cast(&platformInfo->manufacturerSupportURL))) || + + (IPCA_OK != AllocateAndCopyStringToFlatBuffer( + deviceDetails->platformInfo.referenceTime, + const_cast(&platformInfo->referenceTime))))) + { + FreePlatformInfo(platformInfo); + return IPCA_OUT_OF_MEMORY; + } + + *callerPlatformInfo = platformInfo; + return IPCA_OK; +} + +void OCFFramework::FreePlatformInfo(IPCAPlatformInfo* platformInfo) +{ + OICFree(const_cast(reinterpret_cast(platformInfo->platformId))); + OICFree(const_cast(reinterpret_cast(platformInfo->manufacturerName))); + OICFree(const_cast(reinterpret_cast(platformInfo->manufacturerURL))); + OICFree(const_cast(reinterpret_cast(platformInfo->modelNumber))); + OICFree(const_cast(reinterpret_cast(platformInfo->manufacturingDate))); + OICFree(const_cast(reinterpret_cast(platformInfo->platformVersion))); + OICFree(const_cast(reinterpret_cast(platformInfo->osVersion))); + OICFree(const_cast(reinterpret_cast(platformInfo->hardwareVersion))); + OICFree(const_cast(reinterpret_cast(platformInfo->firmwareVersion))); + OICFree(const_cast(reinterpret_cast(platformInfo->manufacturerSupportURL))); + OICFree(const_cast(reinterpret_cast(platformInfo->referenceTime))); + OICFree(const_cast(reinterpret_cast(platformInfo))); +} + +IPCAStatus OCFFramework::CopyResourcePaths(const std::string& resourceInterface, + const std::string& resourceType, + std::string& deviceId, + std::vector& resourcePathList) +{ + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + for (auto const& resource : deviceDetails->resourceMap) + { + if ((!resourceInterface.empty()) && + (!IsStringInList(resourceInterface, resource.second->getResourceInterfaces()))) + { + continue; + } + + if ((!resourceType.empty()) && + (!IsStringInList(resourceType, resource.second->getResourceTypes()))) + { + continue; + } + + resourcePathList.push_back(resource.second->uri()); + } + + return IPCA_OK; +} + +IPCAStatus OCFFramework::CopyResourceInfo(const std::string& deviceId, + const std::string& resourcePath, + ResourceInfoType resourceInfoType, + std::vector& resourceInfo) +{ + DeviceDetails::Ptr deviceDetails; + IPCAStatus status = FindDeviceDetails(deviceId, deviceDetails); + if (status != IPCA_OK) + { + return status; + } + + // Not specific resource. + if (resourcePath.length() == 0) + { + switch(resourceInfoType) + { + case ResourceInfoType::ResourceType: + resourceInfo = deviceDetails->discoveredResourceTypes; + status = IPCA_OK; + break; + + case ResourceInfoType::ResourceInterface: + resourceInfo = deviceDetails->discoveredResourceInterfaces; + status = IPCA_OK; + break; + + default: + status = IPCA_INVALID_ARGUMENT; + break; + } + + return status; + } + + status = IPCA_RESOURCE_NOT_FOUND; + + // Filter for target resource URI. + for (auto const& resource : deviceDetails->resourceMap) + { + if (resourcePath.compare(resource.second->uri()) == 0) + { + switch(resourceInfoType) + { + case ResourceInfoType::ResourceType: + resourceInfo = resource.second->getResourceTypes(); + status = IPCA_OK; + break; + + case ResourceInfoType::ResourceInterface: + resourceInfo = resource.second->getResourceInterfaces(); + status = IPCA_OK; + break; + + default: + status = IPCA_INVALID_ARGUMENT; + break; + + } + } + } + + return status; +} + +void OCFFramework::DebugOutputOCFDevices() +{ +#if DO_DEBUG + std::lock_guard lock(m_OCFFrameworkMutex); + + std::cout << "***** DebugOutputOCFDevices() ****" << std::endl; + std::cout << "Device count: " << m_OCFDevices.size() << std::endl; + + // For each device + for (auto const& device : m_OCFDevices) + { + std::cout << "Device URI : " << device.first << std::endl; + std::cout << "Device id : " << device.second->deviceInfo.deviceId << std::endl; + std::cout << "Device name : " << device.second->deviceInfo.deviceName << std::endl; + std::cout << "Resource Types: " << std::endl; + for (auto const& res : device.second->discoveredResourceTypes) + { + std::cout << " " << res.c_str() << std::endl; + } + + // For each resource + for (auto const& res : device.second->resourceMap) + { + std::cout << "Resource: " << res.first << std::endl; + std::cout << " URI: " << res.second->uri() << std::endl; + + // For each resource type + std::vector resourceTypes = res.second->getResourceTypes(); + for(auto rType : resourceTypes) + { + std::cout << " Resource Type: " << rType.c_str() << std::endl; + } + } + + std::cout << std::endl; + } +#endif +} + +void OCFFramework::DebugOutputOCRep(const OCRepresentation& rep) +{ +#if DO_DEBUG + std::lock_guard lock(m_OCFFrameworkMutex); + PrintOCRep(rep); +#else + OC_UNUSED(rep); +#endif +} + +IPCAStatus OCFFramework::RequestAccess(std::string& deviceId, + CallbackInfo::Ptr callbackInfo, + CallbackInfo::Ptr passwordInputCallbackInfo) +{ + IPCAStatus status = IPCA_OK; + DeviceDetails::Ptr deviceDetails; + RequestAccessContext* requestAccessContext = nullptr; + + if (m_isStopping) + { + return IPCA_FAIL; + } + + // Find the device details for this device + status = FindDeviceDetails(deviceId, deviceDetails); + if (status == IPCA_OK) + { + // Return a failure if an access request is already in progress for this device. + if (!deviceDetails->securityInfo.isStarted) + { + deviceDetails->securityInfo.isStarted = true; + } + else + { + return IPCA_FAIL; + } + } + else + { + return status; + } + + // Construct context for the worker thread + requestAccessContext = static_cast + (OICCalloc(1, sizeof(RequestAccessContext))); + if (nullptr != requestAccessContext) + { + requestAccessContext->deviceId = deviceId; + requestAccessContext->ocfFramework = this; + requestAccessContext->callbackInfo = callbackInfo; + requestAccessContext->passwordInputCallbackInfo = passwordInputCallbackInfo; + } + else + { + return IPCA_OUT_OF_MEMORY; + } + + // Add the context information to the list of contexts so we can clean it up later + { + std::lock_guard lock(m_OCFFrameworkMutex); + m_OCFRequestAccessContexts[deviceId] = requestAccessContext; + } + + // Create a new thread to handle the RequestAccess request + deviceDetails->securityInfo.requestAccessThread = + std::thread(&OCFFramework::RequestAccessWorkerThread, requestAccessContext); + + return status; +} + +void OCFFramework::RequestAccessWorkerThread(RequestAccessContext* requestContext) +{ + IPCAStatus status = IPCA_OK; + IPCAStatus callbackStatus = IPCA_SECURITY_UPDATE_REQUEST_FAILED; + OCStackResult result = OC_STACK_OK; + std::string deviceId = requestContext->deviceId; + OCFFramework* ocfFramework = requestContext->ocfFramework; + CallbackInfo::Ptr callbackInfo = requestContext->callbackInfo; + CallbackInfo::Ptr passwordInputCallbackInfo = requestContext->passwordInputCallbackInfo; + DeviceDetails::Ptr deviceDetails; + OicUuid_t uuid; + + // Check to make sure the OCFFramework is not shutting down before we start this request + if (ocfFramework->m_isStopping) + { + status = IPCA_FAIL; + } + + // Find the device details for this device and convert the device id into a UUID + if (IPCA_OK == status) + { + status = ocfFramework->FindDeviceDetails(deviceId, deviceDetails); + if (status == IPCA_OK) + { + result = ConvertStrToUuid(deviceId.c_str(), &uuid); + if (OC_STACK_OK != result) + { + status = MapOCStackResultToIPCAStatus(result); + } + } + } + + // Check to see if the device supports MOT + if (IPCA_OK == status) + { + result = OCSecure::discoverMultipleOwnerEnabledDevice( + c_discoveryTimeout, &uuid, deviceDetails->securityInfo.device); + + if ((OC_STACK_OK == result) && (nullptr == deviceDetails->securityInfo.device)) + { + status = IPCA_DEVICE_NOT_DISCOVERED; + } + else if(OC_STACK_OK != result) + { + status = MapOCStackResultToIPCAStatus(result); + } + } + + // Take ownership of the device if it supports MOT and the calling app is not a subowner. + // Otherwise if the app is a subowner we will callback to the app indicating success without + // doing anything. + if ((IPCA_OK == status) && (nullptr != deviceDetails->securityInfo.device)) + { + result = deviceDetails->securityInfo.device->isSubownerOfDevice( + &deviceDetails->securityInfo.subowner); + if (OC_STACK_OK == result) + { + deviceDetails->securityInfoAvailable = true; + + if (!deviceDetails->securityInfo.subowner) + { + // Check the selected ownership transfer method of the device to see if there + // is anything we need to do before performing MOT + switch (deviceDetails->securityInfo.device->getSelectedOwnershipTransferMethod()) + { + case OIC_RANDOM_DEVICE_PIN: + { + // Requests for a random pin will be handled by the underlying stack so + // there is nothing else to do + break; + } + case OIC_PRECONFIG_PIN: + { + char passwordBuffer[OXM_PRECONFIG_PIN_MAX_SIZE + 1]; + size_t passwordBufferSize = OXM_PRECONFIG_PIN_MAX_SIZE + 1; + memset(passwordBuffer, 0, passwordBufferSize); + + // We need to set the preconfigured pin before attempting to do MOT. + // Callback to the app asking for the password. + for (const auto& callback : ocfFramework->m_callbacks) + { + callback->PasswordInputCallback(deviceId, + IPCA_OWNERSHIP_TRANSFER_PRECONFIGURED_PIN, + passwordBuffer, + passwordBufferSize, + passwordInputCallbackInfo); + } + + // Set the preconfigured pin + result = deviceDetails->securityInfo.device->addPreconfigPIN( + passwordBuffer, + strnlen_s(passwordBuffer, passwordBufferSize)); + + if (OC_STACK_OK != result) + { + status = MapOCStackResultToIPCAStatus(result); + } + break; + } + default: + { + // Preconfigured and random pin are the only supported MOT ownership + // transfer methods. + // Callback to the app reporting that the current selected method on the + // device is not supported and there needs to be intervention by the admin. + status = IPCA_FAIL; + callbackStatus = IPCA_SECURITY_UPDATE_REQUEST_NOT_SUPPORTED; + break; + } + } + + if (IPCA_OK == status) + { + std::unique_lock lock( + deviceDetails->securityInfo.requestAccessThreadMutex); + + result = deviceDetails->securityInfo.device->doMultipleOwnershipTransfer( + std::bind( + &OCFFramework::OnMultipleOwnershipTransferCompleteCallback, + ocfFramework, + _1, + _2, + deviceId, + callbackInfo)); + + if (OC_STACK_OK == result) + { + // Wait for the callback to indicate that MOT and calling back to the app + // has finished. If this takes longer + // then 30 seconds we assume that something has failed and continue. + // This is to prevent blocking forever and + // not allowing the app to close properly. + cv_status waitStatus = + deviceDetails->securityInfo.requestAccessThreadCV.wait_for( + lock, std::chrono::seconds(30)); + + if ((cv_status::timeout == waitStatus) || ocfFramework->m_isStopping) + { + status = IPCA_FAIL; + } + } + else + { + status = MapOCStackResultToIPCAStatus(result); + } + } + } + else + { + // This app is already a subowner of the device + for (const auto& callback : ocfFramework->m_callbacks) + { + callback->RequestAccessCompletionCallback( + IPCA_SECURITY_UPDATE_REQUEST_FINISHED, + callbackInfo); + } + } + } + else + { + status = MapOCStackResultToIPCAStatus(result); + } + } + + // Callback to the application with the appropriate status information if we encountered an + // issue while preparing to perform Multiple Ownership Transfer. + // OnMultipleOwnershipTransferCompleteCallback will callback to the application to report the + // success or failure of doMultipleOwnershipTransfer. + if (IPCA_OK != status) + { + for (const auto& callback : ocfFramework->m_callbacks) + { + callback->RequestAccessCompletionCallback(callbackStatus, callbackInfo); + } + } +} + +void OCFFramework::OnMultipleOwnershipTransferCompleteCallback(PMResultList_t* result, + bool error, + std::string deviceId, + CallbackInfo::Ptr callbackInfo) +{ + OC_UNUSED(result); + + IPCAStatus status = IPCA_SECURITY_UPDATE_REQUEST_FINISHED; + DeviceDetails::Ptr deviceDetails; + + // @todo: Provide more specific errors once the underlying IoTivity stack is able to provide us + // with better error codes. + if (error) + { + status = IPCA_SECURITY_UPDATE_REQUEST_FAILED; + } + + for (const auto& callback : m_callbacks) + { + callback->RequestAccessCompletionCallback(status, callbackInfo); + } + + // Get the device details so we can make sure the thread has finished and update the + // request state accordingly + status = FindDeviceDetails(deviceId, deviceDetails); + if (status == IPCA_OK) + { + deviceDetails->securityInfo.subowner = true; + deviceDetails->securityInfo.requestAccessThreadCV.notify_all(); + } +} + +IPCAStatus OCFFramework::SetInputPasswordCallback(CallbackInfo::Ptr callbackInfo, + InputPinCallbackHandle* passwordInputCallbackHandle) +{ + OCSecure::registerInputPinCallback(std::bind( + &OCFFramework::OnPasswordInputCallback, + this, + _1, + _2, + _3, + callbackInfo), + passwordInputCallbackHandle); + + return IPCA_OK; +} + +void OCFFramework::OnPasswordInputCallback(OicUuid_t deviceId, + char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr callbackInfo) +{ + std::string strDeviceId; + char uuidString[UUID_STRING_SIZE] = { 0 }; + OCConvertUuidToString(deviceId.id, uuidString); + strDeviceId = uuidString; + + for (const auto& callback : m_callbacks) + { + callback->PasswordInputCallback( + strDeviceId, + IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN, + passwordBuffer, + passwordBufferSize, + callbackInfo); + } +} + +IPCAStatus OCFFramework::SetDisplayPasswordCallback(CallbackInfo::Ptr callbackInfo, + DisplayPinCallbackHandle* passwordDisplayCallbackHandle) +{ + OCSecure::registerDisplayPinCallback(std::bind( + &OCFFramework::OnPasswordDisplayCallback, + this, + _1, + _2, + callbackInfo), + passwordDisplayCallbackHandle); + + return IPCA_OK; +} + +void OCFFramework::OnPasswordDisplayCallback(char* passwordBuffer, + size_t passwordBufferSize, + CallbackInfo::Ptr callbackInfo) +{ + OC_UNUSED(passwordBufferSize); + + for (const auto& callback : m_callbacks) + { + callback->PasswordDisplayCallback("", + IPCA_OWNERSHIP_TRANSFER_RANDOM_PIN, + passwordBuffer, + callbackInfo); + } +} + +void OCFFramework::CleanupRequestAccessDevices() +{ + std::vector requestAccessDevices; + + // Discover all of the devices that performed security operations + { + std::lock_guard lock(m_OCFFrameworkMutex); + + for (auto const& device : m_OCFDevices) + { + if (device.second->securityInfo.isStarted) + { + requestAccessDevices.push_back(device.second); + } + } + } + + // If a RequestAccess operation is still in progress for a device wait for it to finish. + // Once the operation is complete cleanup the RequestAccess context for the operation. + for (auto const& device : requestAccessDevices) + { + device->securityInfo.requestAccessThreadCV.notify_all(); + + if (device->securityInfo.requestAccessThread.joinable()) + { + device->securityInfo.requestAccessThread.join(); + } + + auto context = m_OCFRequestAccessContexts.find(device->deviceId); + if (context != m_OCFRequestAccessContexts.end()) + { + RequestAccessContext* requestAccessContext = context->second; + if (nullptr != requestAccessContext) + { + requestAccessContext->callbackInfo = nullptr; + requestAccessContext->passwordInputCallbackInfo = nullptr; + requestAccessContext->ocfFramework = nullptr; + OICFree(static_cast(requestAccessContext)); + } + m_OCFRequestAccessContexts.erase(device->deviceId); + } + } +} diff --git a/resource/IPCA/src/pretendocprovision.cpp b/resource/IPCA/src/pretendocprovision.cpp new file mode 100644 index 0000000..31d555d --- /dev/null +++ b/resource/IPCA/src/pretendocprovision.cpp @@ -0,0 +1,109 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "ipcainternal.h" + +#include "ocstack.h" +#include "srmutility.h" +#include "base64.h" + +#include + +#include "pinoxmcommon.h" +#include "srmutility.h" +#include "common.h" + +// These APIs are used when building with SECURED=0. + +OCStackResult OCSecure::discoverMultipleOwnerEnabledDevices(unsigned short timeout, + DeviceList_t &list) +{ + OC_UNUSED(timeout); + OC_UNUSED(list); + return OC_STACK_OK; +} + +OCStackResult OCSecure::registerInputPinCallback(InputPinCB inputPinCB, + InputPinCallbackHandle* inputPinCallbackHandle) +{ + OC_UNUSED(inputPinCB); + OC_UNUSED(inputPinCallbackHandle); + return OC_STACK_OK; +} + +OCStackResult OCSecure::provisionInit(const std::string& dbPath) +{ + OC_UNUSED(dbPath); + return OC_STACK_OK; +} + +OCStackResult OCSecure::deregisterInputPinCallback(InputPinCallbackHandle inputPinCallbackHandle) +{ + OC_UNUSED(inputPinCallbackHandle); + return OC_STACK_OK; +} + +OCStackResult OCSecure::registerDisplayPinCallback(DisplayPinCB displayPinCB, + DisplayPinCallbackHandle* displayPinCallbackHandle) +{ + OC_UNUSED(displayPinCB); + OC_UNUSED(displayPinCallbackHandle); + return OC_STACK_OK; +} + +OCStackResult OCSecure::deregisterDisplayPinCallback( + DisplayPinCallbackHandle displayPinCallbackHandle) +{ + OC_UNUSED(displayPinCallbackHandle); + return OC_STACK_OK; +} + +OicSecOxm_t OCSecureResource::getSelectedOwnershipTransferMethod() +{ + return OIC_JUST_WORKS; +} + +OCStackResult OCSecure::discoverMultipleOwnerEnabledDevice(unsigned short timeout, + const OicUuid_t* deviceID, + std::shared_ptr &foundDevice) +{ + OC_UNUSED(timeout); + OC_UNUSED(deviceID); + OC_UNUSED(foundDevice); + return OC_STACK_OK; +} + +OCStackResult OCSecureResource::addPreconfigPIN(const char* preconfPIN, size_t preconfPINLength) +{ + OC_UNUSED(preconfPIN); + OC_UNUSED(preconfPINLength); + return OC_STACK_OK; +} + +OCStackResult OCSecureResource::doMultipleOwnershipTransfer(ResultCallBack resultCallback) +{ + OC_UNUSED(resultCallback); + return OC_STACK_OK; +} + +OCStackResult OCSecureResource::isSubownerOfDevice(bool* subowner) +{ + OC_UNUSED(subowner); + return OC_STACK_OK; +} diff --git a/resource/IPCA/unittests/IPCAElevatorClient.cpp b/resource/IPCA/unittests/IPCAElevatorClient.cpp new file mode 100644 index 0000000..6b690f9 --- /dev/null +++ b/resource/IPCA/unittests/IPCAElevatorClient.cpp @@ -0,0 +1,705 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "ocrandom.h" +#include "octypes.h" +#include "ipca.h" +#include "ipcatestdata.h" +#include "ipcaelevatorclient.h" + +extern IPCAUuid IPCATestAppUuid; +extern char IPCATestAppName[]; +extern std::string g_elevator1Name; + +IPCAStatus IPCAElevatorClient::FactoryResetElevator() +{ + IPCAStatus status = IPCAFactoryReset(m_deviceHandle); + + if (status == IPCA_RESOURCE_NOT_FOUND) + { + // Give it some time to discover the maintenance resource. + std::this_thread::sleep_for(std::chrono::seconds(2)); + status = IPCAFactoryReset(m_deviceHandle); + } + + return status; +} + +IPCAStatus IPCAElevatorClient::RebootElevator() +{ + IPCAStatus status = IPCAReboot(m_deviceHandle); + + if (status == IPCA_RESOURCE_NOT_FOUND) + { + // Give it some time to discover the maintenance resource. + std::this_thread::sleep_for(std::chrono::seconds(2)); + status = IPCAReboot(m_deviceHandle); + } + + return status; +} + +void C_GetPropertiesCb(IPCAStatus result, void* context, IPCAPropertyBagHandle propertyBagHandle) +{ + IPCAElevatorClient* ipcaElevatorClient = (IPCAElevatorClient*)context; + ipcaElevatorClient->GetPropertiesCallback(result, context, propertyBagHandle); +} + +bool IPCAElevatorClient::GetElevatorProperties() +{ + m_getPropertiesCallbackCalled = false; + + // Get the data. + EXPECT_EQ(IPCA_OK, IPCAGetProperties( + m_deviceHandle, + &C_GetPropertiesCb, + (void*)this, + ELEVATOR_RESOURCE_PATH, + nullptr, + nullptr, + nullptr)); + + // Wait for get data completion. + std::unique_lock lock(m_getDataCompleteCbMutex); + m_getDataCompleteCbCV.wait_for( + lock, + std::chrono::seconds(10), + [this ] { return m_getPropertiesCallbackCalled; }); + + return m_getPropertiesCallbackCalled; +} + +void C_SetPropertiesCb(IPCAStatus result, void* context, IPCAPropertyBagHandle propertyBagHandle) +{ + IPCAElevatorClient* ipcaTest = (IPCAElevatorClient*)context; + ipcaTest->SetPropertiesCallback(result, context, propertyBagHandle); +} + +bool IPCAElevatorClient::SetElevatorProperties(IPCAPropertyBagHandle propertyBagHandle) +{ + m_setPropertiesCallbackCalled = false; + + // Set properties. + EXPECT_EQ(IPCA_OK, IPCASetProperties( + m_deviceHandle, + &C_SetPropertiesCb, + this, + (const char*)ELEVATOR_RESOURCE_PATH, + nullptr, + (const char*)ELEVATOR_RESOURCE_TYPE, + propertyBagHandle, + nullptr)); + + + std::unique_lock lock(m_setPropertiesCompleteCbMutex); + + m_setPropertiesCompleteCV.wait_for( + lock, + std::chrono::seconds(10), + [this] { return m_setPropertiesCallbackCalled; }); + + return m_setPropertiesCallbackCalled; +} + +void C_CreateResourceCb(IPCAStatus result, + void* context, + const char* newResourcePath, + IPCAPropertyBagHandle propertyBagHandle) +{ + IPCAElevatorClient* ipcaTest = (IPCAElevatorClient*)context; + ipcaTest->CreateResourceCallback(result, newResourcePath, propertyBagHandle); +} + +bool IPCAElevatorClient::CreateElevatorResource(std::string resourcePath, + IPCAPropertyBagHandle propertyBagHandle) +{ + m_createResourceCallbackCalled = false; + + // Set properties. + EXPECT_EQ(IPCA_OK, IPCACreateResource( + m_deviceHandle, + &C_CreateResourceCb, + this, + resourcePath.c_str(), + nullptr, + nullptr, + propertyBagHandle, + nullptr)); + + + std::unique_lock lock(m_createResourceCompleteCbMutex); + + m_createResourceCompleteCV.wait_for( + lock, + std::chrono::seconds(10), + [this] { return m_createResourceCallbackCalled; }); + + return m_createResourceCallbackCalled; +} + +void C_DeleteResourceCb(IPCAStatus result, void* context) +{ + IPCAElevatorClient* ipcaTest = (IPCAElevatorClient*)context; + ipcaTest->DeleteResourceCallback(result); +} + +bool IPCAElevatorClient::DeleteElevatorResource() +{ + m_deleteResourceCallbackCalled = false; + + // Set properties. + EXPECT_EQ(IPCA_OK, IPCADeleteResource( + m_deviceHandle, + &C_DeleteResourceCb, + this, + (const char*)ELEVATOR_RESOURCE_DELETE_PATH, + nullptr)); + + + std::unique_lock lock(m_deleteResourceCompleteCbMutex); + + m_deleteResourceCompleteCV.wait_for( + lock, + std::chrono::seconds(10), + [this] { return m_deleteResourceCallbackCalled; }); + + return m_deleteResourceCallbackCalled; +} + +void IPCAElevatorClient::GetCurrentFloor(int* currentFloor, bool* result) +{ + *result = false; + if (GetElevatorProperties() == true) + { + *currentFloor = m_currentFloor; + *result = true; + } +} + +void IPCAElevatorClient::GetTargetFloor(int* targetFloor, bool* result) +{ + *result = false; + if (GetElevatorProperties() == true) + { + *targetFloor = m_targetFloor; + *result = true; + } +} + +void IPCAElevatorClient::SetTargetFloor(int targetFloor, bool* result) +{ + *result = false; + + IPCAPropertyBagHandle propertyBagHandle; + ASSERT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt( + propertyBagHandle, ELEVATOR_PROPERTY_TARGET_FLOOR, targetFloor)); + + *result = SetElevatorProperties(propertyBagHandle); + IPCAPropertyBagDestroy(propertyBagHandle); +} + +void IPCAElevatorClient::CreateResourceRelativePath() +{ + IPCAPropertyBagHandle propertyBagHandle; + ASSERT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle)); + + CreateElevatorResource(ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH, propertyBagHandle); + + IPCAPropertyBagDestroy(propertyBagHandle); +} + +void IPCAElevatorClient::DeleteResource() +{ + DeleteElevatorResource(); +} + +void IPCAElevatorClient::CreateResouceExplicitPath() +{ +} + +void C_ResourceChangeNotificationCb(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + IPCAElevatorClient* ipcaTest = (IPCAElevatorClient*)context; + ipcaTest->ResourceChangeNotificationCallback(result, propertyBagHandle); +} + +bool IPCAElevatorClient::StartObserve() +{ + m_observedCurrentFloor = 0; + m_observedDirection = 0; + m_observedTargetFloor = 0; + + bool isObservable; + IPCAIsResourceObservable(m_deviceHandle, "fakePath", &isObservable); + EXPECT_FALSE(isObservable); + + IPCAIsResourceObservable(m_deviceHandle, ELEVATOR_RESOURCE_PATH, &isObservable); + EXPECT_TRUE(isObservable); + + IPCAStatus status = IPCAObserveResource( + m_deviceHandle, + &C_ResourceChangeNotificationCb, + (void*)this, + ELEVATOR_RESOURCE_PATH, + nullptr, + &m_observeHandle); + + return (status == IPCA_OK ? true : false); +} + +void IPCAElevatorClient::StopObserve() +{ + if (m_observeHandle) + { + IPCACloseHandle(m_observeHandle); + } +} + +IPCAStatus IPCAElevatorClient::GetUnknownResource() +{ + // Get the data. + IPCAStatus status = IPCAGetProperties( + m_deviceHandle, + &C_GetPropertiesCb, + (void*)this, + "/ipca/test/unknownresource", + nullptr, + nullptr, + nullptr); + + return status; +} + +IPCAStatus IPCAElevatorClient::SetUnknownResource() +{ + IPCAPropertyBagHandle propertyBagHandle; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_TARGET_FLOOR, 1)); /* 1 is randomly chosen */ + + IPCAStatus status = IPCASetProperties( + m_deviceHandle, + &C_SetPropertiesCb, + this, + "/ipca/test/unknownresource", + nullptr, + nullptr, + nullptr, + nullptr); + + IPCAPropertyBagDestroy(propertyBagHandle); + + return status; +} + +bool IPCAElevatorClient::SetUnknoownInterface() +{ + m_setPropertiesCallbackCalled = false; + + IPCAPropertyBagHandle propertyBagHandle; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_TARGET_FLOOR, 1)); /* 1 is randomly chosen */ + + IPCAStatus status = IPCASetProperties( + m_deviceHandle, + &C_SetPropertiesCb, + this, + ELEVATOR_RESOURCE_PATH, + ELEVATOR_RESOURCE_MADE_UP_INTERFACE, + nullptr, + propertyBagHandle, + nullptr); + + EXPECT_EQ(IPCA_OK, status); + + std::unique_lock lock(m_setPropertiesCompleteCbMutex); + + m_setPropertiesCompleteCV.wait_for( + lock, + std::chrono::seconds(10), + [this] { return m_setPropertiesCallbackCalled; }); + + return m_setPropertiesCallbackCalled; +} + +void IPCAElevatorClient::SetUp() +{ + m_elevator1Discovered = false; + m_discoveredElevator1DeviceId.clear(); + m_discoveredElevator1DeviceName.clear(); + + m_ipcaAppHandle = nullptr; + m_deviceDiscoveryHandle = nullptr; + m_deviceHandle = nullptr; + m_newResourcePath = ""; + + IPCAAppInfo ipcaAppInfo = { IPCATestAppUuid, IPCATestAppName, "1.0.0", "Microsoft" }; + + IPCAStatus status = IPCAOpen(&ipcaAppInfo, IPCA_VERSION_1, &m_ipcaAppHandle); + + ASSERT_EQ(IPCA_OK, status); + + DiscoverElevator1(); +} + +void IPCAElevatorClient::TearDown() +{ + if (m_observeHandle != nullptr) + { + IPCACloseHandle(m_observeHandle); + m_observeHandle = nullptr; + } + + if (m_deviceHandle != nullptr) + { + IPCACloseDevice(m_deviceHandle); + m_deviceHandle = nullptr; + } + + if (m_deviceDiscoveryHandle != nullptr) + { + IPCACloseHandle(m_deviceDiscoveryHandle); + m_deviceDiscoveryHandle = nullptr; + } + + if (m_ipcaAppHandle != nullptr) + { + IPCAClose(m_ipcaAppHandle); + m_ipcaAppHandle = nullptr; + } +} + +IPCAStatus IPCAElevatorClient::ConfirmDeviceAndPlatformInfo() +{ + IPCAStatus status; + + // Device info. + IPCADeviceInfo *deviceInfo = nullptr; + int loopCount = 0; + while (loopCount++ < 10) + { + status = IPCAGetDeviceInfo(m_deviceHandle, &deviceInfo); + if (status == IPCA_OK) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + EXPECT_NE(nullptr, deviceInfo); + if (deviceInfo) + { + EXPECT_STREQ(g_elevator1Name.c_str(), deviceInfo->deviceName); + + // See: ipcatestdata.h ELEVATOR_DATA_MODEL_VERSION_1 to 3. + EXPECT_EQ(3, deviceInfo->dataModelVersionCount); + + bool modelFound = false; + std::string Model1(ELEVATOR_DATA_MODEL_VERSION_1); + for (size_t i = 0; i < deviceInfo->dataModelVersionCount; i++) + { + if (Model1.compare(deviceInfo->dataModelVersions[i]) == 0) + { + modelFound = true; + break; + } + } + + EXPECT_TRUE(modelFound); + EXPECT_STREQ(ELEVATOR_PLATFORM_INDEPENDENT_ID, deviceInfo->protocolIndependentId); + IPCAFreeDeviceInfo(deviceInfo); + } + + // Platform info. + IPCAPlatformInfo *platformInfo; + loopCount = 0; + while (loopCount++ < 10) + { + status = IPCAGetPlatformInfo(m_deviceHandle, &platformInfo); + if (status == IPCA_OK) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + EXPECT_EQ(IPCA_OK, IPCAGetPlatformInfo(m_deviceHandle, &platformInfo)); + EXPECT_STREQ(ELEVATOR_PLATFORM_ID, platformInfo->platformId); + IPCAFreePlatformInfo(platformInfo); + + return IPCA_OK; +} + +IPCAStatus IPCAElevatorClient::ConfirmResources() +{ + // Resource list in array of char*, expect elevator resource is one of them. + char** resourcePathList; + size_t resourcePathCount; + EXPECT_EQ(IPCA_OK, IPCAGetResources(m_deviceHandle, + nullptr, nullptr, &resourcePathList, &resourcePathCount)); + EXPECT_LT(0, resourcePathCount); // At least there must be elevator resource. + int i = 0; + bool elevatorResourceFound = false; + for (i = 0; i < resourcePathCount; i++) + { + std::string resourcePath = resourcePathList[i]; + if (resourcePath.find(ELEVATOR_KEYWORD) != std::string::npos) + { + elevatorResourceFound = true; + break; + } + } + EXPECT_TRUE(elevatorResourceFound); + IPCAFreeStringArray(resourcePathList, resourcePathCount); + return IPCA_OK; +} + +IPCAStatus IPCAElevatorClient::ConfirmResourceTypes() +{ + // Function list in array of char*, expect elevator function is one of them. + char** resourceTypes; + size_t resourceTypeCount; + EXPECT_EQ(IPCA_OK, IPCAGetResourceTypes(m_deviceHandle, + nullptr, &resourceTypes, &resourceTypeCount)); + EXPECT_LT(0, resourceTypeCount); // At least 1 resource type. + int i = 0; + bool elevatorResourceTypeFound = false; + for (i = 0; i < resourceTypeCount; i++) + { + std::string rt = resourceTypes[i]; + if (rt.find(ELEVATOR_KEYWORD) != std::string::npos) + { + elevatorResourceTypeFound = true; + break; + } + } + EXPECT_TRUE(elevatorResourceTypeFound); + IPCAFreeStringArray(resourceTypes, resourceTypeCount); // free the IPCA allocated memory. + + // Function list for a given resource. + EXPECT_EQ(IPCA_RESOURCE_NOT_FOUND, IPCAGetResourceTypes(m_deviceHandle, + "oic/fake", &resourceTypes, &resourceTypeCount)); + EXPECT_EQ(0, resourceTypeCount); + EXPECT_EQ(nullptr, resourceTypes); + + EXPECT_EQ(IPCA_OK, IPCAGetResourceTypes(m_deviceHandle, + ELEVATOR_RESOURCE_PATH, &resourceTypes, &resourceTypeCount)); + EXPECT_LT(0, resourceTypeCount); // At least 1 function. + elevatorResourceTypeFound = false; + for (i = 0; i < resourceTypeCount; i++) + { + std::string rt = resourceTypes[i]; + if (rt.find(ELEVATOR_KEYWORD) != std::string::npos) + { + elevatorResourceTypeFound = true; + break; + } + } + EXPECT_TRUE(elevatorResourceTypeFound); + IPCAFreeStringArray(resourceTypes, resourceTypeCount); // free the IPCA allocated memory. + + return IPCA_OK; +} + +IPCAStatus IPCAElevatorClient::ConfirmResourceInterfaces() +{ + // There should be at least 2 interfaces (ELEVATOR_CO_PRIVATE_INTERFACE and DEFAULT_INTERFACE). + char** resourceInterfaces; + size_t resourceInterfaceCount; + EXPECT_EQ(IPCA_OK, IPCAGetResourceInterfaces(m_deviceHandle, + nullptr, &resourceInterfaces, &resourceInterfaceCount)); + EXPECT_LT(1, resourceInterfaceCount); + + // Should return only 1 resource with ELEVATOR_CO_PRIVATE_INTERFACE interface. + char** resourcePathList; + size_t resourcePathCount; + EXPECT_EQ(IPCA_OK, IPCAGetResources(m_deviceHandle, + ELEVATOR_CO_PRIVATE_INTERFACE, + nullptr, + &resourcePathList, + &resourcePathCount)); + EXPECT_EQ(1, resourcePathCount); + EXPECT_STREQ(ELEVATOR_CO_RESOURCE_PATH, resourcePathList[0]); + IPCAFreeStringArray(resourcePathList, resourcePathCount); + + // Should return 0 resource with non existence interface. + EXPECT_EQ(IPCA_OK, IPCAGetResources(m_deviceHandle, + ELEVATOR_RESOURCE_MADE_UP_INTERFACE, + nullptr, + &resourcePathList, + &resourcePathCount)); + EXPECT_EQ(0, resourcePathCount); + IPCAFreeStringArray(resourcePathList, resourcePathCount); + + // Should be at least 4 resouces with DEFAULT_INTERFACE created in ElevatorServer::Start(). + EXPECT_EQ(IPCA_OK, IPCAGetResources(m_deviceHandle, + OC_RSRVD_INTERFACE_DEFAULT, + nullptr, + &resourcePathList, + &resourcePathCount)); + EXPECT_LT(3, resourcePathCount); + IPCAFreeStringArray(resourcePathList, resourcePathCount); + + return IPCA_OK; +} + +void IPCAElevatorClient::DiscoverElevator1Callback( + void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* discoveredDeviceInfo) +{ + OC_UNUSED(deviceStatus); + OC_UNUSED(context); + + if (g_elevator1Name.compare(discoveredDeviceInfo->deviceName) == 0) + { + m_discoveredElevator1DeviceUris.clear(); + for (int i = 0; i < discoveredDeviceInfo->deviceUriCount; i++) + { + m_discoveredElevator1DeviceUris.push_back(discoveredDeviceInfo->deviceUris[i]); + } + + m_discoveredElevator1DeviceId = discoveredDeviceInfo->deviceId; + m_discoveredElevator1DeviceName = discoveredDeviceInfo->deviceId; + m_elevator1Discovered = true; + m_deviceDiscoveredCV.notify_all(); + } +} + +void C_DiscoverElevator1Cb(void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* discoveredDeviceInfo) +{ + IPCAElevatorClient* ipcaTest = (IPCAElevatorClient*)context; + ipcaTest->DiscoverElevator1Callback(context, deviceStatus, discoveredDeviceInfo); +} + +void IPCAElevatorClient::DiscoverElevator1() +{ + // Resource types to work with, an elevator device with vertical function (ipca.test.elevator) + // and standard OCF resource type (carbon monoxide detection). + const char* RequiredResourceTypes[] = { + ELEVATOR_RESOURCE_TYPE, + ELEVATOR_CO_RESOURCE_TYPE, + ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH_TYPE, + ELEVATOR_RESOURCE_DELETE_TYPE + }; + + const int ResourceTypeCount = sizeof(RequiredResourceTypes) / sizeof(char*); + + // Start discovery. + IPCAStatus status = IPCADiscoverDevices( + m_ipcaAppHandle, + &C_DiscoverElevator1Cb, + (void*)this, + RequiredResourceTypes, + ResourceTypeCount, + &m_deviceDiscoveryHandle); + + ASSERT_EQ(IPCA_OK, status); + + // Wait for the callback. + std::unique_lock lock(m_deviceDiscoveredCVMutex); + m_deviceDiscoveredCV.wait_for( + lock, + std::chrono::seconds(10), + [this]{ return IsElevator1Discovered(); }); + + ASSERT_EQ(true, IsElevator1Discovered()); + ASSERT_EQ(IPCA_OK, IPCAOpenDevice(m_ipcaAppHandle, + m_discoveredElevator1DeviceId.c_str(), &m_deviceHandle)); +} + +void IPCAElevatorClient::SetPropertiesCallback(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + OC_UNUSED(propertyBagHandle); + OC_UNUSED(context); + OC_UNUSED(result); + + m_setPropertiesCallbackCalled = true; + m_setPropertiesCompleteCV.notify_all(); +} + +void IPCAElevatorClient::GetPropertiesCallback(IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle) +{ + OC_UNUSED(context); + + m_getPropertiesCallbackCalled = true; + m_statusOfLastCallback = result; + + if (propertyBagHandle != nullptr) + { + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_CURRENT_FLOOR, &m_currentFloor)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_DIRECTION, &m_direction)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_TARGET_FLOOR, &m_targetFloor)); + } + + m_getDataCompleteCbCV.notify_all(); +} + +void IPCAElevatorClient::CreateResourceCallback(IPCAStatus result, + const char* newResourcePath, + IPCAPropertyBagHandle propertyBagHandle) +{ + OC_UNUSED(result); + OC_UNUSED(propertyBagHandle); + + m_newResourcePath = (newResourcePath != nullptr) ? newResourcePath : ""; + m_createResourceCallbackCalled = true; + m_createResourceCompleteCV.notify_all(); +} + +void IPCAElevatorClient::DeleteResourceCallback(IPCAStatus result) +{ + OC_UNUSED(result); + + m_deleteResourceCallbackCalled = true; + m_deleteResourceCompleteCV.notify_all(); +} + +void IPCAElevatorClient::ResourceChangeNotificationCallback(IPCAStatus result, + IPCAPropertyBagHandle propertyBagHandle) +{ + EXPECT_EQ(IPCA_OK, result); + + IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_CURRENT_FLOOR, &m_observedCurrentFloor); + IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_DIRECTION, &m_observedDirection); + IPCAPropertyBagGetValueInt(propertyBagHandle, + ELEVATOR_PROPERTY_TARGET_FLOOR, &m_observedTargetFloor); + + m_resourceChangeCbCV.notify_all(); +} diff --git a/resource/IPCA/unittests/IPCAElevatorClient.h b/resource/IPCA/unittests/IPCAElevatorClient.h new file mode 100644 index 0000000..1c771be --- /dev/null +++ b/resource/IPCA/unittests/IPCAElevatorClient.h @@ -0,0 +1,148 @@ +/****************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef IPCA_ELEVATOR_CLIENT_H +#define IPCA_ELEVATOR_CLIENT_H + +/** + * Class that asssists testing IPCA apis. + */ +class IPCAElevatorClient : public testing::Test +{ +public: + // Discovery functionalities + bool IsElevator1Discovered() { return m_elevator1Discovered; } + IPCAStatus ConfirmDeviceAndPlatformInfo(); + IPCAStatus ConfirmResources(); + IPCAStatus ConfirmResourceTypes(); + IPCAStatus ConfirmResourceInterfaces(); + + // Use IPCA to get the elevator's current floor. + void GetCurrentFloor(int* currentFloor, bool* result); + void GetTargetFloor(int* targetFloor, bool* result); + void SetTargetFloor(int targetFloor, bool* result); + void CreateResourceRelativePath(); + const char* GetNewResourceURI() { return m_newResourcePath.c_str(); } + void CreateResouceExplicitPath(); + void DeleteResource(); + + // These are values updated by notifications. + int GetObservedCurrentFloor() { return m_observedCurrentFloor; } + int GetObservedTargetFloor() { return m_observedTargetFloor; } + int GetObservedDirection() { return m_observedDirection; } + + // Use IPCA to start/stop observing elevator resource. + IPCAHandle m_observeHandle; + std::mutex m_resourceChangeCbMutex; + std::condition_variable m_resourceChangeCbCV; + bool StartObserve(); + void StopObserve(); + + // Helper functions + IPCAStatus FactoryResetElevator(); + IPCAStatus RebootElevator(); + + void DiscoverElevator1(); + void DiscoverElevator1Callback( + void* context, + IPCADeviceStatus deviceStatus, + const IPCADiscoveredDeviceInfo* discoveredDeviceInfo); + + void GetPropertiesCallback( + IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); + + void SetPropertiesCallback( + IPCAStatus result, + void* context, + IPCAPropertyBagHandle propertyBagHandle); + + void CreateResourceCallback( + IPCAStatus result, + const char* newResourcePath, + IPCAPropertyBagHandle propertyBagHandle); + + void DeleteResourceCallback(IPCAStatus result); + + void ResourceChangeNotificationCallback( + IPCAStatus result, + IPCAPropertyBagHandle propertyBagHandle); + + // Failure cases. + IPCAStatus GetUnknownResource(); + IPCAStatus SetUnknownResource(); + bool SetUnknoownInterface(); + +private: + IPCAAppHandle m_ipcaAppHandle; + IPCAHandle m_deviceDiscoveryHandle; + IPCADeviceHandle m_deviceHandle; + + int m_currentFloor; + int m_targetFloor; + int m_direction; + + int m_observedCurrentFloor; + int m_observedDirection; + int m_observedTargetFloor; + + // Used by IPCADiscoverDevices() tests. + bool m_elevator1Discovered; + std::string m_discoveredElevator1DeviceName; + std::string m_discoveredElevator1DeviceId; + std::vector m_discoveredElevator1DeviceUris; + std::mutex m_deviceDiscoveredCVMutex; + std::condition_variable m_deviceDiscoveredCV; // conditional variable to wake up thread waiting + // for device discovered callback. + + // Used by IPCAGetProperties() tests. + bool m_getPropertiesCallbackCalled; + IPCAStatus m_statusOfLastCallback; // the IPCAStatus of the last callback. + std::mutex m_getDataCompleteCbMutex; + std::condition_variable m_getDataCompleteCbCV; + + // Used by IPCASetProperties() tests. + bool m_setPropertiesCallbackCalled; + std::mutex m_setPropertiesCompleteCbMutex; + std::condition_variable m_setPropertiesCompleteCV; + + // Used by IPCACreateResource() tests + std::string m_newResourcePath; + bool m_createResourceCallbackCalled; + std::mutex m_createResourceCompleteCbMutex; + std::condition_variable m_createResourceCompleteCV; + + // Used by IPCADeleteResource() tests + bool m_deleteResourceCallbackCalled; + std::mutex m_deleteResourceCompleteCbMutex; + std::condition_variable m_deleteResourceCompleteCV; + + bool GetElevatorProperties(); + bool SetElevatorProperties(IPCAPropertyBagHandle propertyBagHandle); + bool CreateElevatorResource( + std::string resourcePath, IPCAPropertyBagHandle propertyBagHandle); + bool DeleteElevatorResource(); + +protected: + virtual void SetUp(); + virtual void TearDown(); +}; + +#endif // IPCA_ELEVATOR_CLIENT_H diff --git a/resource/IPCA/unittests/IPCAUnitTest.dat b/resource/IPCA/unittests/IPCAUnitTest.dat new file mode 100644 index 0000000000000000000000000000000000000000..180fa6779699658ddfb154bf9bb7d0a9f5feedae GIT binary patch literal 1839 zcmbtUv2xom47Jl-u78oGQ}0a4@-;b+?H6>&kc<=wMX)7NN7A-3+Sh(Yhqms}Kj@Y( z$k5N|-i*D3pkl{WoJ4M^6Cn@+`1kuCMfx9 z)L_&^JIjZqOf@8Fpil&EHP&`Ma^Gw=W~5>=M^Z6iStc}OaP`NH+AHy1l6=xTAL%?6 z>dqtP1~M$b8(jpwJwUk`pxh}y<`EbWfjcgR@nAJ0%|UMhx!-?Og3i$xRK?z-F245mVX=t6cgC>IGfq!}=8dGf<96vx{ z0xSg&EEpTO+MaT&G^NK@j0}tnY;9;u&cR5lSpk}w%~R8N6!eLj=0r>MJYTxtOXc*_ zcZo)o*RjYuv30M?_hXaqk0eY{K>Hp43F32AE2$$+e_fI=^G-qnvW_sqc7Z67*E<8v z+{t!ibLX?^iYa~0P+6R!y;6~*eVE0!ge1!D=5^byPgC-=7bSGZ zrAAkXAi3pVQYeIySe#qBFx*AQRtrq?nWTT-zio`f-EF93br^;HH{sDBJPD6NH-ab} m4#Qy-o +#include +#include +#include + +#include "gtest/gtest.h" +#include "ocrandom.h" +#include "ipca.h" +#include "testelevatorserver.h" +#include "testelevatorclient.h" +#include "ipcatestdata.h" +#include "ipcaelevatorclient.h" + +using namespace std; +using namespace std::placeholders; + +// Implemented in ipca.dll. +void IPCASetUnitTestMode(); + +// IPCA test app info. +IPCAUuid IPCATestAppUuid = {0x84, 0x71, 0x88, 0x78, 0xe6, 0x91, 0x4b, 0xf4, + 0xa9, 0x57, 0x04, 0xe0, 0x1b, 0x9b, 0x59, 0xd1}; +char IPCATestAppName[] = "IPCA Unittests"; + +// IoTivity supports 1 server per instance. +ElevatorServer g_testElevator1; + +// The elevator's name is suffixed with random number to differentiate it from elevators from +// other unit tests. +std::string g_elevator1Name; + +/** + * Start elevator 1. + */ +bool StartElevator1() +{ + // One time generation of the elevator's device name. + if (g_elevator1Name.length() == 0) + { + char uuidString[37] = {}; + uint8_t uuid[UUID_SIZE] = {}; + OCGenerateUuid(uuid); + OCConvertUuidToString(uuid, uuidString); + + g_elevator1Name = "IPCA Test Elevator "; + + // e.g., IPCA Test Elevator 2923be84-e16c-d6ae-5290-49f1f1bbe9eb + g_elevator1Name.append(uuidString); + } + + bool result = g_testElevator1.Start(g_elevator1Name); + return result; +} + +/** + * Stop elevator 1. + */ +void StopElevator1() +{ + g_testElevator1.Stop(); +} + +// Start the test elevator once and use it through out the rest of the tests. +TEST(ElevatorServerStart, start) +{ + IPCASetUnitTestMode(); + ASSERT_TRUE(StartElevator1()); +} + +// Test IoTivity api directly before subsequent tests which use IPCA apis. +TEST(IoTivityDirect, IsIoTivityWorking) +{ + ElevatorClient elevatorClient; + int loopCount; + + // Able to find the elevator server. + elevatorClient.FindElevator(g_elevator1Name); + loopCount = 0; + while ((loopCount++ < 100) && (elevatorClient.IsElevatorFound() == false)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + ASSERT_EQ(true, elevatorClient.IsElevatorFound()); + + // Confirm able to set target floor using IoTivity client API and read the value back from the + // elevator server. + loopCount = 0; + const int TARGET_FLOOR = 3; + elevatorClient.SetTargetFloor(TARGET_FLOOR); + while ((loopCount++ < 20) && (g_testElevator1.GetCurrentFloor() != TARGET_FLOOR)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + EXPECT_EQ(TARGET_FLOOR, g_testElevator1.GetCurrentFloor()); + + // Confirm able to get current floor using IoTivity client API. The current floor should be + // TARGET_FLOOR. + int m_currentFloor; + elevatorClient.GetCurrentFloor(&m_currentFloor); + EXPECT_EQ(g_testElevator1.GetCurrentFloor(), m_currentFloor); + + // Confirm observation by setting the target floor to NEW_TARGET_FLOOR. + // The client should receive current floor notification that it reaches the NEW_TARGET_FLOOR. + const int NEW_TARGET_FLOOR = 8; + bool returnValue = elevatorClient.StartObservation(); + EXPECT_TRUE(returnValue); + elevatorClient.SetTargetFloor(NEW_TARGET_FLOOR); + while ((loopCount++ < 20) && (elevatorClient.GetObservedCurrentFloor() != NEW_TARGET_FLOOR)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + EXPECT_EQ(NEW_TARGET_FLOOR, elevatorClient.GetObservedCurrentFloor()); + returnValue = elevatorClient.StopObservation(); + EXPECT_TRUE(returnValue); +} + +/* + * IPCA property bag tests. + */ +class IPCAPropertyBagTest : public testing::Test +{ + public: + IPCAPropertyBagHandle m_propertyBagHandle; + + // Create a property bag containing an integar value. + void CreateIntPropertyBag(IPCAPropertyBagHandle& propertyBagHandle, const char* key, int value) + { + ASSERT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle, key, value)); + } + + public: + int m_intValue; + double m_doubleValue; + char* m_charPointerValue; + + protected: + virtual void SetUp() + { + ASSERT_EQ(IPCA_OK, IPCAPropertyBagCreate(&m_propertyBagHandle)); + } + virtual void TearDown() + { + IPCAPropertyBagDestroy(m_propertyBagHandle); + } +}; + +TEST_F(IPCAPropertyBagTest, PropertyBagNonExistenceAttribute) +{ + EXPECT_EQ(IPCA_FAIL, IPCAPropertyBagGetValueInt(m_propertyBagHandle, + "NonexistenceAttribute", &m_intValue)); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagInt) +{ + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(m_propertyBagHandle, "IntValue", 3)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(m_propertyBagHandle, "IntValue", &m_intValue)); + EXPECT_EQ(3, m_intValue); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagDouble) +{ + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueDouble(m_propertyBagHandle, + "DoubleValue", 12345678)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagGetValueDouble(m_propertyBagHandle, + "DoubleValue", &m_doubleValue)); + EXPECT_EQ(12345678, m_doubleValue); + + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueDouble(m_propertyBagHandle, + "DoubleValue", 1234.5678)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagGetValueDouble(m_propertyBagHandle, + "DoubleValue", &m_doubleValue)); + EXPECT_EQ(1234.5678, m_doubleValue); + + EXPECT_EQ(IPCA_FAIL, IPCAPropertyBagGetValueDouble(m_propertyBagHandle, + "doubleValue", &m_doubleValue)); // incorrect capital case +} + +TEST_F(IPCAPropertyBagTest, PropertyBagBool) +{ + bool trueBool, falseBool; + + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueBool(m_propertyBagHandle, + "TrueBoolValue", true)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagGetValueBool(m_propertyBagHandle, + "TrueBoolValue", &trueBool)); + EXPECT_TRUE(trueBool); + + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueBool(m_propertyBagHandle, + "FalseBoolValue", false)); + ASSERT_EQ(IPCA_OK, IPCAPropertyBagGetValueBool(m_propertyBagHandle, + "FalseBoolValue", &falseBool)); + EXPECT_FALSE(falseBool); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagString) +{ + ASSERT_EQ(IPCA_OK, IPCAPropertyBagSetValueString(m_propertyBagHandle, + "MyString", "Hello World")); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueString(m_propertyBagHandle, + "MyString", &m_charPointerValue)); + EXPECT_STREQ("Hello World", m_charPointerValue); + IPCAPropertyBagFreeString(m_charPointerValue); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagPropertyBagType) +{ + IPCAPropertyBagHandle propertyBagHandle1, propertyBagHandle2, propertyBagHandle3; + CreateIntPropertyBag(propertyBagHandle1, "IntValue1", 1); + CreateIntPropertyBag(propertyBagHandle2, "IntValue2", 2); + CreateIntPropertyBag(propertyBagHandle3, "IntValue3", 3); + + // Add propertyBagHandle1 to 3 to propertyBagHandle. + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(m_propertyBagHandle, + "PropertyBag1", propertyBagHandle1)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(m_propertyBagHandle, + "PropertyBag2", propertyBagHandle2)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(m_propertyBagHandle, + "PropertyBag3", propertyBagHandle3)); + + // The original is no longer needed. + IPCAPropertyBagDestroy(propertyBagHandle1); + IPCAPropertyBagDestroy(propertyBagHandle2); + IPCAPropertyBagDestroy(propertyBagHandle3); + + // Read back each property bag object and compare with the original values. + IPCAPropertyBagHandle propertyBag1; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(m_propertyBagHandle, + "PropertyBag1", &propertyBag1)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag1, + "IntValue1", &m_intValue)); + EXPECT_EQ(1, m_intValue); + IPCAPropertyBagDestroy(propertyBag1); + + IPCAPropertyBagHandle propertyBag2; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(m_propertyBagHandle, + "PropertyBag2", &propertyBag2)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag2, + "IntValue2", &m_intValue)); + EXPECT_EQ(2, m_intValue); + IPCAPropertyBagDestroy(propertyBag2); + + IPCAPropertyBagHandle propertyBag3; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(m_propertyBagHandle, + "PropertyBag3", &propertyBag3)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag3, + "IntValue3", &m_intValue)); + EXPECT_EQ(3, m_intValue); + + IPCAPropertyBagDestroy(propertyBag3); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagIntArrayType) +{ + // array of int + int intArray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueIntArray(m_propertyBagHandle, + "IntArray", intArray, 10)); + + int* readBackIntArray; + size_t readBackIntArraySize; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueIntArray(m_propertyBagHandle, + "IntArray", &readBackIntArray, &readBackIntArraySize)); + EXPECT_EQ(10, readBackIntArraySize); + EXPECT_EQ(0, memcmp(intArray, readBackIntArray, readBackIntArraySize)); + + IPCAPropertyBagFreeIntArray(readBackIntArray); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagDoubleArrayType) +{ + // array of double + double doubleArray[] = {1.01, 2.02, 3.03, 4.04, 5.05, 6.06, 7.07, 8.08, 9.09, 10.010}; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueDoubleArray(m_propertyBagHandle, + "DoubleArray", doubleArray, 10)); + + double* readBackDoubleArray; + size_t readBackDoubleArraySize; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueDoubleArray(m_propertyBagHandle, + "DoubleArray", &readBackDoubleArray, &readBackDoubleArraySize)); + EXPECT_EQ(10, readBackDoubleArraySize); + EXPECT_EQ(0, memcmp(doubleArray, readBackDoubleArray, readBackDoubleArraySize)); + + IPCAPropertyBagFreeDoubleArray(readBackDoubleArray); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagBoolArrayType) +{ + // array of bool + bool boolArray[] = {true, true, false, false, true, false, true, false, false, false}; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueBoolArray(m_propertyBagHandle, + "boolArray", boolArray, 10)); + + bool* readBackBoolArray; + size_t readBackBoolArraySize; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueBoolArray(m_propertyBagHandle, + "boolArray", &readBackBoolArray, &readBackBoolArraySize)); + EXPECT_EQ(10, readBackBoolArraySize); + EXPECT_EQ(0, memcmp(boolArray, readBackBoolArray, readBackBoolArraySize)); + + IPCAPropertyBagFreeBoolArray(readBackBoolArray); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagStringArrayType) +{ + // array of string + char* stringArray[] = {"hello world 1", "hello world 2", "hello world 3"}; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueStringArray(m_propertyBagHandle, + "stringArray", (const char**)stringArray, 3)); + + char** readBackString; + size_t readbackStringSize; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueStringArray(m_propertyBagHandle, + "stringArray", &readBackString, &readbackStringSize)); + ASSERT_EQ(3, readbackStringSize); + EXPECT_STREQ(stringArray[0], readBackString[0]); + EXPECT_STREQ(stringArray[1], readBackString[1]); + EXPECT_STREQ(stringArray[2], readBackString[2]); + IPCAPropertyBagFreeStringArray(readBackString, readbackStringSize); +} + +TEST_F(IPCAPropertyBagTest, PropertyBagPropertyBagArrayType) +{ + IPCAPropertyBagHandle propertyBagHandle1, propertyBagHandle1A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle1)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle1A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle1A, "IntKey1", 1)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueString(propertyBagHandle1A, "StringKey1", "One")); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(propertyBagHandle1, + "values", propertyBagHandle1A)); + + IPCAPropertyBagHandle propertyBagHandle2, propertyBagHandle2A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle2)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle2A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle2A, "IntKey2", 2)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueString(propertyBagHandle2A, "StringKey2", "Two")); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(propertyBagHandle2, + "values", propertyBagHandle2A)); + + IPCAPropertyBagHandle propertyBagHandle3, propertyBagHandle3A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle3)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagCreate(&propertyBagHandle3A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueInt(propertyBagHandle3A, "IntKey3", 3)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValueString(propertyBagHandle3A, "StringKey3", "Three")); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBag(propertyBagHandle3, + "values", propertyBagHandle3A)); + + IPCAPropertyBagHandle propertyBagArray[] = { + propertyBagHandle1, propertyBagHandle2, propertyBagHandle3 }; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagSetValuePropertyBagArray(m_propertyBagHandle, + "PropertyBagArray", propertyBagArray, 3)); + + // the original is no longer needed. + IPCAPropertyBagDestroy(propertyBagHandle3); + IPCAPropertyBagDestroy(propertyBagHandle3A); + IPCAPropertyBagDestroy(propertyBagHandle2); + IPCAPropertyBagDestroy(propertyBagHandle2A); + IPCAPropertyBagDestroy(propertyBagHandle1); + IPCAPropertyBagDestroy(propertyBagHandle1A); + + // Read back each property bag object and compare with the original values. + IPCAPropertyBagHandle* propertyBagReadBackArray; + size_t arrayCount; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBagArray(m_propertyBagHandle, + "PropertyBagArray", &propertyBagReadBackArray, &arrayCount)); + ASSERT_EQ(3, arrayCount); + + IPCAPropertyBagHandle propertyBag1 = propertyBagReadBackArray[0], propertyBag1A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(propertyBag1, "values", &propertyBag1A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag1A, "IntKey1", &m_intValue)); + EXPECT_EQ(1, m_intValue); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueString(propertyBag1A, + "StringKey1", &m_charPointerValue)); + EXPECT_STREQ("One", m_charPointerValue); + + IPCAPropertyBagHandle propertyBag2 = propertyBagReadBackArray[1], propertyBag2A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(propertyBag2, "values", &propertyBag2A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag2A, "IntKey2", &m_intValue)); + EXPECT_EQ(2, m_intValue); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueString(propertyBag2A, + "StringKey2", &m_charPointerValue)); + EXPECT_STREQ("Two", m_charPointerValue); + + IPCAPropertyBagHandle propertyBag3 = propertyBagReadBackArray[2], propertyBag3A; + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValuePropertyBag(propertyBag3, "values", &propertyBag3A)); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueInt(propertyBag3A, "IntKey3", &m_intValue)); + EXPECT_EQ(3, m_intValue); + EXPECT_EQ(IPCA_OK, IPCAPropertyBagGetValueString(propertyBag3A, + "StringKey3", &m_charPointerValue)); + EXPECT_STREQ("Three", m_charPointerValue); + + IPCAPropertyBagDestroy(propertyBag1A); + IPCAPropertyBagDestroy(propertyBag2A); + IPCAPropertyBagDestroy(propertyBag3A); +} + +/* + * Test multiple IPCAOpen(). + */ + +class IPCAMiscTest : public testing::Test +{ + public: + IPCAAppHandle m_ipcaAppHandle; + IPCAAppHandle m_anotherIPCAAppHandle; + IPCAAppInfo m_ipcaAppInfo; + + IPCAStatus DoAnotherIPCAOpen() + { + return IPCAOpen(&m_ipcaAppInfo, IPCA_VERSION_1, &m_anotherIPCAAppHandle); + } + + protected: + virtual void SetUp() + { + m_anotherIPCAAppHandle = NULL; + m_ipcaAppInfo = { IPCATestAppUuid, IPCATestAppName, "1.0.0", "Microsoft" }; + ASSERT_EQ(IPCA_OK, IPCAOpen(&m_ipcaAppInfo, IPCA_VERSION_1, &m_ipcaAppHandle)); + } + virtual void TearDown() + { + if (m_ipcaAppHandle != NULL) + { + IPCAClose(m_ipcaAppHandle); + m_ipcaAppHandle = NULL; + } + + if (m_anotherIPCAAppHandle != NULL) + { + IPCAClose(m_anotherIPCAAppHandle); + m_anotherIPCAAppHandle = NULL; + } + } +}; + +TEST_F(IPCAMiscTest, ShouldNotAllowMultipleCallsToIPCOpen) +{ + EXPECT_EQ(IPCA_ALREADY_OPENED, DoAnotherIPCAOpen()); +} + +TEST_F(IPCAMiscTest, IPCAOpenShouldBeAllowedAfterIPCAClose) +{ + IPCAClose(m_ipcaAppHandle); + m_ipcaAppHandle = NULL; + + ASSERT_EQ(IPCA_OK, DoAnotherIPCAOpen()); +} + +/* + * IPCADiscoverDevices() must be called before IPCAOpenDevice(). + */ +TEST_F(IPCAMiscTest, ShouldFailOpenDeviceWithoutDiscoveryFirst) +{ + IPCADeviceHandle deviceHandle; + + // UUID is just random. + EXPECT_EQ(IPCA_DEVICE_NOT_DISCOVERED, IPCAOpenDevice(m_ipcaAppHandle, + "eee01972-8a91-11e6-ae22-56b6b6499611", + &deviceHandle)); +} + +TEST_F(IPCAElevatorClient, DiscoveryShouldFindElevatorServer) +{ + EXPECT_TRUE(IsElevator1Discovered()); +} + +TEST_F(IPCAElevatorClient, DiscoveryShouldFindDeviceAndPlatformInfo) +{ + EXPECT_EQ(IPCA_OK, ConfirmDeviceAndPlatformInfo()); +} + +TEST_F(IPCAElevatorClient, DiscoveryShouldFindElevatorResources) +{ + EXPECT_EQ(IPCA_OK, ConfirmResources()); +} + +TEST_F(IPCAElevatorClient, DiscoveryShouldFindTargetedResourceTypes) +{ + EXPECT_EQ(IPCA_OK, ConfirmResourceTypes()); +} + +TEST_F(IPCAElevatorClient, ShouldBeAbleToFilterOnResourceInterface) +{ + EXPECT_EQ(IPCA_OK, ConfirmResourceInterfaces()); +} + +TEST_F(IPCAElevatorClient, SuccessfullyGetDataFromElevatorServer) +{ + // Directly set target floor of the elevator to 8. + g_testElevator1.SetTargetFloor(8); + + int elevatorTargetFloor; + bool result; + + GetTargetFloor(&elevatorTargetFloor, &result); + ASSERT_TRUE(result); + EXPECT_EQ(8, elevatorTargetFloor); +} + +TEST_F(IPCAElevatorClient, ShouldFailGetUnknownResource) +{ + EXPECT_EQ(IPCA_RESOURCE_NOT_FOUND, GetUnknownResource()); +} + +TEST_F(IPCAElevatorClient, ShouldFailSetUnknownResource) +{ + EXPECT_EQ(IPCA_RESOURCE_NOT_FOUND, SetUnknownResource()); +} + +TEST_F(IPCAElevatorClient, ShouldFailSetWithUnknownInterface) +{ + size_t incorrectInterfaceCount = g_testElevator1.GetIncorrectInterfaceCount(); + EXPECT_EQ(true, SetUnknoownInterface()); + EXPECT_EQ((incorrectInterfaceCount + 1), g_testElevator1.GetIncorrectInterfaceCount()); +} + +TEST_F(IPCAElevatorClient, SuccessfullySetElevatorServerProperties) +{ + g_testElevator1.SetTargetFloor(1); // Set to known target floor. + + bool result; + SetTargetFloor(8, &result); // Set it to 8th floor. + ASSERT_EQ(true, result); + + // Now read back directly from the elevator. + int newTargetFloor = g_testElevator1.GetTargetFloor(); + EXPECT_EQ(8, newTargetFloor); +} + +TEST_F(IPCAElevatorClient, SuccessfullyCreateAndDeleteResources) +{ + // Do a few rounds of create resource with relative path. + size_t beforeCreateCount, afterCreateCount; + for (int i = 0 ; i < 3 ; i++) + { + beforeCreateCount = g_testElevator1.GetRelativePathResourceCreateCount(); + CreateResourceRelativePath(); + afterCreateCount = g_testElevator1.GetRelativePathResourceCreateCount(); + ASSERT_EQ(beforeCreateCount + 1, afterCreateCount); + // @todo: when IOT-1819 is resolved. + // EXPECT_STREQ(ELEVATOR_RESOURCE_NEW_RESOURCE_PATH, GetNewResourceURI()); + } + + // Delete resource + size_t beforeDeleteCount, afterDeleteCount; + for (int i = 0 ; i < 3 ; i++) + { + beforeDeleteCount = g_testElevator1.GetDeleteResourceCount(); + DeleteResource(); // the test elevator simply increments count, + // therefore can be deleted multiple times + afterDeleteCount = g_testElevator1.GetDeleteResourceCount(); + ASSERT_EQ(beforeDeleteCount + 1, afterDeleteCount); + } +} + +TEST_F(IPCAElevatorClient, SuccessfullyReceiveResourceChangeNotifications) +{ + // Set to known target floor. + g_testElevator1.SetTargetFloor(1); + + // Start observing + ASSERT_EQ(true, StartObserve()); + + // Use IPCA to set the target floor. + bool result; + SetTargetFloor(10, &result); // Set to floor 10 + ASSERT_EQ(true, result); + + // Wait until observed current floor is set to targeted floor. + std::unique_lock lock(m_resourceChangeCbMutex); + m_resourceChangeCbCV.wait_for( + lock, + std::chrono::seconds(10), + [this] { return GetObservedCurrentFloor() == 10; }); + + EXPECT_EQ(10, GetObservedCurrentFloor()); // check it is at floor 10. + + StopObserve(); +} + +TEST_F(IPCAElevatorClient, SuccessfulFactoryReset) +{ + EXPECT_EQ(IPCA_OK, FactoryResetElevator()); +} + +TEST_F(IPCAElevatorClient, SuccessfulReboot) +{ + EXPECT_EQ(IPCA_OK, RebootElevator()); +} + + +TEST(ElevatorServerStop, Stop) +{ + StopElevator1(); +} diff --git a/resource/IPCA/unittests/mockInProcClientWrapper.cpp b/resource/IPCA/unittests/mockInProcClientWrapper.cpp new file mode 100644 index 0000000..698b290 --- /dev/null +++ b/resource/IPCA/unittests/mockInProcClientWrapper.cpp @@ -0,0 +1,296 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "OCPlatform_impl.h" +#include +#include +#include +#include "ipcatestdata.h" + +namespace OC +{ + // This is mocked because OCPlatform_impl instantiates the client and the server wrappers. + // There is no implementation in this file since the mock directly links the client and server + // apps. + InProcClientWrapper::InProcClientWrapper( + std::weak_ptr csdkLock, PlatformConfig cfg) + : m_threadRun(false), m_csdkLock(csdkLock), + m_cfg { cfg } + { + } + + InProcClientWrapper::~InProcClientWrapper() + { + } + + OCStackResult InProcClientWrapper::start() + { + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::stop() + { + return OC_STACK_OK; + } + + void InProcClientWrapper::listeningFunc() + { + } + + OCStackResult InProcClientWrapper::ListenForResource( + const std::string& serviceUrl, + const std::string& resourceType, + OCConnectivityType connectivityType, + FindCallback& callback, QualityOfService QoS) + { + OC_UNUSED(serviceUrl); + OC_UNUSED(resourceType); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::ListenErrorForResource( + const std::string& serviceUrl, + const std::string& resourceType, + OCConnectivityType connectivityType, + FindCallback& callback, FindErrorCallback& errorCallback, + QualityOfService QoS) + { + OC_UNUSED(serviceUrl); + OC_UNUSED(resourceType); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(errorCallback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + + OCStackResult InProcClientWrapper::ListenForResourceList( + const std::string& serviceUrl, + const std::string& resourceType, + OCConnectivityType connectivityType, + FindResListCallback& callback, QualityOfService QoS) + { + OC_UNUSED(serviceUrl); + OC_UNUSED(resourceType); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::ListenForResourceListWithError( + const std::string& serviceUrl, + const std::string& resourceType, + OCConnectivityType connectivityType, + FindResListCallback& callback, + FindErrorCallback& errorCallback, QualityOfService QoS) + { + OC_UNUSED(serviceUrl); + OC_UNUSED(resourceType); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(errorCallback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::ListenForDevice( + const std::string& serviceUrl, + const std::string& deviceURI, + OCConnectivityType connectivityType, + FindDeviceCallback& callback, + QualityOfService QoS) + { + OC_UNUSED(serviceUrl); + OC_UNUSED(deviceURI); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::GetResourceRepresentation( + const OCDevAddr& devAddr, + const std::string& resourceUri, + const QueryParamsMap& queryParams, const HeaderOptions& headerOptions, + OCConnectivityType connectivityType, + GetCallback& callback, QualityOfService QoS) + { + OC_UNUSED(devAddr); + OC_UNUSED(resourceUri); + OC_UNUSED(queryParams); + OC_UNUSED(headerOptions); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::PutResourceRepresentation( + const OCDevAddr& devAddr, + const std::string& uri, + const OCRepresentation& rep, + const QueryParamsMap& queryParams, const HeaderOptions& headerOptions, + PutCallback& callback, QualityOfService QoS) + { + OC_UNUSED(devAddr); + OC_UNUSED(uri); + OC_UNUSED(rep); + OC_UNUSED(queryParams); + OC_UNUSED(headerOptions); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::PostResourceRepresentation( + const OCDevAddr& devAddr, + const std::string& uri, + const OCRepresentation& rep, + const QueryParamsMap& queryParams, const HeaderOptions& headerOptions, + OCConnectivityType connectivityType, + PostCallback& callback, QualityOfService QoS) + { + OC_UNUSED(devAddr); + OC_UNUSED(uri); + OC_UNUSED(rep); + OC_UNUSED(queryParams); + OC_UNUSED(headerOptions); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + + OCStackResult InProcClientWrapper::DeleteResource( + const OCDevAddr& devAddr, + const std::string& uri, + const HeaderOptions& headerOptions, + OCConnectivityType connectivityType, + DeleteCallback& callback, + QualityOfService /*QoS*/) + { + OC_UNUSED(devAddr); + OC_UNUSED(uri); + OC_UNUSED(headerOptions); + OC_UNUSED(connectivityType); + OC_UNUSED(callback); + + return OC_STACK_OK; + } + + + OCStackResult InProcClientWrapper::ObserveResource(ObserveType observeType, OCDoHandle* handle, + const OCDevAddr& devAddr, + const std::string& uri, + const QueryParamsMap& queryParams, const HeaderOptions& headerOptions, + ObserveCallback& callback, QualityOfService QoS) + + { + OC_UNUSED(observeType); + OC_UNUSED(handle); + OC_UNUSED(devAddr); + OC_UNUSED(uri); + OC_UNUSED(queryParams); + OC_UNUSED(headerOptions); + OC_UNUSED(callback); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::CancelObserveResource( + OCDoHandle handle, + const std::string& /*host*/, + const std::string& /*uri*/, + const HeaderOptions& headerOptions, + QualityOfService QoS) + + { + OC_UNUSED(handle); + OC_UNUSED(headerOptions); + OC_UNUSED(QoS); + + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::SubscribePresence(OCDoHandle* handle, + const std::string& host, const std::string& resourceType, + OCConnectivityType connectivityType, SubscribeCallback& presenceHandler) + { + OC_UNUSED(handle); + OC_UNUSED(host); + OC_UNUSED(resourceType); + OC_UNUSED(connectivityType); + OC_UNUSED(presenceHandler); + + return OC_STACK_OK; + } + + + OCStackResult InProcClientWrapper::UnsubscribePresence(OCDoHandle handle) + { + OC_UNUSED(handle); + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::GetDefaultQos(QualityOfService& qos) + { + OC_UNUSED(qos); + return OC_STACK_OK; + } + + + OCStackResult InProcClientWrapper::FindDirectPairingDevices(unsigned short waittime, + GetDirectPairedCallback& callback) + { + OC_UNUSED(waittime); + OC_UNUSED(callback); + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::GetDirectPairedDevices(GetDirectPairedCallback& callback) + { + OC_UNUSED(callback); + return OC_STACK_OK; + } + + OCStackResult InProcClientWrapper::DoDirectPairing(std::shared_ptr peer, + const OCPrm_t& pmSel, const std::string& pinNumber, DirectPairingCallback& callback) + { + OC_UNUSED(peer); + OC_UNUSED(pmSel); + OC_UNUSED(pinNumber); + OC_UNUSED(callback); + return OC_STACK_OK; + } +} diff --git a/resource/IPCA/unittests/mockInProcServerWrapper.cpp b/resource/IPCA/unittests/mockInProcServerWrapper.cpp new file mode 100644 index 0000000..2ccaf4a --- /dev/null +++ b/resource/IPCA/unittests/mockInProcServerWrapper.cpp @@ -0,0 +1,195 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "OCPlatform_impl.h" +#include +#include +#include +#include "ipcatestdata.h" + +namespace OC +{ + // This is mocked because OCPlatform_impl instantiates the client and the server wrappers. + // There is no implementation in this file since the mock directly links the client and server + // apps. + InProcServerWrapper::InProcServerWrapper( + std::weak_ptr csdkLock, PlatformConfig cfg) + : m_threadRun(false), m_csdkLock(csdkLock), + m_cfg { cfg } + { + } + + InProcServerWrapper::~InProcServerWrapper() + { + } + + OCStackResult InProcServerWrapper::start() + { + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::stop() + { + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::registerResource( + OCResourceHandle& resourceHandle, + std::string& resourceURI, + const std::string& resourceTypeName, + const std::string& resourceInterface, + EntityHandler& eHandler, + uint8_t resourceProperties) + { + OC_UNUSED(resourceHandle); + OC_UNUSED(resourceURI); + OC_UNUSED(resourceTypeName); + OC_UNUSED(resourceInterface); + OC_UNUSED(eHandler); + OC_UNUSED(resourceProperties); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::registerDeviceInfo(const OCDeviceInfo deviceInfo) + { + OC_UNUSED(deviceInfo); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::registerPlatformInfo(const OCPlatformInfo platformInfo) + { + OC_UNUSED(platformInfo); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::unregisterResource(const OCResourceHandle& resourceHandle) + { + OC_UNUSED(resourceHandle); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::bindTypeToResource(const OCResourceHandle& resourceHandle, + const std::string& resourceTypeName) + { + OC_UNUSED(resourceHandle); + OC_UNUSED(resourceTypeName); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::bindInterfaceToResource( + const OCResourceHandle& resourceHandle, + const std::string& resourceInterfaceName) + { + OC_UNUSED(resourceHandle); + OC_UNUSED(resourceInterfaceName); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::startPresence(const unsigned int seconds) + { + OC_UNUSED(seconds); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::stopPresence() + { + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::setDefaultDeviceEntityHandler + (EntityHandler entityHandler) + { + OC_UNUSED(entityHandler); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::sendResponse( + const std::shared_ptr pResponse) + { + OC_UNUSED(pResponse); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::setPropertyValue(OCPayloadType type, + const std::string& propName, + const std::string& propValue) + { + OC_UNUSED(type); + OC_UNUSED(propName); + OC_UNUSED(propValue); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::getPropertyValue(OCPayloadType type, + const std::string& propName, std::string& propValue) + { + OC_UNUSED(type); + OC_UNUSED(propName); + OC_UNUSED(propValue); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::getPropertyList(OCPayloadType type, + const std::string& propName, std::vector& propValue) + { + OC_UNUSED(type); + OC_UNUSED(propName); + OC_UNUSED(propValue); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::registerResourceWithTps( + OCResourceHandle& resourceHandle, + std::string& resourceURI, + const std::string& resourceTypeName, + const std::string& resourceInterface, + EntityHandler& eHandler, + uint8_t resourceProperties, + OCTpsSchemeFlags resourceTpsTypes) + { + OC_UNUSED(resourceHandle); + OC_UNUSED(resourceURI); + OC_UNUSED(resourceTypeName); + OC_UNUSED(resourceInterface); + OC_UNUSED(eHandler); + OC_UNUSED(resourceProperties); + OC_UNUSED(resourceTpsTypes); + + return OC_STACK_OK; + } + + OCStackResult InProcServerWrapper::getSupportedTransportsInfo(OCTpsSchemeFlags& supportedTps) + { + OC_UNUSED(supportedTps); + + return OC_STACK_OK; + } +} diff --git a/resource/IPCA/unittests/mockOC.cpp b/resource/IPCA/unittests/mockOC.cpp new file mode 100644 index 0000000..818b360 --- /dev/null +++ b/resource/IPCA/unittests/mockOC.cpp @@ -0,0 +1,849 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "OCPlatform_impl.h" +#include +#include +#include +#include "ipcatestdata.h" + +// This file implements subset of functions of the following classes that are required by IPCA unit +// tests. +// OCResource +// OCPlatform +// OCResourceRequest (data structure sent to server app's entity handler) +// OCResourceReponse (data structure sent to client app's callbacks) + +using namespace OC; + +typedef struct +{ + std::string deviceName; + std::vector types; + std::string specVersion; + std::string platformIndependentId; + std::vector dataModelVersions; +} Mock_OCDeviceInfo; + +typedef struct +{ + std::string platformID; + std::string manufacturerName; + std::string manufacturerUrl; + std::string modelNumber; + std::string dateOfManufacture; + std::string platformVersion; + std::string operatingSystemVersion; + std::string hardwareVersion; + std::string firmwareVersion; + std::string supportUrl; + std::string systemTime; +} Mock_OCPlatformInfo; + +class MockOCResource +{ + public: + typedef std::shared_ptr Ptr; + MockOCResource(const std::string& uri, + const std::string& resourceTypeName, + const std::string& resourceInterface, + EntityHandler entityHandler, + uint8_t resourceProperty, + std::shared_ptr ocResource) : + m_uri(uri), + m_resourceType(resourceTypeName), + m_resourceInterface(resourceInterface), + m_entityHandler(entityHandler), + m_resourceProperty(resourceProperty), + m_ocResource(ocResource) + { + }; + std::string m_uri; + std::string m_resourceType; + std::string m_resourceInterface; + EntityHandler m_entityHandler; + uint8_t m_resourceProperty; + OCResource::Ptr m_ocResource; +}; + +size_t g_requestId = 0; // incremented per request. +OCObservationId g_observationId = 0; // incremented per observe request. +struct PendingRequest +{ + typedef std::shared_ptr Ptr; + size_t requestNumber; + MockOCResource::Ptr mockOCResource; + OCMethod method; + PutCallback postCallback; + GetCallback getCallback; + ObserveCallback observeCallback; + OCObservationId observationId; + DeleteCallback deleteCallback; +}; + +PlatformConfig g_platformConfig; +Mock_OCDeviceInfo g_deviceInfo; // C++ version of OCDeviceInfo. +Mock_OCPlatformInfo g_platformInfo; // C++ version of OCPlatformInfo. + +std::recursive_mutex g_globalMutex; // the only lock. +std::map g_resourceList; // Key is resource uri. +std::map g_requestList; // Key is PendingRequest.requestNumber. + +std::string g_mockHostAddress = "fe80::1%eth0"; +uint16_t g_mockHostPort = 10000; +std::string g_mockCompleteAddress = "coap://[fe80::1%25eth0]:10000"; +void SetMockOCDevAddr(OCDevAddr& addr) +{ + addr = {OC_DEFAULT_ADAPTER, OC_IP_USE_V6}; + addr.port = g_mockHostPort; + strcpy(addr.addr, g_mockHostAddress.c_str()); +} + +// Forward decl. +OCEntityHandlerResult MockEntityHandler(OCEntityHandlerFlag flag, + OCMethod method, + const OCRepresentation& rep, + const QueryParamsMap& queryParametersMap, + PostCallback postCallback, + GetCallback getCallback, + ObserveCallback observeCallback, + DeleteCallback deleteCallback, + const std::string& uri); + +// Called to configure platform config. +void OCPlatform::Configure(const PlatformConfig& config) +{ + g_platformConfig = config; +} + +OCStackResult OCPlatform::setPropertyValue(OCPayloadType type, const std::string& tag, + const std::string& value) +{ + if ((type == PAYLOAD_TYPE_DEVICE) && (tag.compare(OC_RSRVD_PROTOCOL_INDEPENDENT_ID) == 0)) + { + g_deviceInfo.platformIndependentId = value; + } + + return OC_STACK_OK; +} + +OCStackResult OCPlatform::setPropertyValue(OCPayloadType type, const std::string& tag, + const std::vector& value) +{ + if ((type == PAYLOAD_TYPE_DEVICE) && (tag.compare(OC_RSRVD_DATA_MODEL_VERSION) == 0)) + { + g_deviceInfo.dataModelVersions = value; + } + + return OC_STACK_OK; +} + +OCStackResult OCPlatform::getPropertyValue(OCPayloadType type, const std::string& tag, + std::string& value) +{ + if ((type == PAYLOAD_TYPE_DEVICE) && (tag.compare(OC_RSRVD_PROTOCOL_INDEPENDENT_ID) == 0)) + { + value = g_deviceInfo.platformIndependentId; + } + + return OC_STACK_OK; +} + +OCStackResult OCPlatform::getPropertyValue(OCPayloadType type, const std::string& tag, + std::vector& value) +{ + if ((type == PAYLOAD_TYPE_DEVICE) && (tag.compare(OC_RSRVD_DATA_MODEL_VERSION) == 0)) + { + value = g_deviceInfo.dataModelVersions; + } + + return OC_STACK_OK; +} + +// Called by server app to register device info. +OCStackResult OCPlatform::registerDeviceInfo(const OCDeviceInfo deviceInfo) +{ + g_deviceInfo.deviceName = deviceInfo.deviceName; + g_deviceInfo.specVersion = deviceInfo.specVersion; + + if (deviceInfo.types) + { + for (OCStringLL* temp = deviceInfo.types; temp; temp = temp->next) + { + if (temp->value) + { + g_deviceInfo.types.push_back(temp->value); + } + } + } + + if (deviceInfo.dataModelVersions) + { + for (OCStringLL* temp = deviceInfo.dataModelVersions; temp; temp = temp->next) + { + if (temp->value) + { + g_deviceInfo.dataModelVersions.push_back(temp->value); + } + } + } + + return OC_STACK_OK; +} + +// Called by server app to register platform info. +OCStackResult OCPlatform::registerPlatformInfo(const OCPlatformInfo platformInfo) +{ + g_platformInfo.platformID = platformInfo.platformID; + g_platformInfo.manufacturerName = platformInfo.manufacturerName; + g_platformInfo.manufacturerUrl = platformInfo.manufacturerUrl; + g_platformInfo.modelNumber = platformInfo.modelNumber; + g_platformInfo.dateOfManufacture = platformInfo.dateOfManufacture; + g_platformInfo.platformVersion = platformInfo.platformVersion; + g_platformInfo.operatingSystemVersion = platformInfo.operatingSystemVersion; + g_platformInfo.hardwareVersion = platformInfo.hardwareVersion; + g_platformInfo.firmwareVersion = platformInfo.firmwareVersion; + g_platformInfo.supportUrl = platformInfo.supportUrl; + g_platformInfo.systemTime = platformInfo.systemTime ? platformInfo.systemTime : ""; + return OC_STACK_OK; +} + +// Called by server app to register resource. This mock creates an OCResource that can be used by +// client app. +// MockOCResource keeps track of OC::OCResource and callbacks to the server app. +OCStackResult OCPlatform::registerResource( + OCResourceHandle& resourceHandle, + std::string& resourceURI, + const std::string& resourceTypeName, + const std::string& resourceInterface, + EntityHandler entityHandler, + uint8_t resourceProperty) +{ + std::lock_guard lock(g_globalMutex); + + if (g_resourceList.find(resourceURI) != g_resourceList.end()) + { + // duplicate resource. + return OC_STACK_ERROR; + } + + OCConnectivityType connectivityType = CT_DEFAULT; + std::vector types = { resourceTypeName }; + std::vector ifaces = { resourceInterface }; + OCResource::Ptr ocResource = OCPlatform::constructResourceObject( + "", // host address is mocked in SetHost(). + resourceURI, + connectivityType, + (resourceProperty & OC_OBSERVABLE) ? true : false, + types, + ifaces); + + std::shared_ptr newMockResource = std::shared_ptr( + new MockOCResource( + resourceURI, + resourceTypeName, + resourceInterface, + entityHandler, + resourceProperty, + ocResource)); + if (newMockResource == nullptr) + { + return OC_STACK_ERROR; + } + + g_resourceList[resourceURI] = newMockResource; + resourceHandle = reinterpret_cast(newMockResource.get()); + return OC_STACK_OK; +} + +// Called by server app to shut down a resource. +// This also deletes the MockOCResource. +OCStackResult OCPlatform::unregisterResource(const OCResourceHandle& resourceHandle) +{ + std::lock_guard lock(g_globalMutex); + + MockOCResource* rawMockOCResource = reinterpret_cast(resourceHandle); + + for (auto mockResource : g_resourceList) + { + if (mockResource.second.get() == rawMockOCResource) + { + g_resourceList.erase(mockResource.second->m_uri); + break; + } + } + + return OC_STACK_OK; +} + +// Called by client app to discover a resource. +OCStackResult OCPlatform::findResource(const std::string& host, + const std::string& resourceName, + OCConnectivityType connectivityType, + FindCallback resourceHandler) +{ + OC_UNUSED(connectivityType); + OC_UNUSED(resourceName); + OC_UNUSED(host); + + // Ideally: return only requested resource types embedded in resourceName. + // This mock fires back everything that server app registered. + std::lock_guard lock(g_globalMutex); + for (auto mockResource : g_resourceList) + { + std::thread findResourceCallbackThread(resourceHandler, mockResource.second->m_ocResource); + findResourceCallbackThread.detach(); + } + + return OC_STACK_OK; +} + +// Called by client app to get device info of device. +OCStackResult OCPlatform::getDeviceInfo(const std::string& host, + const std::string& deviceURI, + OCConnectivityType connectivityType, + FindDeviceCallback deviceInfoHandler) +{ + OC_UNUSED(host); + OC_UNUSED(connectivityType); + OC_UNUSED(deviceURI); + + OCDevAddr addr; + SetMockOCDevAddr(addr); + + OCRepresentation ocRep; + ocRep.setDevAddr(addr); + ocRep.setValue(OC_RSRVD_DEVICE_NAME, g_deviceInfo.deviceName); + ocRep.setValue(OC_RSRVD_SPEC_VERSION, g_deviceInfo.specVersion); + ocRep.setValue(OC_RSRVD_DATA_MODEL_VERSION, g_deviceInfo.dataModelVersions); + + std::thread getDeviceInfoCallbackThread(deviceInfoHandler, ocRep); + getDeviceInfoCallbackThread.detach(); + return OC_STACK_OK; +} + +// Called by client app to get platform info of device. +OCStackResult OCPlatform::getPlatformInfo(const std::string& host, + const std::string& platformURI, + OCConnectivityType connectivityType, + FindPlatformCallback platformInfoHandler) +{ + OC_UNUSED(host); + OC_UNUSED(platformURI); + OC_UNUSED(connectivityType); + + OCDevAddr addr; + SetMockOCDevAddr(addr); + + OCRepresentation ocRep; + ocRep.setDevAddr(addr); + ocRep.setValue(OC_RSRVD_PLATFORM_ID, g_platformInfo.platformID); + ocRep.setValue(OC_RSRVD_MFG_NAME, g_platformInfo.manufacturerName); + ocRep.setValue(OC_RSRVD_MFG_URL, g_platformInfo.manufacturerUrl); + ocRep.setValue(OC_RSRVD_MODEL_NUM, g_platformInfo.modelNumber); + ocRep.setValue(OC_RSRVD_MFG_DATE, g_platformInfo.dateOfManufacture); + ocRep.setValue(OC_RSRVD_PLATFORM_VERSION, g_platformInfo.platformVersion); + ocRep.setValue(OC_RSRVD_OS_VERSION, g_platformInfo.operatingSystemVersion); + ocRep.setValue(OC_RSRVD_HARDWARE_VERSION, g_platformInfo.hardwareVersion); + ocRep.setValue(OC_RSRVD_FIRMWARE_VERSION, g_platformInfo.firmwareVersion); + ocRep.setValue(OC_RSRVD_SUPPORT_URL, g_platformInfo.supportUrl); + ocRep.setValue(OC_RSRVD_SYSTEM_TIME, g_platformInfo.systemTime); + + std::thread getPlatformInfoCallbackThread(platformInfoHandler, ocRep); + getPlatformInfoCallbackThread.detach(); + return OC_STACK_OK; + +} + +// Called by server app to send notification to a list of observers that have called ObserveResource(). +OCStackResult OCPlatform::notifyListOfObservers(OCResourceHandle resourceHandle, + ObservationIds& observationIds, + const std::shared_ptr pResponse) +{ + OC_UNUSED(resourceHandle); + + OCRepresentation ocRep = pResponse->getResourceRepresentation(); + OCEntityHandlerResult result = pResponse->getResponseResult(); + HeaderOptions serverHeaderOptions; /* not mocked */ + ObservationIds localCopy = observationIds; + + { + std::lock_guard lock(g_globalMutex); + for (auto targetObserveId : localCopy) + { + for (auto request : g_requestList) + { + PendingRequest::Ptr pendingRequest = request.second; + if ((pendingRequest->method == OC_REST_OBSERVE) || + (pendingRequest->method == OC_REST_OBSERVE_ALL)) + { + if (pendingRequest->observationId == targetObserveId) + { + std::thread observeCallbackThread(pendingRequest->observeCallback, + serverHeaderOptions, + ocRep, + (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR, + 1 /* sequence number, not mocked */); + observeCallbackThread.detach(); + } + + } + } + } + } + + return OC_STACK_OK; +} + +// Every response from server app to client app is performed by this function. +OCStackResult OCPlatform::sendResponse(const std::shared_ptr pResponse) +{ + size_t requestNumber = reinterpret_cast(pResponse->getRequestHandle()); + OCRepresentation ocRep = pResponse->getResourceRepresentation(); + OCEntityHandlerResult result = pResponse->getResponseResult(); + HeaderOptions serverHeaderOptions; + + PendingRequest::Ptr pendingRequest; + { + std::lock_guard lock(g_globalMutex); + if (g_requestList.find(requestNumber) == g_requestList.end()) + { + return OC_STACK_ERROR; + } + + pendingRequest = g_requestList[requestNumber]; + } + + switch (pendingRequest->method) + { + case OC_REST_POST: + { + std::thread postCallbackThread(pendingRequest->postCallback, + serverHeaderOptions, + ocRep, + (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR); + postCallbackThread.detach(); + break; + } + + case OC_REST_GET: + { + std::thread getCallbackThread(pendingRequest->getCallback, + serverHeaderOptions, + ocRep, + (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR); + getCallbackThread.detach(); + break; + } + + case OC_REST_DELETE: + { + std::thread deleteCallbackThread(pendingRequest->deleteCallback, + serverHeaderOptions, + (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR); + deleteCallbackThread.detach(); + break; + } + } + + return OC_STACK_OK; +} + +// Constructor of OCResource. +OCResource::OCResource(std::weak_ptr clientWrapper, + const std::string& host, + const std::string& uri, + const std::string& serverId, + OCConnectivityType connectivityType, + uint8_t property, + const std::vector& resourceTypes, + const std::vector& interfaces) : + m_clientWrapper(clientWrapper), + m_uri(uri), + m_resourceId(serverId, m_uri), + m_isCollection(false), + m_property(property), + m_resourceTypes(resourceTypes), + m_interfaces(interfaces), + m_observeHandle(nullptr) +{ + m_devAddr.adapter = static_cast(connectivityType >> CT_ADAPTER_SHIFT); + m_devAddr.flags = static_cast(connectivityType & CT_MASK_FLAGS); + this->setHost(host); +} + +OCResource::~OCResource() +{ +} + +void defaultCallback(const HeaderOptions&, const OCRepresentation&, const int) +{ +} + +void defaultDeleteCallback(const HeaderOptions&, const int) +{ +} + +void defaultObserveCallback(const HeaderOptions&, const OCRepresentation&, const int, const int) +{ +} + +// Client app's request to Delete a resource. +OCStackResult OCResource::deleteResource(DeleteCallback deleteHandler) +{ + OCRepresentation rep; + QueryParamsMap queryParametersMap; + OCEntityHandlerResult result = MockEntityHandler( + OC_REQUEST_FLAG, + OC_REST_DELETE, + rep, + queryParametersMap, + &defaultCallback, + &defaultCallback, + &defaultObserveCallback, + deleteHandler, + uri()); + + return (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR; +} + +// Client app's request to Get a resource. +OCStackResult OCResource::get(const QueryParamsMap& queryParametersMap, + GetCallback attributeHandler) +{ + OC_UNUSED(queryParametersMap); + + OCRepresentation rep; + OCEntityHandlerResult result = MockEntityHandler( + OC_REQUEST_FLAG, + OC_REST_GET, + rep, + queryParametersMap, + &defaultCallback, + attributeHandler, + &defaultObserveCallback, + &defaultDeleteCallback, + uri()); + + return (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR; +} + +// Client app's request to Post to a resource. +OCStackResult OCResource::post(const OCRepresentation& rep, + const QueryParamsMap& queryParametersMap, + PostCallback attributeHandler) +{ + OC_UNUSED(queryParametersMap); + + OCEntityHandlerResult result = MockEntityHandler( + OC_REQUEST_FLAG, + OC_REST_POST, + rep, + queryParametersMap, + attributeHandler, + &defaultCallback, + &defaultObserveCallback, + &defaultDeleteCallback, + uri()); + + return (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR; +} + +// Client app's request to observe a resource. +OCStackResult OCResource::observe(ObserveType observeType, + const QueryParamsMap& queryParametersMap, + ObserveCallback observeHandler) +{ + OC_UNUSED(queryParametersMap); + + OCRepresentation rep; + OCEntityHandlerResult result = MockEntityHandler( + OC_OBSERVE_FLAG, + (observeType == ObserveType::Observe) ? + OC_REST_OBSERVE : OC_REST_OBSERVE_ALL, + rep, + queryParametersMap, + &defaultCallback, + &defaultCallback, + observeHandler, + &defaultDeleteCallback, + uri()); + + return (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR; +} + +// Client app's request to cancel observe request. +OCStackResult OCResource::cancelObserve() +{ + std::lock_guard lock(g_globalMutex); + + for (auto request : g_requestList) + { + PendingRequest::Ptr pendingRequest = request.second; + if ((pendingRequest->method == OC_REST_OBSERVE) || + (pendingRequest->method == OC_REST_OBSERVE_ALL)) + { + if (pendingRequest->mockOCResource->m_uri.compare(uri()) == 0) + { + g_requestList.erase(pendingRequest->requestNumber); + break; + } + } + } + + return OC_STACK_OK; +} + +// Is resource observable. +bool OCResource::isObservable() const +{ + return (m_property & OC_OBSERVABLE) == OC_OBSERVABLE; +} + +// Host address of the resource. +std::string OCResource::host() const +{ + std::ostringstream ss; + ss << m_devAddr.addr; + return ss.str(); +} + +// Host address of the resource. +std::string OCResource::setHost(const std::string& host) +{ + OC_UNUSED(host); + + g_mockCompleteAddress.copy(m_devAddr.addr, sizeof(m_devAddr.addr)); + m_devAddr.addr[g_mockCompleteAddress.length()] = NULL; + return host; +} + +// Resource types implemented by resource. +std::vector OCResource::getResourceTypes() const +{ + return m_resourceTypes; +} + +// Resource interfaces implemented by resource. +std::vector OCResource::getResourceInterfaces() const +{ + return m_interfaces; +} + +// URI of the resource. +std::string OCResource::uri() const +{ + return m_uri; +} + +// ID of reosurce. +std::string OCResource::sid() const +{ + return this->uniqueIdentifier().m_representation; +} + +// See: OCResourceRequest.h declaration of this function prototype as friend function. +// Function prototype must match that. +void formResourceRequest(OCEntityHandlerFlag flag, + OCEntityHandlerRequest* entityHandlerRequest, + std::shared_ptr pRequest) +{ + if(pRequest && entityHandlerRequest) + { + pRequest->setRequestHandle(entityHandlerRequest->requestHandle); + pRequest->setResourceHandle(entityHandlerRequest->resource); + pRequest->setMessageID(entityHandlerRequest->messageID); + } + + if(flag & OC_REQUEST_FLAG) + { + pRequest->setRequestHandlerFlag(OC::RequestHandlerFlag::RequestFlag); + + if(entityHandlerRequest) + { + if(OC_REST_GET == entityHandlerRequest->method) + { + pRequest->setRequestType(OC::PlatformCommands::GET); + } + else if(OC_REST_PUT == entityHandlerRequest->method) + { + pRequest->setRequestType(OC::PlatformCommands::PUT); + pRequest->setPayload(entityHandlerRequest->payload); + } + else if(OC_REST_POST == entityHandlerRequest->method) + { + pRequest->setRequestType(OC::PlatformCommands::POST); + pRequest->setPayload(entityHandlerRequest->payload); + } + else if(OC_REST_DELETE == entityHandlerRequest->method) + { + pRequest->setRequestType(OC::PlatformCommands::DELETE); + } + } + } + + if(flag & OC_OBSERVE_FLAG) + { + pRequest->setRequestHandlerFlag( + OC::RequestHandlerFlag::RequestFlag | OC::RequestHandlerFlag::ObserverFlag); + + if(entityHandlerRequest) + { + OC::ObservationInfo observationInfo; + observationInfo.action = (OC::ObserveAction) entityHandlerRequest->obsInfo.action; + observationInfo.obsId = entityHandlerRequest->obsInfo.obsId; + pRequest->setObservationInfo(observationInfo); + } + } + + QueryParamsMap* queryParams = reinterpret_cast(entityHandlerRequest->query); + pRequest->setQueryParams(*queryParams); + +} + +// Set payload to the OCResourceRequest sent to the server app. +void OCResourceRequest::setPayload(OCPayload* payload) +{ + MessageContainer info; + + if(payload == nullptr) + { + return; + } + + if(payload->type != PAYLOAD_TYPE_REPRESENTATION) + { + throw std::logic_error("Wrong payload type"); + return; + } + + info.setPayload(payload); + + const std::vector& reps = info.representations(); + if(reps.size() > 0) + { + std::vector::const_iterator itr = reps.begin(); + std::vector::const_iterator back = reps.end(); + m_representation = *itr; + ++itr; + + for(;itr != back; ++itr) + { + m_representation.addChild(*itr); + } + } +} + +OCResourceIdentifier::OCResourceIdentifier(const std::string& wireServerIdentifier, + const std::string& resourceUri) + :m_representation(wireServerIdentifier), m_resourceUri(resourceUri) +{ +} + +OCResourceIdentifier OCResource::uniqueIdentifier() const +{ + return m_resourceId; +} + + +// Every callback to server app is performed by this function. +OCEntityHandlerResult MockEntityHandler(OCEntityHandlerFlag flag, + OCMethod method, + const OCRepresentation& rep, + const QueryParamsMap& queryParametersMap, + PostCallback postCallback, + GetCallback getCallback, + ObserveCallback observeCallback, + DeleteCallback deleteCallback, + const std::string& uri) +{ + size_t requestNumber; + MockOCResource::Ptr mockOCResource = nullptr; + { + std::lock_guard lock(g_globalMutex); + if (g_resourceList.find(uri) == g_resourceList.end()) + { + return OC_EH_ERROR; + } + + mockOCResource = g_resourceList[uri]; + requestNumber = ++g_requestId; + } + + auto pendingRequest = std::make_shared(); + if (pendingRequest == nullptr) + { + return OC_EH_ERROR; + } + + pendingRequest->requestNumber = requestNumber; + pendingRequest->mockOCResource = mockOCResource; + pendingRequest->method = method; + pendingRequest->postCallback = postCallback; + pendingRequest->getCallback = getCallback; + pendingRequest->observeCallback = observeCallback; + pendingRequest->deleteCallback = deleteCallback; + { + // Store the request for response. + std::lock_guard lock(g_globalMutex); + g_requestList[requestNumber] = pendingRequest; + } + + auto pRequest = std::make_shared(); + if (pRequest == nullptr) + { + return OC_EH_ERROR; + } + + MessageContainer ocInfo; + ocInfo.addRepresentation(rep); + for(const OCRepresentation& r : rep.getChildren()) + { + ocInfo.addRepresentation(r); + } + + OCPayload* payload = reinterpret_cast(ocInfo.getPayload()); + + OCEntityHandlerRequest entityHandlerRequest = {0}; + entityHandlerRequest.requestHandle = reinterpret_cast(pendingRequest->requestNumber); + entityHandlerRequest.resource = nullptr; + entityHandlerRequest.messageID = 0; + entityHandlerRequest.method = method; + entityHandlerRequest.payload = payload; + + if (flag & OC_OBSERVE_FLAG) + { + std::lock_guard lock(g_globalMutex); + entityHandlerRequest.obsInfo.action = OC_OBSERVE_REGISTER; + entityHandlerRequest.obsInfo.obsId = ++g_observationId; + pendingRequest->observationId = entityHandlerRequest.obsInfo.obsId; + } + + // Pass QueryParamsMap directly to formResourceRequest() which has access + // to pRequest->setQueryParams(). + QueryParamsMap localCopy = queryParametersMap; + entityHandlerRequest.query = reinterpret_cast(&localCopy); + + formResourceRequest(flag, &entityHandlerRequest, pRequest); + pRequest->setResourceUri(uri); + + std::thread callEntityHandlerThread(mockOCResource->m_entityHandler, pRequest); + callEntityHandlerThread.detach(); + return OC_EH_OK; +} diff --git a/resource/IPCA/unittests/mockOCPlatform_impl.cpp b/resource/IPCA/unittests/mockOCPlatform_impl.cpp new file mode 100644 index 0000000..4514d35 --- /dev/null +++ b/resource/IPCA/unittests/mockOCPlatform_impl.cpp @@ -0,0 +1,87 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "OCPlatform_impl.h" +#include +#include +#include +#include "ipcatestdata.h" + +using namespace OC; + + // OCPlatform_impl is friend class of OCResource. + // Every OCResource object is created via this function. + + IClientWrapper::Ptr g_mockClientWrapper = std::shared_ptr(); + extern PlatformConfig g_platformConfig; + OCResource::Ptr OCPlatform_impl::constructResourceObject( + const std::string& host, + const std::string& uri, + OCConnectivityType connectivityType, + bool isObservable, + const std::vector& resourceTypes, + const std::vector& interfaces) + { + uint8_t resourceProperty = 0; + + if (isObservable) + { + resourceProperty = (resourceProperty | OC_OBSERVABLE); + } + + return std::shared_ptr(new OCResource( + g_mockClientWrapper, + host, + uri, + "", + connectivityType, + resourceProperty, + resourceTypes, + interfaces)); + } + + OCPlatform_impl::OCPlatform_impl(const PlatformConfig& config) : + m_cfg { config }, + m_WrapperInstance { make_unique() }, + m_csdkLock { std::make_shared() } + { + } + + OCPlatform_impl::~OCPlatform_impl(void) + { + } + + OCPlatform_impl& OCPlatform_impl::Instance() + { + static OCPlatform_impl platform(g_platformConfig); + return platform; + } + + OCResource::Ptr OCPlatform::constructResourceObject(const std::string& host, + const std::string& uri, + OCConnectivityType connectivityType, + bool isObservable, + const std::vector& resourceTypes, + const std::vector& interfaces) + { + return OCPlatform_impl::Instance().constructResourceObject(host, + uri, connectivityType, + isObservable, + resourceTypes, interfaces); + } diff --git a/resource/IPCA/unittests/testelevatorclient.cpp b/resource/IPCA/unittests/testelevatorclient.cpp new file mode 100644 index 0000000..c2b2afa --- /dev/null +++ b/resource/IPCA/unittests/testelevatorclient.cpp @@ -0,0 +1,395 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "TestElevatorClient.h" +#include "ipcatestdata.h" + +#define VERBOSE_INFO 0 // set to 1 for extra output. + +using namespace OC; +using namespace std::placeholders; + +ElevatorClient::ElevatorClient() +{ + m_elevatorResource = nullptr; +} + +ElevatorClient::~ElevatorClient() +{ +} + +void ElevatorClient::OnObserveCallback( + const HeaderOptions headerOptions, + const OCRepresentation &rep, + const int &eCode, + const int &sequenceNumber) +{ + OC_UNUSED(sequenceNumber); + OC_UNUSED(eCode); + + int newCurrentFloor, newTargetFloor, newDirection; + + if (rep.getValue(ELEVATOR_PROPERTY_CURRENT_FLOOR, newCurrentFloor) == true) + { + m_notifiedCurrentFloor = newCurrentFloor; + } + + if (rep.getValue(ELEVATOR_PROPERTY_TARGET_FLOOR, newTargetFloor) == true) + { + m_notifiedTargetFloor = newTargetFloor; + } + + if (rep.getValue(ELEVATOR_PROPERTY_DIRECTION, newDirection) == true) + { + m_notifiedDirection = newDirection; + } +} + +int ElevatorClient::GetObservedCurrentFloor() +{ + return m_notifiedCurrentFloor; +} + +bool ElevatorClient::StartObservation() +{ + OCStackResult result = m_elevatorResource->observe( + ObserveType::Observe, + QueryParamsMap(), + std::bind(&ElevatorClient::OnObserveCallback, + this, _1, _2, _3, _4)); + return result == OC_STACK_OK ? true : false; +} + +bool ElevatorClient::StopObservation() +{ + OCStackResult result = m_elevatorResource->cancelObserve(); + return result == OC_STACK_OK ? true : false; +} + +const int DEFAULT_WAITTIME = 2000; +bool ElevatorClient::WaitForCallback(int waitingTime = DEFAULT_WAITTIME) +{ + std::unique_lock lock { syncMutex }; + + if (syncCV.wait_for(lock, std::chrono::milliseconds{ waitingTime }) != std::cv_status::timeout) + { + return true; + } + else + { + return false; + } +} + +void ElevatorClient::SignalCompletion() +{ + syncCV.notify_all(); +} + +// Callback handler on GET request +void ElevatorClient::OnGet(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode) +{ + OC_UNUSED(headerOptions); + OC_UNUSED(eCode); + + if (eCode == OC_STACK_OK) + { + ReadRepresentation(rep); + } + else + { + printf("OnGet:: received error [0x%x]\r\n", eCode); + } + + SignalCompletion(); +} + +// Callback handler on PUT request +void ElevatorClient::OnPut(const HeaderOptions& headerOptions, + const OCRepresentation& rep, + const int eCode) +{ + OC_UNUSED(headerOptions); + + if (eCode == OC_STACK_OK || eCode == OC_STACK_RESOURCE_CHANGED) + { + ReadRepresentation(rep); + } + else + { + printf ("PUT request failed, error: 0x%x\r\n", eCode); + } + + SignalCompletion(); +} + +void ElevatorClient::ReadRepresentation(const OCRepresentation& rep) +{ + m_targetFloor = rep.getValue(ELEVATOR_PROPERTY_TARGET_FLOOR); + m_currentFloor = rep.getValue(ELEVATOR_PROPERTY_CURRENT_FLOOR); + m_direction = rep.getValue(ELEVATOR_PROPERTY_DIRECTION); +} + +std::recursive_mutex theMutex; + +void ElevatorClient::PrintResource(std::shared_ptr resource) +{ +#if (VERBOSE_INFO == 1) + std::lock_guard lock(theMutex); + std::string resourcePath; + std::string hostAddress; + std::cout << "ElevatorClient::PrintResource:" << std::endl; + std::cout << "Resource URI: " << resource->uri() << std::endl; + std::cout << "Device URI: " << resource->host() << std::endl; + std::cout << "Resource Types: " << std::endl; + for(auto &rt : resource->getResourceTypes()) + std::cout << " " << rt << std::endl; + std::cout << "List of resource interfaces: " << std::endl; + for(auto &ri : resource->getResourceInterfaces()) + std::cout << " " << ri << std::endl; + std::cout << std::endl; +#else + OC_UNUSED(resource); +#endif +} + +// Temporarily store the found resource pointers until their device name is found. +// Key is device URI (e.g., "coap://[fe80::5828:93a8:d53e:4222%7]:62744") +std::map> OCFDevices; + +void ElevatorClient::OnDeviceInfoCallback(const OCRepresentation& rep) +{ + std::lock_guard lock(theMutex); + std::string deviceName; + rep.getValue("n", deviceName); + + if (m_elevatorResource == nullptr && deviceName.compare(m_targetElevatorName) == 0) + { + m_elevatorResource = OCFDevices[rep.getHost()]; + OCFDevices.clear(); // free rest of the resources. + PrintResource(m_elevatorResource); + } +} + +void ElevatorClient::PrintOCRep(const OCRepresentation& rep) +{ +#if (VERBOSE_INFO == 1) + std::lock_guard lock(theMutex); + std::cout << "++++++++++ PrintOCRep() ++++++++++++" << std::endl; + std::cout << "For device URI: " << rep.getHost() << std::endl; + std::cout << "Resoure URI: " << rep.getUri() << std::endl; + OCRepresentation::const_iterator itr= rep.begin(); + OCRepresentation::const_iterator endItr = rep.end(); + for(;itr!=endItr;++itr) + { + std::string value = (*itr).getValue(); + std::cout << " = ElevatorClient:: Attribute name: " << itr->attrname() << std::endl; + std::cout << " ElevatorClient:: Attribute type: " << itr->type() << std::endl; + std::cout << " ElevatorClient:: Attribute value: "; + switch(itr->type()) + { + case AttributeType::Null: + { + std::cout << "nullptr" << std::endl; + break; + } + + case AttributeType::String: + { + std::cout << ((*itr).getValue()).c_str() << std::endl; + break; + } + + case AttributeType::Integer: + { + std::cout << (*itr).getValue() << std::endl; + break; + } + + default: + { + std::cout << "unrecognized type" << std::endl; + break; + } + } + + } + std::cout << "---------- PrintOCRep() ------------" << std::endl << std::endl; +#else + OC_UNUSED(rep); +#endif +} + +void ElevatorClient::OnPlatformInfoCallback(const OCRepresentation& rep) +{ + OC_UNUSED(rep); + std::lock_guard lock(theMutex); + PrintOCRep(rep); +} + +// Callback to found resources +void ElevatorClient::OnResourceFound(std::shared_ptr resource) +{ + std::lock_guard lock(theMutex); + + // Target elevator already found. + if (m_elevatorResource != nullptr) + { + return; + } + + std::vector resourceTypes = resource->getResourceTypes(); + + if (resourceTypes.size() != 1) + { + return; + } + + bool matchTargetResourceType = false; + + for (auto const& rt : resourceTypes) + { + if (rt.compare(ELEVATOR_RESOURCE_TYPE) == 0) + { + matchTargetResourceType = true; + break; + } + } + + if (matchTargetResourceType == false) + { + return; + } + + if (OCFDevices.find(resource->host()) != OCFDevices.end()) + { + return; // have seen this resource from this host. + } + + OCFDevices[resource->host()] = resource; + + // Get the device name for match with m_targetElevatorName. + OCPlatform::getDeviceInfo( + resource->host(), + OC_RSRVD_DEVICE_URI, + CT_DEFAULT, + std::bind(&ElevatorClient::OnDeviceInfoCallback, this, _1)); + + + OCPlatform::getPlatformInfo( + resource->host(), + OC_RSRVD_PLATFORM_URI, + CT_DEFAULT, + std::bind(&ElevatorClient::OnPlatformInfoCallback, this, _1)); +} + +// each elevator in unit test has unique device name to differentiate it from the others. +bool ElevatorClient::FindElevator(std::string elevatorName) +{ + std::string defaultElevatorResourceType = ELEVATOR_RESOURCE_TYPE; + std::ostringstream deviceUri; + m_targetElevatorName = elevatorName; + + deviceUri << OC_RSRVD_WELL_KNOWN_URI << "?rt=" << defaultElevatorResourceType.c_str(); + + OCConnectivityType connectivityType = CT_ADAPTER_IP; + + OCStackResult result = OCPlatform::findResource( + "", + deviceUri.str(), + connectivityType, + std::bind(&ElevatorClient::OnResourceFound, this, _1)); + + if (result == OC_STACK_OK) + { + return true; + } + else + { + return false; + } +} + +bool ElevatorClient::IsElevatorFound() +{ + return m_elevatorResource != nullptr; +} + +void ElevatorClient::SetTargetFloor(int targetFloor) +{ + if(m_elevatorResource) + { + OCRepresentation rep; + rep[ELEVATOR_PROPERTY_TARGET_FLOOR] = targetFloor; + + m_elevatorResource->post( + rep, + QueryParamsMap(), + std::bind(&ElevatorClient::OnPut, this, _1, _2, _3)); + + WaitForCallback(); // wait for completion. + } +} + +bool ElevatorClient::GetTargetFloor(int* targetFlr) +{ + m_elevatorResource->get(QueryParamsMap(), std::bind(&ElevatorClient::OnGet, this, _1, _2, _3)); + if (WaitForCallback()) + { + *targetFlr = m_targetFloor; + return true; + } + else + { + *targetFlr = -1; + return false; + } +} + +bool ElevatorClient::GetCurrentFloor(int* currFloor) +{ + m_elevatorResource->get(QueryParamsMap(), std::bind(&ElevatorClient::OnGet, this, _1, _2, _3)); + if (WaitForCallback()) + { + *currFloor = m_currentFloor; + return true; + } + else + { + *currFloor = -1; + return false; + } +} + +bool ElevatorClient::GetDirection(int* dir) +{ + m_elevatorResource->get(QueryParamsMap(), std::bind(&ElevatorClient::OnGet, this, _1, _2, _3)); + if (WaitForCallback()) + { + *dir = m_direction; + return true; + } + else + { + *dir = -1; + return false; + } +} diff --git a/resource/IPCA/unittests/testelevatorclient.h b/resource/IPCA/unittests/testelevatorclient.h new file mode 100644 index 0000000..28ab738 --- /dev/null +++ b/resource/IPCA/unittests/testelevatorclient.h @@ -0,0 +1,92 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ +#ifndef _ELEVATOR_CLIENT_H +#define _ELEVATOR_CLIENT_H + +#include +#include +#include +#include +#include + +#include "OCPlatform.h" +#include "OCApi.h" + +using namespace OC; + +class ElevatorClient +{ +public: + ElevatorClient(); + ~ElevatorClient(); + + bool FindElevator(std::string targetElevatorName); + bool IsElevatorFound(); + + void SetTargetFloor(int targetFloor); + + bool GetTargetFloor(int* targetFlr); + bool GetCurrentFloor(int* currFloor); + bool GetDirection(int* dir); + + bool StartObservation(); + bool StopObservation(); + int GetObservedCurrentFloor(); + +private: + // Mutex is used togeter with the conditional_variable for waiting async OnGet and OnPut + // callbacks. + std::mutex syncMutex; + std::condition_variable syncCV; + bool WaitForCallback(int waitingTime); // return false if wait timeout. + void SignalCompletion(); + + std::shared_ptr m_elevatorResource; + + void OnObserveCallback( + const HeaderOptions headerOptions, + const OCRepresentation &rep, + const int &eCode, + const int &sequenceNumber); + + void OnResourceFound(std::shared_ptr resource); + void OnDeviceInfoCallback(const OCRepresentation& rep); + void OnPlatformInfoCallback(const OCRepresentation& rep); + void OnGet(const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode); + void OnPut(const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode); + void ReadRepresentation(const OCRepresentation& rep); + void PrintResource(std::shared_ptr resource); + void PrintOCRep(const OCRepresentation& rep); + + // Target server has this device name. + std::string m_targetElevatorName; + + // Last known property values from server. + int m_targetFloor; + int m_currentFloor; + int m_direction; + + // Last known notified values from server. + int m_notifiedTargetFloor; + int m_notifiedCurrentFloor; + int m_notifiedDirection; + +}; + +#endif // _ELEVATOR_CLIENT_H \ No newline at end of file diff --git a/resource/IPCA/unittests/testelevatorserver.cpp b/resource/IPCA/unittests/testelevatorserver.cpp new file mode 100644 index 0000000..a6eac74 --- /dev/null +++ b/resource/IPCA/unittests/testelevatorserver.cpp @@ -0,0 +1,585 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#include "logger.h" +#include "TestElevatorServer.h" +#include "ipcatestdata.h" + +using namespace OC; +using namespace std::placeholders; + +#define TAG "TestElevatorServer.cpp" + +// Initialize Persistent Storage for security database +FILE* elevatorServer_fopen(const char *path, const char *mode) +{ + OC_UNUSED(path); + return fopen("IPCAUnitTest.dat", mode); +} + +OCPersistentStorage elevatorServerPS = {elevatorServer_fopen, fread, fwrite, fclose, unlink}; + +static char* ELEVATOR_MAINTENANCE_PATH = "/oic/mnt"; + +// +// Class ElevatorServer implementation. +// +ElevatorServer::ElevatorServer() : + m_engineThread(), + m_elevatorResourceHandle(nullptr), + m_elevatorCOResourceHandle(nullptr), + m_elevatorMaintenanceHandle(nullptr), + m_elevatorCreateRelativeResource(nullptr), + m_elevatorDeleteResource(nullptr) +{ + m_isRunning = false; + m_targetFloor = m_currentFloor = 1; + m_direction = ElevatorDirection::Stopped; + m_relativePathResourceCreateCount = 0; + m_explicitPathResourceCreateCount = 0; + m_deleteResourceCount = 0; +} + +ElevatorServer::~ElevatorServer() +{ +} + +// Return all properties in response. +OCStackResult ElevatorServer::SendResponse(std::shared_ptr request, + OCEntityHandlerResult result) +{ + // Values to return. + OCRepresentation responseRep; + responseRep[ELEVATOR_PROPERTY_CURRENT_FLOOR] = GetCurrentFloor(); + responseRep[ELEVATOR_PROPERTY_TARGET_FLOOR] = GetTargetFloor(); + responseRep[ELEVATOR_PROPERTY_DIRECTION] = (int)GetElevatorDirection(); + + // Prepare the response. + auto pResponse = std::make_shared(); + pResponse->setRequestHandle(request->getRequestHandle()); + pResponse->setResourceHandle(request->getResourceHandle()); + pResponse->setResourceRepresentation(responseRep); + pResponse->setResponseResult(result); + + // Send the response. + return OCPlatform::sendResponse(pResponse); +} + +OCStackResult ElevatorServer::SendMaintenanceResponse(std::shared_ptr request) +{ + // Values to return. + OCRepresentation responseRep; + responseRep["fr"] = false; + responseRep["rb"] = false; + + // Prepare the response. + auto pResponse = std::make_shared(); + pResponse->setRequestHandle(request->getRequestHandle()); + pResponse->setResourceHandle(request->getResourceHandle()); + pResponse->setResourceRepresentation(responseRep); + pResponse->setResponseResult(OC_EH_OK); + + // Send the response. + return OCPlatform::sendResponse(pResponse); +} + +// Callback handler for elevator resource. +OCEntityHandlerResult ElevatorServer::ElevatorEntityHandler( + std::shared_ptr request) +{ + OCEntityHandlerResult ehResult = OC_EH_ERROR; + + if (request->getResourceUri() == ELEVATOR_CO_RESOURCE_PATH) + { + return OC_EH_FORBIDDEN; + } + + if (request) + { + std::string resourceUri = request->getResourceUri(); + + // Get the request type and request flag + std::string requestType = request->getRequestType(); + int requestFlag = request->getRequestHandlerFlag(); + + if (requestFlag & RequestHandlerFlag::RequestFlag) + { + // If the request type is GET + if (requestType == OC::PlatformCommands::GET) + { + if (resourceUri.compare(ELEVATOR_RESOURCE_PATH) == 0) + { + if (SendResponse(request) == OC_STACK_OK) + { + ehResult = OC_EH_OK; + } + } + else + if (resourceUri.compare(ELEVATOR_MAINTENANCE_PATH) == 0) + { + if (SendMaintenanceResponse(request) == OC_STACK_OK) + { + ehResult = OC_EH_OK; + } + } + } + else if (requestType == OC::PlatformCommands::POST) + { + OCRepresentation requestRep = request->getResourceRepresentation(); + + if (resourceUri.compare(ELEVATOR_RESOURCE_PATH) == 0) + { + QueryParamsMap queryMaps = request->getQueryParameters(); + auto interfaceQuery = queryMaps.find("if"); + if ((interfaceQuery != queryMaps.end()) && + (interfaceQuery->second.compare(ELEVATOR_RESOURCE_MADE_UP_INTERFACE) == 0)) + { + m_IncorrectInterfaceCount++; + if (OC_STACK_OK == SendResponse(request, OC_EH_ERROR)) + { + ehResult = OC_EH_OK; + } + } + else + { + // Target floor can be set. + int targetFloor; + if (requestRep.getValue(ELEVATOR_PROPERTY_TARGET_FLOOR, targetFloor)) + { + SetTargetFloor((int)targetFloor); + if (OC_STACK_OK == SendResponse(request)) + { + ehResult = OC_EH_OK; + } + } + } + + } + else + if (resourceUri.compare(ELEVATOR_MAINTENANCE_PATH) == 0) + { + bool action; + + if (requestRep.getValue("rb", action)) + { + std::cout << "ElevatorServer: Reboot request received." << std::endl; + } + + if (requestRep.getValue("fr", action)) + { + std::cout << "ElevatorServer: Factory reset request received." << std::endl; + } + + if (SendMaintenanceResponse(request) == OC_STACK_OK) + { + ehResult = OC_EH_OK; + } + + ehResult = OC_EH_OK; + } + if (resourceUri.compare(ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH) == 0) + { + m_relativePathResourceCreateCount++; + + // Prepare the response. + auto pResponse = std::make_shared(); + pResponse->setRequestHandle(request->getRequestHandle()); + pResponse->setResourceHandle(request->getResourceHandle()); + pResponse->setNewResourceUri(ELEVATOR_RESOURCE_NEW_RESOURCE_PATH); + pResponse->setResponseResult(OC_EH_RESOURCE_CREATED); + + // Send the response. + OCPlatform::sendResponse(pResponse); + ehResult = OC_EH_OK; + } + + } + else if (requestType == OC::PlatformCommands::PUT) + { + m_explicitPathResourceCreateCount++; + ehResult = OC_EH_OK; + } + else if (requestType == OC::PlatformCommands::DELETE) + { + if (resourceUri.compare(ELEVATOR_RESOURCE_DELETE_PATH) == 0) + { + m_deleteResourceCount++; + + // Prepare the response. + auto pResponse = std::make_shared(); + pResponse->setRequestHandle(request->getRequestHandle()); + pResponse->setResourceHandle(request->getResourceHandle()); + pResponse->setResponseResult(OC_EH_RESOURCE_DELETED); + + // Send the response. + OCPlatform::sendResponse(pResponse); + ehResult = OC_EH_OK; + } + } + } + + if (requestFlag & RequestHandlerFlag::ObserverFlag) + { + ObservationInfo observationInfo = request->getObservationInfo(); + if (ObserveAction::ObserveRegister == observationInfo.action) + { + OIC_LOG_V(INFO, TAG, "ElevatorEntityHandler(): new observer ID: %d", + observationInfo.obsId); + m_observers.push_back(observationInfo.obsId); + } + else if (ObserveAction::ObserveUnregister == observationInfo.action) + { + OIC_LOG_V(INFO, TAG, "ElevatorEntityHandler(): removing observer ID: %d", + observationInfo.obsId); + m_observers.erase(std::remove( + m_observers.begin(), + m_observers.end(), + observationInfo.obsId), + m_observers.end()); + } + + ehResult = OC_EH_OK; + } + } + + return ehResult; +} + + +// Copy from std::string to char array. Return true if source is truncated at dest. +bool CopyStringToBuffer(std::string& source, char* dest, size_t destSize) +{ + bool isTruncated = false; + size_t copied = source.copy(dest, destSize, 0); + if (copied == destSize) + { + copied -= 1; // make room for null + isTruncated = true; + } + + // std::string copy does not include null. + dest[copied] = 0x00; + return isTruncated; +} + +bool ElevatorServer::Start(std::string& elevatorName) +{ + // OCPlatform needs only 1 time initialization. + static bool OCFInitialized = false; + if (false == OCFInitialized) + { + PlatformConfig Configuration { + ServiceType::InProc, + ModeType::Both, + "0.0.0.0", // By setting to "0.0.0.0", it binds to all available + // interfaces + 0, // Uses randomly available port + QualityOfService::NaQos, + &elevatorServerPS + }; + + OCPlatform::Configure(Configuration); + OCFInitialized = true; + } + + if (false == m_isRunning) + { + std::string resourceTypeName(ELEVATOR_RESOURCE_TYPE); + std::string resourcePath(ELEVATOR_RESOURCE_PATH); + + m_name = elevatorName; + + // Start with known state. + m_targetFloor = m_currentFloor = 1; + m_direction = ElevatorDirection::Stopped; + + // Start the engine thread. + m_isRunning = true; + m_engineThread = std::thread(&ElevatorServer::Engine, this); + + // Device Info. + char devName[256]; + char resTypeName[256]; + CopyStringToBuffer(m_name, devName, 256); + CopyStringToBuffer(resourceTypeName, resTypeName, 256); + OCStringLL types { nullptr, resTypeName }; + OCDeviceInfo deviceInfo = { devName, &types, "0.0.1", nullptr }; + + std::vector dataModelVersions = { + ELEVATOR_DATA_MODEL_VERSION_1, + ELEVATOR_DATA_MODEL_VERSION_2, + ELEVATOR_DATA_MODEL_VERSION_3}; + + + // Platform Info + char* platformId = ELEVATOR_PLATFORM_ID; + char manufacturerName[] = "Elevator Manufacturer"; + char manufacturerUrl[] = "http://www.example.com/elevator"; + char modelNumber[] = "Elevator Model Number"; + char dateManufacture[] = "2017-02-28"; + char platformVersion[] = "Elevator Platform Version"; + char osVersion[] = "Elevator OS Version"; + char hardwareVersion[] = "Elevator Hardware Version"; + char firmwareVersion[] = "Elevator Firmware Version"; + char supportURL[] = "http://www.example.com/elevator/support"; + + OCPlatformInfo platformInfo = { + platformId, + manufacturerName, + manufacturerUrl, + modelNumber, + dateManufacture, + platformVersion, + osVersion, + hardwareVersion, + firmwareVersion, + supportURL, + nullptr}; + + // Register elevator's platformInfo, deviceInfo, and resource. + if (OC_STACK_OK != OCPlatform::registerPlatformInfo(platformInfo)) + { + return false; + } + + if (OC_STACK_OK != OCPlatform::registerDeviceInfo(deviceInfo)) + { + return false; + } + + // additional info for device info. + if (OC_STACK_OK != OCPlatform::setPropertyValue( + PAYLOAD_TYPE_DEVICE, + OC_RSRVD_PROTOCOL_INDEPENDENT_ID, + ELEVATOR_PLATFORM_INDEPENDENT_ID)) + { + return false; + } + + if (OC_STACK_OK != OCPlatform::setPropertyValue( + PAYLOAD_TYPE_DEVICE, + OC_RSRVD_DATA_MODEL_VERSION, + dataModelVersions)) + { + return false; + } + + OCStackResult result = OCPlatform::registerResource( + m_elevatorResourceHandle, + resourcePath, + resourceTypeName, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + if (result != OC_STACK_OK) + { + return false; + } + + std::string maintenanceResourcePath(ELEVATOR_MAINTENANCE_PATH); + std::string mainenanceResourceType(OC_RSRVD_RESOURCE_TYPE_MAINTENANCE); + + result = OCPlatform::registerResource( + m_elevatorMaintenanceHandle, + maintenanceResourcePath, + mainenanceResourceType, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE); + + if (result != OC_STACK_OK) + { + return false; + } + + std::string COresourceTypeName(ELEVATOR_CO_RESOURCE_TYPE); + std::string COresourcePath(ELEVATOR_CO_RESOURCE_PATH); + result = OCPlatform::registerResource( + m_elevatorCOResourceHandle, + COresourcePath, + COresourceTypeName, + ELEVATOR_CO_PRIVATE_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE); + + if (result != OC_STACK_OK) + { + return false; + } + + + std::string createRelativeResourcePath(ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH); + std::string createRelativeResourceType(ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH_TYPE); + result = OCPlatform::registerResource( + m_elevatorCreateRelativeResource, + createRelativeResourcePath, + createRelativeResourceType, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE); + + if (result != OC_STACK_OK) + { + return false; + } + + std::string deleteResourcePath(ELEVATOR_RESOURCE_DELETE_PATH); + std::string deleteResourceType(ELEVATOR_RESOURCE_DELETE_TYPE); + result = OCPlatform::registerResource( + m_elevatorDeleteResource, + deleteResourcePath, + deleteResourceType, + DEFAULT_INTERFACE, + std::bind(&ElevatorServer::ElevatorEntityHandler, this, _1), + OC_DISCOVERABLE | OC_OBSERVABLE); + + if (result != OC_STACK_OK) + { + return false; + } + + return true; + } + + // It's already running. + return true; +} + +void ElevatorServer::Stop() +{ + if (true == m_isRunning) + { + // Unregister OCF resources. + OCPlatform::unregisterResource(m_elevatorResourceHandle); + m_elevatorResourceHandle = nullptr; + + OCPlatform::unregisterResource(m_elevatorCOResourceHandle); + m_elevatorCOResourceHandle = nullptr; + + OCPlatform::unregisterResource(m_elevatorMaintenanceHandle); + m_elevatorMaintenanceHandle = nullptr; + + OCPlatform::unregisterResource(m_elevatorCreateRelativeResource); + m_elevatorCreateRelativeResource = nullptr; + + OCPlatform::unregisterResource(m_elevatorDeleteResource); + m_elevatorDeleteResource = nullptr; + + // Signal the m_engineThread to stop and wait for it to exit. + m_isRunning = false; + if (m_engineThread.joinable()) + { + m_engineThread.join(); + } + } +} + +void ElevatorServer::SetTargetFloor(int floor) +{ + m_targetFloor = floor; +} + +int ElevatorServer::GetTargetFloor() +{ + return m_targetFloor; +} + +int ElevatorServer::GetCurrentFloor() +{ + return m_currentFloor; +} + +ElevatorDirection ElevatorServer::GetElevatorDirection() +{ + return m_direction; +} + +void ElevatorServer::Engine(ElevatorServer* elevator) +{ + while (elevator->m_isRunning == true) + { + elevator->MoveElevator(); + std::this_thread::sleep_for(std::chrono::seconds(0)); + } +} + +void ElevatorServer::NotifyObservers(std::string propertyName, int value) +{ + if (m_observers.size() == 0) + { + return; + } + + OIC_LOG_V(INFO, TAG, "NotifyObservers(): notifying observer of property:: %s", + propertyName.c_str()); + + OCRepresentation rep; + rep[propertyName.c_str()] = value; + + // Prepare the response. + auto response = std::make_shared(); + response->setResourceRepresentation(rep, DEFAULT_INTERFACE); + + OCStackResult result = OCPlatform::notifyListOfObservers( + m_elevatorResourceHandle, + m_observers, + response); + + if (OC_STACK_NO_OBSERVERS == result) + { + std::cout << "ElevatorServer:: failed notifyListOfObservers: result = "; + std::cout << result << std::endl; + } + +} + +void ElevatorServer::MoveElevator() +{ + int dwTargetFloor = m_targetFloor; + int incrementValue; + + if (m_currentFloor == dwTargetFloor) + { + return; + } + + if (m_currentFloor < dwTargetFloor) + { + m_direction = ElevatorDirection::Up; + incrementValue = 1; + } + else + { + m_direction = ElevatorDirection::Down; + incrementValue = -1; + } + + NotifyObservers(ELEVATOR_PROPERTY_DIRECTION, m_direction); + + while (m_currentFloor != dwTargetFloor) + { + const int delayBetweenFloorInSecond = 0; + m_currentFloor += incrementValue; + NotifyObservers(ELEVATOR_PROPERTY_CURRENT_FLOOR, m_currentFloor); + std::this_thread::sleep_for(std::chrono::seconds(delayBetweenFloorInSecond)); + } + + m_direction = ElevatorDirection::Stopped; + NotifyObservers(ELEVATOR_PROPERTY_DIRECTION, m_direction); +} diff --git a/resource/IPCA/unittests/testelevatorserver.h b/resource/IPCA/unittests/testelevatorserver.h new file mode 100644 index 0000000..9cf13eb --- /dev/null +++ b/resource/IPCA/unittests/testelevatorserver.h @@ -0,0 +1,124 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft + * + * + * 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. + * + ******************************************************************/ + +#ifndef _ELEVATOR_SERVER_H +#define _ELEVATOR_SERVER_H + +#include +#include "OCPlatform.h" +#include "OCApi.h" + +using namespace OC; + +typedef enum +{ + Stopped = 0, + Up, + Down +} ElevatorDirection; + +class ElevatorServer +{ +public: + ElevatorServer(); + ~ElevatorServer(); + + // Start stop the thread processing the elevator movement. Also register/unregister the + // elevator from IoTivity. + bool Start(std::string& elevatorName); + void Stop(); + + // Target floor is set by caller. + void SetTargetFloor(int floor); + int GetTargetFloor(); + + // Current floor is set by elevator. + int GetCurrentFloor(); + ElevatorDirection GetElevatorDirection(); + + size_t GetRelativePathResourceCreateCount() { return m_relativePathResourceCreateCount; } + size_t GetExplicitPathResourceCreateCount() { return m_explicitPathResourceCreateCount; } + size_t GetDeleteResourceCount() { return m_deleteResourceCount; } + size_t GetIncorrectInterfaceCount() { return m_IncorrectInterfaceCount; } + +private: + // List of observers, when client app calls resource->Observer(). + ObservationIds m_observers; + + // Send notification to observers when property values change. + void NotifyObservers(std::string propertyName, int value); + + // Elevator has one resource. + OCResourceHandle m_elevatorResourceHandle; // The elevator resource. + OCResourceHandle m_elevatorCOResourceHandle; // CO resource. + OCResourceHandle m_elevatorMaintenanceHandle; + OCResourceHandle m_elevatorCreateRelativeResource; // Resource that handles pretent create. + OCResourceHandle m_elevatorDeleteResource; // Resource that handles pretend delete. + + int m_targetFloor; // where elevator needs to be. + int m_currentFloor; // where elevator is. + ElevatorDirection m_direction; // current direction of the elevator. + + // Thread moving the elevator. + std::thread m_engineThread; + bool m_isRunning; + static void Engine(ElevatorServer* elevator); + + // Move current floor to target floor. + void MoveElevator(); + + // Helper function to send response for a request. + OCStackResult SendResponse(std::shared_ptr request, + OCEntityHandlerResult result = OC_EH_OK); + OCStackResult SendMaintenanceResponse(std::shared_ptr request); + + // OCF callback for this elevator. + OCEntityHandlerResult ElevatorEntityHandler(std::shared_ptr request); + + // Elevator device details. + std::string m_name; + + // Elevator platform details. + std::string m_platformID; + std::string m_modelNumber; + std::string m_platformVersion; + std::string m_serialNumber; + std::string m_specVersion; + std::string m_defaultLanguage; + std::string m_manufacturerName; + std::string m_manufacturerUrl; + std::string m_dateOfManufacture; + std::string m_operatingSystemVersion; + std::string m_hardwareVersion; + std::string m_firmwareVersion; + std::string m_supportUrl; + std::string m_systemTime; + + // Elevator new resource request count. + size_t m_relativePathResourceCreateCount; + size_t m_explicitPathResourceCreateCount; + + // Elevator delete resource count. + size_t m_deleteResourceCount; + + // Number of times entity handler is called with incorrect resource interface. + size_t m_IncorrectInterfaceCount; +}; + +#endif // _ELEVATOR_SERVER_H diff --git a/resource/SConscript b/resource/SConscript index 0e62a8d..3a0d5ae 100644 --- a/resource/SConscript +++ b/resource/SConscript @@ -49,6 +49,10 @@ if target_os not in ['arduino', 'darwin', 'ios']: # Build liboc SConscript('src/SConscript') +if target_os in ['windows']: + # Build IoTivity Procedural Client API + SConscript('IPCA/SConscript') + if target_os not in ['arduino','darwin','ios','android']: # Build examples SConscript('examples/SConscript') diff --git a/resource/csdk/include/octypes.h b/resource/csdk/include/octypes.h index 95bb8ea..566815b 100644 --- a/resource/csdk/include/octypes.h +++ b/resource/csdk/include/octypes.h @@ -166,6 +166,9 @@ extern "C" { /** To represent resource type with platform.*/ #define OC_RSRVD_RESOURCE_TYPE_PLATFORM "oic.wk.p" +/** To represent resource type with maintenance.*/ +#define OC_RSRVD_RESOURCE_TYPE_MAINTENANCE "oic.wk.mnt" + /** To represent resource type with collection.*/ #define OC_RSRVD_RESOURCE_TYPE_COLLECTION "oic.wk.col" diff --git a/resource/csdk/stack/octbstack_product_secured.def b/resource/csdk/stack/octbstack_product_secured.def index aa4f1a7..400792b 100644 --- a/resource/csdk/stack/octbstack_product_secured.def +++ b/resource/csdk/stack/octbstack_product_secured.def @@ -21,6 +21,7 @@ OCDeletePdAclList OCDeleteUuidList OCDiscoverOwnedDevices OCDiscoverSingleDevice +OCDiscoverSingleDeviceInUnicast OCDiscoverUnownedDevices OCDoOwnershipTransfer OCGetACLResource @@ -43,6 +44,7 @@ OCRemoveTrustCertChainNotifier OCResetDevice OCResetSVRDB OCSaveTrustCertChain +OCSelectOwnershipTransferMethod OCSetOwnerTransferCallbackData OCUnlinkDevices OCSetOxmAllowStatus diff --git a/resource/unit_tests.scons b/resource/unit_tests.scons index 98b36fa..ae080c8 100644 --- a/resource/unit_tests.scons +++ b/resource/unit_tests.scons @@ -48,5 +48,9 @@ if target_os in ['linux', 'windows', 'darwin', 'msys_nt']: if (env.get('SECURED') == '1') and (target_os != 'windows'): SConscript('provisioning/unittests/SConscript', 'test_env') + if target_os in ['windows']: + # Build IPCA unit tests + SConscript('IPCA/unittests/SConscript', 'test_env') + # Build C unit tests SConscript('csdk/unittests/SConscript', 'test_env') -- 2.7.4