#include "oic_time.h"
#include "ipcainternal.h"
+#include "ocrandom.h"
// Object that implements interface to IoTivity.
// @future: Consider having an instance of this per app when there's mechanism to unregister
{
}
-IPCAStatus App::Start(bool unitTestMode)
+IPCAStatus App::Start(bool unitTestMode, App::Ptr thisSharedPtr)
{
- m_callback = std::shared_ptr<Callback>(new Callback(this));
+ char appId[UUID_STRING_SIZE];
+ if (!OCConvertUuidToString(m_ipcaAppInfo.appId.uuid, appId))
+ {
+ return IPCA_FAIL;
+ }
+
+ m_appId = appId;
+ m_thisSharedPtr = thisSharedPtr;
+
+ m_callback = std::make_shared<Callback>(m_thisSharedPtr);
if (m_callback == nullptr)
{
return IPCA_OUT_OF_MEMORY;
}
// Start periodic discovery thread.
- m_appWorkerThread = std::thread(&App::AppWorkerThread, this);
+ m_appWorkerThread = std::thread(&App::AppWorkerThread, m_thisSharedPtr);
return IPCA_OK;
}
DeleteAndUnregisterCallbackInfo(m_passwordDisplayCallbackInfo->mapKey);
m_passwordDisplayCallbackInfo = nullptr;
}
+
+ m_thisSharedPtr = nullptr; // Release reference to self.
+}
+
+std::string App::GetAppId()
+{
+ return m_appId;
}
-void App::AppWorkerThread(App* app)
+void App::AppWorkerThread(App::Ptr app)
{
const uint64_t FastDiscoveryCount = 4; // First 4 periodic discovery requests use fast period.
const uint64_t SlowDiscoveryPeriodMs = 30000;
OIC_LOG_V(INFO, TAG, "-AppWorkerThread exit.");
}
-IPCAStatus App::OpenDevice(const char* deviceId, IPCADeviceHandle* deviceHandle)
+IPCAStatus App::OpenDevice(App::Ptr thisApp, const char* deviceId, IPCADeviceHandle* deviceHandle)
{
*deviceHandle = nullptr;
return IPCA_OUT_OF_MEMORY;
}
- Device::Ptr device = std::shared_ptr<Device>(new Device(deviceId, &ocfFramework, this));
+ Device::Ptr device = std::shared_ptr<Device>(new Device(deviceId, &ocfFramework, thisApp));
if (device == nullptr)
{
return IPCA_OUT_OF_MEMORY;
return status;
}
- deviceWrapper->app = this;
- deviceWrapper->device = device;
+ deviceWrapper->app = thisApp;
+ deviceWrapper->device = device; // Take a device reference.
*deviceHandle = reinterpret_cast<IPCADeviceHandle>(deviceWrapper.get());
- m_openedDevices[deviceWrapper.get()] = deviceWrapper.get(); // Take a device reference.
+ m_openedDevices[deviceWrapper.get()] = deviceWrapper.get();
deviceWrapper.release();
return IPCA_OK;
}
#define TAG "IPCA_Callback"
+// Next key for the m_callbackInfoList map. Key is unique across all IPCA apps.
+static std::atomic<size_t> g_nextKey(1);
+
extern OCFFramework ocfFramework;
-Callback::Callback(App* app) :
- m_nextKey(0),
+Callback::Callback(AppPtr app) :
m_app(app),
m_stopCalled(false),
m_expiredCallbacksInProgress(0)
uint32_t i = 0;
while (i++ < UINT_MAX)
{
- size_t newKey = m_nextKey++;
+ size_t newKey = g_nextKey++;
+ if (newKey == 0)
+ {
+ // Avoid misinterpreted as NULL handle.
+ continue;
+ }
+
if (m_callbackInfoList.find(newKey) == m_callbackInfoList.end())
{
OIC_LOG_V(INFO, TAG, "AddCallbackInfo() with key: %" PRIuPTR, newKey);
#include "ipcainternal.h"
#include "oic_malloc.h"
-Device::Device(const char* deviceId, OCFFramework* ocf, App* app) :
+Device::Device(const char* deviceId, OCFFramework* ocf, App::Ptr app) :
m_deviceId(deviceId),
m_app(app),
m_ocfFramework(ocf),
struct IPCAAppInfoInternal
{
IPCAUuid appId;
+ std::string appIdString;
std::string appName;
std::string appSoftwareVersion;
std::string appCompanyName;
class App
{
public:
+ typedef std::shared_ptr<App> Ptr;
+
App(const IPCAAppInfo* ipcaAppInfo, IPCAVersion ipcaVersion);
~App();
// Application calls IPCAOpen()/IPCAClose().
- IPCAStatus Start(bool unitTestMode);
+ IPCAStatus Start(bool unitTestMode, App::Ptr thisSharedPtr);
void Stop();
+ // Returns application's ID.
+ std::string GetAppId();
+
// Application calls IPCADiscoverDevices().
IPCAStatus DiscoverDevices(
IPCADiscoverDeviceCallback callback,
IPCAHandle* handle);
// Application calls IPCAOpenDevice().
- IPCAStatus OpenDevice(const char* deviceId, IPCADeviceHandle* deviceHandle);
+ IPCAStatus OpenDevice(
+ App::Ptr thisApp,
+ const char* deviceId,
+ IPCADeviceHandle* deviceHandle);
+
void CloseDevice(IPCADeviceHandle deviceHandle);
// Application calls IPCAGetProperties().
IPCAHandle* handle);
// Close handle returned in DiscoverDevices(), GetProperties(), SetProperties(),
- // ObserveResource(), CreateResource() and RequestAccess().
+ // ObserveResource(), CreateResource() or RequestAccess().
+ // Return IPCA_OK if successful. Or failure, for example, if handle is already closed.
IPCAStatus CloseIPCAHandle(
IPCAHandle handle,
IPCACloseHandleComplete closeHandleComplete,
const void* context);
-
private:
std::mutex m_appMutex;
+ App::Ptr m_thisSharedPtr; // shared_ptr of this object.
volatile bool m_isStopped; // set to true when Stop() is called.
IPCAAppInfoInternal m_ipcaAppInfo;
+ std::string m_appId; // String of m_ipcaAppInfo.appId.
IPCAVersion m_ipcaVersion; // IPCA version requested in the call to IPCAOpen().
// Object that implements callbacks to the app.
const void* context = nullptr);
// Thread performing periodic discovery.
- static void AppWorkerThread(App* app);
+ static void AppWorkerThread(App::Ptr app);
// List of resource types to discover periodically.
// Key is cbInfo->mapKey of each IPCADiscoverDevices() request.
class App;
class Device;
typedef std::shared_ptr<Device> DevicePtr;
+typedef std::shared_ptr<App> AppPtr;
// Structure contains information to make callbacks to app.
struct CallbackInfo
std::cout << mapKey << std::endl; */ }
typedef std::shared_ptr<CallbackInfo> 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.
+ AppPtr app; // the app that creates this callback.
+ DevicePtr device; // the device expected to respond to the callback.
CallbackType type;
union
{
{
public:
typedef std::shared_ptr<Callback> Ptr;
- Callback(App* app);
+ Callback(AppPtr app);
~Callback();
// Preparation to shut down.
std::mutex m_discoverDeviceCallbackMutex;
// Table of CallbackInfo. Key is autogenerated.
- std::atomic<size_t> m_nextKey; // next key for the m_callbackInfoList map.
std::map<size_t, CallbackInfo::Ptr> m_callbackInfoList; // List of expected callbacks.
- App* m_app; // Callback object is per app.
+ AppPtr m_app; // Callback object is per app.
volatile bool m_stopCalled; // Set to true when Stop() is called.
// Number of expired callbacks in progress.
class App;
class OCFFramework;
+typedef std::shared_ptr<App> AppPtr;
+
/* Type of resource info requested in GetResourceInfo() */
enum class ResourceInfoType
{
public:
typedef std::shared_ptr<Device> Ptr;
- Device(const char* deviceId, OCFFramework* ocf, App* app);
+ Device(const char* deviceId, OCFFramework* ocf, AppPtr app);
~Device();
- App* GetApp() { return m_app; }
+ AppPtr 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.
private:
std::string m_deviceId; // from IPCAOpenDevice()
- App* m_app;
+ AppPtr m_app;
OCFFramework* m_ocfFramework;
bool m_isClosed; // set to true after Close().
};
// returned as IPCAHandle to the app.
typedef struct
{
- App* app;
+ AppPtr app;
Device::Ptr device;
} DeviceWrapper;
#include "iotivity_config.h"
#include "oic_malloc.h"
#include "ipca.h"
+#include "ocrandom.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;
-
+// List of applications. Since IPCA runs inproc and also IoTivity does not yet support multiple
+// apps (i.e. servers) in same process, the max count of g_AppList is currently 1.
+static std::map<size_t, App::Ptr> g_AppList; // Map's key is generated from g_nextAppKey.
+static std::map<size_t, uint32_t> g_AppListRefCount; // Ref count of App objects.
+size_t g_nextAppKey = 1;
+std::recursive_mutex g_ipcaAppMutex;
bool g_unitTestMode = false;
+// Return App with matching appId.
+App::Ptr FindApp(size_t appId)
+{
+ std::lock_guard<std::recursive_mutex> lock(g_ipcaAppMutex);
+ if (g_AppList.find(appId) == g_AppList.end())
+ {
+ return nullptr;
+ }
+
+ return g_AppList[appId];
+}
+
IPCAStatus IPCA_CALL IPCAOpen(
const IPCAAppInfo* ipcaAppInfo,
IPCAVersion ipcaVersion,
IPCAAppHandle* ipcaAppHandle)
{
- std::lock_guard<std::mutex> lock(g_ipcaAppMutex);
+ App::Ptr app = nullptr;
+
+ std::lock_guard<std::recursive_mutex> 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 ||
return IPCA_INVALID_ARGUMENT;
}
- g_app= new App(ipcaAppInfo, ipcaVersion);
+ char appId[UUID_STRING_SIZE];
+ if (!OCConvertUuidToString(ipcaAppInfo->appId.uuid, appId))
+ {
+ return IPCA_FAIL;
+ }
+
+ // Since IPCA runs in proc, support one IPCAOpen() per process.
+ // An exception is made for app calling IPCAOpen() with same app ID to conveniently retrieve the
+ // IPCAAppHandle.
+ for (auto registeredApp : g_AppList)
+ {
+ if (registeredApp.second->GetAppId().compare(appId) == 0)
+ {
+ *ipcaAppHandle = reinterpret_cast<IPCAAppHandle>(registeredApp.first);
+ g_AppListRefCount[registeredApp.first]++;
+ return IPCA_OK;
+ }
+ }
- if (g_app == nullptr)
+ // One App object per process. In future, may support multiple App objects per process when
+ // https://jira.iotivity.org/browse/IOT-1379 is resolved.
+ if (g_AppList.size() != 0)
+ {
+ return IPCA_ALREADY_OPENED;
+ }
+
+ app = std::make_shared<App>(ipcaAppInfo, ipcaVersion);
+ if (app == nullptr)
{
return IPCA_OUT_OF_MEMORY;
}
- IPCAStatus status = g_app->Start(g_unitTestMode);
+ IPCAStatus status = app->Start(g_unitTestMode, app);
if (status != IPCA_OK)
{
- delete g_app;
- g_app = nullptr;
return status;
}
- *ipcaAppHandle = reinterpret_cast<IPCAAppHandle>(g_app);
- return IPCA_OK;
+ uint32_t i = 0;
+ while (i++ < UINT_MAX)
+ {
+ size_t newAppKey = g_nextAppKey++;
+
+ if (newAppKey == 0)
+ {
+ // Skip returning handle with value 0.
+ continue;
+ }
+
+ if (g_AppList.find(newAppKey) == g_AppList.end())
+ {
+ g_AppList[newAppKey] = app;
+ g_AppListRefCount[newAppKey] = 1;
+ *ipcaAppHandle = reinterpret_cast<IPCAAppHandle>(newAppKey);
+ return IPCA_OK;
+ }
+ }
+
+ // Too many apps.
+ app->Stop();
+ return IPCA_FAIL;
}
void IPCA_CALL IPCAClose(IPCAAppHandle ipcaAppHandle)
{
- App* app = reinterpret_cast<App*>(ipcaAppHandle);
- std::lock_guard<std::mutex> lock(g_ipcaAppMutex);
- if (g_app == nullptr)
+ std::lock_guard<std::recursive_mutex> lock(g_ipcaAppMutex);
+ App::Ptr app = FindApp(reinterpret_cast<size_t>(ipcaAppHandle));
+ if (app == nullptr)
{
return;
}
- app->Stop();
- delete app;
- g_app = nullptr;
+ if ((--g_AppListRefCount[reinterpret_cast<size_t>(ipcaAppHandle)]) == 0)
+ {
+ // stop the app.
+ app->Stop();
+
+ // remove app from list.
+ g_AppList.erase(reinterpret_cast<size_t>(ipcaAppHandle));
+ g_AppListRefCount.erase(reinterpret_cast<size_t>(ipcaAppHandle));
+ }
}
IPCAStatus IPCA_CALL IPCADiscoverDevices(
int resourceTypeCount,
IPCAHandle* handle)
{
- App* app = reinterpret_cast<App*>(ipcaAppHandle);
+ App::Ptr app = FindApp(reinterpret_cast<size_t>(ipcaAppHandle));
+ if (app == nullptr)
+ {
+ return IPCA_INVALID_ARGUMENT;
+ }
+
return app->DiscoverDevices(callback, context, resourceTypeList, resourceTypeCount, handle);
}
const char* deviceId,
IPCADeviceHandle* deviceHandle)
{
- App* app = reinterpret_cast<App*>(ipcaAppHandle);
- return app->OpenDevice(deviceId, deviceHandle);
+ App::Ptr app = FindApp(reinterpret_cast<size_t>(ipcaAppHandle));
+ if (app == nullptr)
+ {
+ return IPCA_INVALID_ARGUMENT;
+ }
+
+ return app->OpenDevice(app, deviceId, deviceHandle);
}
void IPCA_CALL IPCACloseDevice(IPCADeviceHandle deviceHandle)
{
DeviceWrapper* deviceWrapper = reinterpret_cast<DeviceWrapper*>(deviceHandle);
- App* app = deviceWrapper->device->GetApp();
+ App::Ptr app = deviceWrapper->device->GetApp();
app->CloseDevice(deviceHandle);
}
IPCACloseHandleComplete closeHandleComplete,
void* context)
{
- std::lock_guard<std::mutex> lock(g_ipcaAppMutex);
- if (g_app != nullptr)
+ std::lock_guard<std::recursive_mutex> lock(g_ipcaAppMutex);
+
+ // There's no direct mapping from handle to App. When number of App objects is large, may need
+ // to create a mapping table for efficiency.
+ for (auto app : g_AppList)
{
- return (g_app->CloseIPCAHandle(handle, closeHandleComplete, context));
+ if (app.second->CloseIPCAHandle(handle, closeHandleComplete, context) == IPCA_OK)
+ {
+ // handle is unique across all apps.
+ return IPCA_OK;
+ }
}
return IPCA_FAIL;
IPCADisplayPasswordCallback displayCallback,
void* context)
{
- App* app = reinterpret_cast<App*>(ipcaAppHandle);
+ App::Ptr app = FindApp(reinterpret_cast<size_t>(ipcaAppHandle));
+ if (app == nullptr)
+ {
+ return IPCA_INVALID_ARGUMENT;
+ }
+
return app->SetPasswordCallbacks(inputCallback, displayCallback, context);
}
IPCAAppHandle m_anotherIPCAAppHandle;
IPCAAppInfo m_ipcaAppInfo;
- IPCAStatus DoAnotherIPCAOpen()
+ IPCAStatus DoAnotherIPCAOpenWithSameAppId()
{
return IPCAOpen(&m_ipcaAppInfo, IPCA_VERSION_1, &m_anotherIPCAAppHandle);
}
+ IPCAStatus DoAnotherIPCAOpenWithDifferentAppId()
+ {
+ IPCAAppInfo ipcaAppInfoExtra;
+ IPCAAppHandle ipcaAppHandleExtra;
+ IPCAUuid extraTestAppUuid = {
+ {0x63, 0x72, 0xc0, 0xea, 0x1f, 0x12, 0x11, 0xe7,
+ 0x93, 0xae, 0x92, 0x36, 0x1f, 0x00, 0x26, 0x71}
+ };
+
+ ipcaAppInfoExtra = { extraTestAppUuid, IPCATestAppName, "1.0.0", "Microsoft" };
+ return IPCAOpen(&ipcaAppInfoExtra, IPCA_VERSION_1, &ipcaAppHandleExtra);
+ }
+
protected:
virtual void SetUp()
{
}
};
-TEST_F(IPCAMiscTest, ShouldNotAllowMultipleCallsToIPCOpen)
+TEST_F(IPCAMiscTest, MultipleIPCAOpen)
{
- EXPECT_EQ(IPCA_ALREADY_OPENED, DoAnotherIPCAOpen());
+ EXPECT_EQ(IPCA_OK, DoAnotherIPCAOpenWithSameAppId());
+ EXPECT_EQ(IPCA_ALREADY_OPENED, DoAnotherIPCAOpenWithDifferentAppId());
}
TEST_F(IPCAMiscTest, IPCAOpenShouldBeAllowedAfterIPCAClose)
IPCAClose(m_ipcaAppHandle);
m_ipcaAppHandle = NULL;
- ASSERT_EQ(IPCA_OK, DoAnotherIPCAOpen());
+ ASSERT_EQ(IPCA_OK, DoAnotherIPCAOpenWithSameAppId());
}
/*