[Feature] - API in Dbus to handle "Display Off" signal.
- Switching to default container when "Display Off" signal occurs.
[Cause] SC must properly react when device is inactive for some time.
[Solution] Create a Dbus API for Display Off signal. Use this event to switch to default
container.
[Verification] Build, install, run ContainersManagerSuite and ContainerConnectionSuite tests. Both
suites should pass.
Change-Id: I34e0178cd9d8efbbdad92e1f2d69f4c32b41f779
Signed-off-by: Lukasz Kostyra <l.kostyra@samsung.com>
{
"containerConfigs" : ["containers/private.conf", "containers/business.conf" ],
"foregroundId" : "private",
+ "defaultId" : "private",
"inputConfig" : {"enabled" : true,
"device" : "gpio-keys",
"code" : 139,
#include "container-connection.hpp"
#include "container-dbus-definitions.hpp"
#include "exception.hpp"
+// TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager
+#include "fake-power-manager-dbus-definitions.hpp"
#include "log/logger.hpp"
_3,
_4,
_5));
+
+ mDbusConnection->signalSubscribe(std::bind(&ContainerConnection::onSignalReceived,
+ this,
+ _1,
+ _2,
+ _3,
+ _4,
+ _5),
+ std::string(fake_power_manager_api::BUS_NAME));
+
LOGD("Connected");
}
mNotifyActiveContainerCallback = callback;
}
+void ContainerConnection::setDisplayOffCallback(const DisplayOffCallback& callback)
+{
+ mDisplayOffCallback = callback;
+}
+
void ContainerConnection::onMessageCall(const std::string& objectPath,
const std::string& interface,
const std::string& methodName,
}
}
+void ContainerConnection::onSignalReceived(const std::string& senderBusName,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& signalName,
+ GVariant* /*parameters*/)
+{
+ LOGD("Received signal: " << senderBusName << "; " << objectPath << "; " << interface << "; "
+ << signalName);
+ if (objectPath == fake_power_manager_api::OBJECT_PATH &&
+ interface == fake_power_manager_api::INTERFACE) {
+ //power-manager sent us a signal, check it
+ if (signalName == fake_power_manager_api::SIGNAL_DISPLAY_OFF && mDisplayOffCallback) {
+ mDisplayOffCallback();
+ }
+ }
+}
+
void ContainerConnection::sendNotification(const std::string& container,
const std::string& application,
const std::string& message)
public:
typedef std::function<void()> OnNameLostCallback;
+ typedef std::function<void()> DisplayOffCallback;
ContainerConnection(const std::string& address, const OnNameLostCallback& callback);
~ContainerConnection();
void setNotifyActiveContainerCallback(const NotifyActiveContainerCallback& callback);
/**
+ * Register callback to handle turning off the display
+ */
+ void setDisplayOffCallback(const DisplayOffCallback& callback);
+
+ /**
* Send notification signal to this container
*/
void sendNotification(const std::string& container,
bool mNameLost;
OnNameLostCallback mOnNameLostCallback;
NotifyActiveContainerCallback mNotifyActiveContainerCallback;
+ DisplayOffCallback mDisplayOffCallback;
void onNameAcquired();
void onNameLost();
const std::string& methodName,
GVariant* parameters,
dbus::MethodResultBuilder& result);
+ void onSignalReceived(const std::string& senderBusName,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& signalName,
+ GVariant* parameters);
};
if (mNotifyCallback) {
mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
}
+ if (mDisplayOffCallback) {
+ mConnection->setDisplayOffCallback(mDisplayOffCallback);
+ }
+
// Send to the background only after we're connected,
// otherwise it'd take ages.
LOGD(getId() << ": DBUS connected, sending to the background");
}
}
+void Container::setDisplayOffCallback(const DisplayOffCallback& callback)
+{
+ Lock lock(mReconnectMutex);
+
+ mDisplayOffCallback = callback;
+ if (mConnection) {
+ mConnection->setDisplayOffCallback(callback);
+ }
+}
+
+
} // namespace security_containers
virtual ~Container();
typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
+ typedef ContainerConnection::DisplayOffCallback DisplayOffCallback;
/**
* Get the container id
void setNotifyActiveContainerCallback(const NotifyActiveContainerCallback& callback);
/**
+ * Register callback used when switching to default container.
+ */
+ void setDisplayOffCallback(const DisplayOffCallback& callback);
+
+ /**
* Send notification signal to this container
*
* @param container name of container in which the notification occurred
std::thread mReconnectThread;
mutable std::recursive_mutex mReconnectMutex;
NotifyActiveContainerCallback mNotifyCallback;
+ DisplayOffCallback mDisplayOffCallback;
void onNameLostCallback();
void reconnectHandler();
*/
std::string foregroundId;
+ /**
+ * An ID of default container.
+ */
+ std::string defaultId;
+
CONFIG_REGISTER
(
containerConfigs,
foregroundId,
+ defaultId,
inputConfig
)
};
id,
_1,
_2));
+
+ c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
+ this,
+ id));
+
mContainers.insert(ContainerMap::value_type(id, std::move(c)));
}
+ // check if default container exists, throw ContainerOperationException if not found
+ if (mContainers.find(mConfig.defaultId) == mContainers.end()) {
+ LOGE("Provided default container ID " << mConfig.defaultId << " is invalid.");
+ throw ContainerOperationException("Provided default container ID " + mConfig.defaultId +
+ " is invalid.");
+ }
+
LOGD("ContainersManager object instantiated");
if (mConfig.inputConfig.enabled) {
}
}
+void ContainersManager::displayOffHandler(const std::string& /*caller*/)
+{
+ LOGI("Switching to default container " << mConfig.defaultId);
+ focus(mConfig.defaultId);
+}
+
} // namespace security_containers
void notifyActiveContainerHandler(const std::string& caller,
const std::string& appliaction,
const std::string& message);
+ void displayOffHandler(const std::string& caller);
};
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file fake-power-manager-dbus-definitions.h
+ * @author Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief Declaration of fake dbus definitions from power-manager. Made only to test API in
+ * ContainerConnection.
+ */
+
+#ifndef FAKE_POWER_MANAGER_DBUS_DEFINITIONS_H
+#define FAKE_POWER_MANAGER_DBUS_DEFINITIONS_H
+
+/**
+ * !!WARNING!!
+ *
+ * This header file is created only to test if API in ContainerConnection works correctly. It should
+ * be removed when power-managers API will be created.
+ */
+
+namespace fake_power_manager_api
+{
+
+const std::string BUS_NAME = "com.tizen.fakepowermanager";
+const std::string OBJECT_PATH = "/com/tizen/fakepowermanager";
+const std::string INTERFACE = "com.tizen.fakepowermanager.manager";
+
+const std::string SIGNAL_DISPLAY_OFF = "DisplayOff";
+
+const std::string DEFINITION =
+ "<node>"
+ " <interface name='" + INTERFACE + "'>"
+ " <signal name='" + SIGNAL_DISPLAY_OFF + "'>"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+
+} // namespace fake_power_manager_api
+
+#endif // FAKE_POWER_MANAGER_DBUS_DEFINITIONS_H
{
"containerConfigs" : ["containers/console1.conf", "missing/file/path/missing.conf", "containers/console3.conf"],
"foregroundId" : "ut-containers-manager-console1",
+ "defaultId" : "ut-containers-manager-console1",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
--- /dev/null
+{
+ "containerConfigs" : ["containers/console1-dbus.conf", "containers/console2-dbus.conf", "containers/console3-dbus.conf"],
+ "foregroundId" : "ut-containers-manager-console1",
+ "defaultId" : "in_no_way_there_is_a_valid_id_here",
+ "inputConfig" : {"enabled" : false,
+ "device" : "/dev/doesnotexist",
+ "code" : 139,
+ "numberOfEvents" : 2,
+ "timeWindowMs" : 500}
+}
{
"containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
"foregroundId" : "this_id_does_not_exist",
+ "defaultId" : "ut-containers-manager-console1",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
{
"containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
"foregroundId" : "ut-containers-manager-console1",
+ "defaultId" : "ut-containers-manager-console1",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
"containers/console2-dbus.conf",
"containers/console3-dbus.conf"],
"foregroundId" : "ut-containers-manager-console1",
+ "defaultId" : "ut-containers-manager-console1",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
{
"containerConfigs" : ["containers/container1.conf", "missing/file/path/missing.conf", "containers/container3.conf"],
"foregroundId" : "ut-server-container1",
+ "defaultId" : "ut-server-container1",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
{
"containerConfigs" : ["containers/container1.conf", "containers/container2.conf", "containers/container3.conf"],
"foregroundId" : "ut-server-container1",
+ "defaultId" : "ut-server-container1",
"inputConfig" : {"enabled" : false,
"device" : "gpio-keys.4",
"code" : 139,
#include "container-connection.hpp"
#include "container-connection-transport.hpp"
#include "container-dbus-definitions.hpp"
+// TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager
+#include "fake-power-manager-dbus-definitions.hpp"
#include "dbus/connection.hpp"
+#include "dbus/exception.hpp"
#include "utils/scoped-daemon.hpp"
#include "utils/glib-loop.hpp"
#include "utils/latch.hpp"
ScopedDaemon mDaemon;
};
+class DbusNameSetter {
+public:
+ DbusNameSetter()
+ : mNameAcquired(false),
+ mPendingDisconnect(false)
+ {
+ }
+
+ void setName(const std::unique_ptr<DbusConnection>& conn, const std::string& name)
+ {
+ conn->setName(name,
+ std::bind(&DbusNameSetter::onNameAcquired, this),
+ std::bind(&DbusNameSetter::onDisconnect, this));
+
+ if(!waitForName()) {
+ throw dbus::DbusOperationException("Could not acquire name.");
+ }
+ }
+
+ bool waitForName()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameCondition.wait(lock, [this] {return mNameAcquired || mPendingDisconnect;});
+ return mNameAcquired;
+ }
+
+ void onNameAcquired()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameAcquired = true;
+ mNameCondition.notify_one();
+ }
+
+ void onDisconnect()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mPendingDisconnect = true;
+ mNameCondition.notify_one();
+ }
+
+private:
+ bool mNameAcquired;
+ bool mPendingDisconnect;
+ std::mutex mMutex;
+ std::condition_variable mNameCondition;
+};
+
} // namespace
BOOST_CHECK(signalEmitted.wait(EVENT_TIMEOUT));
}
+BOOST_AUTO_TEST_CASE(SignalDisplayOffApiTest)
+{
+ ScopedGlibLoop loop;
+ ScopedDbusDaemon dbus;
+
+ Latch displayOffCalled;
+ std::unique_ptr<ContainerConnection> connection;
+
+ BOOST_REQUIRE_NO_THROW(connection.reset(new ContainerConnection(dbus.acquireAddress(),
+ nullptr)));
+
+ DbusConnection::Pointer client = DbusConnection::create(dbus.acquireAddress());
+
+ auto callback = [&]() {
+ displayOffCalled.set();
+ };
+
+ connection->setDisplayOffCallback(callback);
+
+ client->emitSignal(fake_power_manager_api::OBJECT_PATH,
+ fake_power_manager_api::INTERFACE,
+ fake_power_manager_api::SIGNAL_DISPLAY_OFF,
+ nullptr);
+
+ // timeout should occur, since no name is set to client
+ BOOST_CHECK(!displayOffCalled.wait(EVENT_TIMEOUT));
+
+ DbusNameSetter setter;
+
+ setter.setName(client, fake_power_manager_api::BUS_NAME);
+
+ client->emitSignal(fake_power_manager_api::OBJECT_PATH,
+ fake_power_manager_api::INTERFACE,
+ fake_power_manager_api::SIGNAL_DISPLAY_OFF,
+ nullptr);
+
+ // now signal should be delivered correctly
+ BOOST_CHECK(displayOffCalled.wait(EVENT_TIMEOUT));
+}
+
BOOST_AUTO_TEST_SUITE_END()
#include "containers-manager.hpp"
#include "container-dbus-definitions.hpp"
+// TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager
+#include "fake-power-manager-dbus-definitions.hpp"
#include "exception.hpp"
#include "dbus/connection.hpp"
+#include "dbus/exception.hpp"
#include "utils/glib-loop.hpp"
#include "config/exception.hpp"
#include "utils/latch.hpp"
#include <string>
#include <algorithm>
#include <functional>
+#include <mutex>
+#include <condition_variable>
using namespace security_containers;
const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-dbus-daemon.conf";
const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-daemon.conf";
const std::string BUGGY_FOREGROUND_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-foreground-daemon.conf";
+const std::string BUGGY_DEFAULTID_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-default-daemon.conf";
const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/missing-daemon.conf";
const int EVENT_TIMEOUT = 5000;
const int TEST_DBUS_CONNECTION_CONTAINERS_COUNT = 3;
public:
std::vector<std::string> mReceivedSignalsSource;
- DbusAccessory(int id, Latch& signalEmittedLatch)
+ DbusAccessory(int id)
: mId(id),
mClient(DbusConnection::create(acquireAddress())),
- mSignalEmittedLatch(signalEmittedLatch)
+ mNameAcquired(false),
+ mPendingDisconnect(false)
{
}
- void signalSubscribe()
+ void setName(const std::string& name)
+ {
+ mClient->setName(name,
+ std::bind(&DbusAccessory::onNameAcquired, this),
+ std::bind(&DbusAccessory::onDisconnect, this));
+
+ if(!waitForName()) {
+ mClient.reset();
+ throw dbus::DbusOperationException("Could not acquire name.");
+ }
+ }
+
+ bool waitForName()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameCondition.wait(lock, [this] {return mNameAcquired || mPendingDisconnect;});
+ return mNameAcquired;
+ }
+
+ void onNameAcquired()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameAcquired = true;
+ mNameCondition.notify_one();
+ }
+
+ void onDisconnect()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mPendingDisconnect = true;
+ mNameCondition.notify_one();
+ }
+
+ void signalSubscribe(Latch& referenceLatch)
{
using namespace std::placeholders;
- mClient->signalSubscribe(std::bind(&DbusAccessory::handler, this, _1, _2, _3, _4, _5),
+ mClient->signalSubscribe(std::bind(&DbusAccessory::handler,
+ this, std::ref(referenceLatch), _1, _2, _3, _4, _5),
api::BUS_NAME);
}
+ void emitSignal(const std::string& objectPath,
+ const std::string& interface,
+ const std::string& name,
+ GVariant* parameters)
+ {
+ mClient->emitSignal(objectPath, interface, name, parameters);
+ }
+
void callMethod()
{
GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSGAE.c_str());
private:
const int mId;
DbusConnection::Pointer mClient;
- Latch& mSignalEmittedLatch;
+ bool mNameAcquired;
+ bool mPendingDisconnect;
+ std::mutex mMutex;
+ std::condition_variable mNameCondition;
std::string acquireAddress() const
{
"-dbus/dbus/system_bus_socket";
}
- void handler(const std::string& /*senderBusName*/,
+ void handler(Latch& referenceLatch,
+ const std::string& /*senderBusName*/,
const std::string& objectPath,
const std::string& interface,
const std::string& signalName,
g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
mReceivedSignalsSource.push_back(container);
if (application == TEST_APP_NAME && message == TEST_MESSGAE) {
- mSignalEmittedLatch.set();
+ referenceLatch.set();
}
}
}
BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console2");
}
+BOOST_AUTO_TEST_CASE(BuggyDefaultTest)
+{
+ BOOST_REQUIRE_THROW(ContainersManager cm(BUGGY_DEFAULTID_CONFIG_PATH),
+ ContainerOperationException);
+}
+
BOOST_AUTO_TEST_CASE(StopAllTest)
{
ContainersManager cm(TEST_CONFIG_PATH);
std::vector< std::unique_ptr<DbusAccessory> > dbuses;
for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
- dbuses.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i, signalReceivedLatch)));
+ dbuses.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
}
for (auto& dbus : dbuses) {
- dbus->signalSubscribe();
+ dbus->signalSubscribe(signalReceivedLatch);
}
for (auto& dbus : dbuses) {
dbus->callMethod();
dbuses.clear();
}
+BOOST_AUTO_TEST_CASE(DisplayOffTest)
+{
+ ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+ BOOST_REQUIRE_NO_THROW(cm.startAll());
+
+ std::vector<std::unique_ptr<DbusAccessory>> clients;
+ for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
+ clients.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
+ }
+
+ for (auto& client : clients) {
+ client->setName(fake_power_manager_api::BUS_NAME);
+ }
+
+ std::mutex Mutex;
+ std::unique_lock<std::mutex> Lock(Mutex);
+ std::condition_variable Condition;
+ auto cond = [&cm]() -> bool {
+ return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1";
+ };
+
+ for (auto& client : clients) {
+ // TEST SWITCHING TO DEFAULT CONTAINER
+ // focus non-default container
+ BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3"));
+
+ // emit signal from dbus connection
+ BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
+ fake_power_manager_api::INTERFACE,
+ fake_power_manager_api::SIGNAL_DISPLAY_OFF,
+ nullptr));
+
+ // check if default container has focus
+ BOOST_CHECK(Condition.wait_for(Lock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()