#define final
#define override
#define thread_local __thread // use GCC extension instead of C++11
+#define steady_clock monotonic_clock
#endif // GCC_VERSION < 40700
#endif // GCC_VERSION
"zoneTemplatePath" : "/etc/vasum/templates/template.conf",
"zoneNewConfigPrefix" : "/var/lib/vasum",
"runMountPointPrefix" : "/var/run/zones",
- "foregroundId" : "",
"defaultId" : "",
"lxcTemplatePrefix" : "/etc/vasum/lxc-templates",
"inputConfig" : {"enabled" : true,
const std::string OBJECT_PATH = "/org/tizen/vasum/host";
const std::string INTERFACE = "org.tizen.vasum.host.manager";
-const std::string ERROR_ZONE_STOPPED = "org.tizen.vasum.host.Error.ZonesStopped";
+const std::string ERROR_ZONE_NOT_RUNNING = "org.tizen.vasum.host.Error.ZonesNotRunning";
const std::string METHOD_GET_ZONE_DBUSES = "GetZoneDbuses";
const std::string METHOD_GET_ZONE_ID_LIST = "GetZoneIds";
std::string dbPath;
/**
- * An ID of a currently focused/foreground zone.
- */
- std::string foregroundId;
-
- /**
- * An ID of default zone.
- */
- std::string defaultId;
-
- /**
* A path where the zones mount points reside.
*/
std::string zonesPath;
CONFIG_REGISTER
(
dbPath,
- foregroundId,
- defaultId,
zonesPath,
zoneImagePath,
zoneTemplatePath,
*/
std::vector<std::string> zoneConfigs;
+ /**
+ * An ID of default zone.
+ */
+ std::string defaultId;
+
CONFIG_REGISTER
(
- zoneConfigs
+ zoneConfigs,
+ defaultId
)
};
createZone(utils::createFilePath(mConfig.zoneNewConfigPrefix, zoneConfig));
}
- // check if default zone exists, throw ZoneOperationException if not found
- if (!mConfig.defaultId.empty() && mZones.find(mConfig.defaultId) == mZones.end()) {
- LOGE("Provided default zone ID " << mConfig.defaultId << " is invalid.");
- throw ZoneOperationException("Provided default zone ID " + mConfig.defaultId +
- " is invalid.");
- }
+ updateDefaultId();
LOGD("ZonesManager object instantiated");
config::saveToKVStore(mConfig.dbPath, mDynamicConfig, DB_PREFIX);
}
+void ZonesManager::updateDefaultId()
+{
+ // TODO add an api to change defaultId
+ if (mZones.empty() && mDynamicConfig.defaultId.empty()) {
+ LOGT("Keep empty defaultId");
+ return;
+ }
+ if (mZones.find(mDynamicConfig.defaultId) != mZones.end()) {
+ LOGT("Keep " << mDynamicConfig.defaultId << " as defaultId");
+ return;
+ }
+
+ // update
+ if (mZones.empty()) {
+ mDynamicConfig.defaultId.clear();
+ LOGD("DefaultId cleared");
+ } else {
+ mDynamicConfig.defaultId = mZones.begin()->first;
+ LOGD("DefaultId changed to " << mDynamicConfig.defaultId);
+ }
+ saveDynamicConfig();
+}
+
void ZonesManager::createZone(const std::string& zoneConfigPath)
{
LOGT("Creating Zone " << zoneConfigPath);
throw ZoneOperationException("No such zone");
}
- // TODO give back the focus
it->second->setDestroyOnExit();
mZones.erase(it);
// update dynamic config
remove(mDynamicConfig.zoneConfigs, getConfigName(zoneId));
saveDynamicConfig();
+ updateDefaultId();
+
+ refocus();
}
void ZonesManager::focus(const std::string& zoneId)
{
Lock lock(mMutex);
+ if (zoneId == mActiveZoneId) {
+ // nothing to do
+ return;
+ }
+
+ if (zoneId.empty()) {
+ LOGI("Focus to: host");
+ // give back the focus to the host
+ // TODO switch to host vt
+ mActiveZoneId.clear();
+ return;
+ }
+
+ LOGI("Focus to: " << zoneId);
+
/* try to access the object first to throw immediately if it doesn't exist */
- ZoneMap::mapped_type& foregroundZone = mZones.at(zoneId);
+ auto& foregroundZone = mZones.at(zoneId);
+
+ assert(foregroundZone->isRunning());
if (!foregroundZone->activateVT()) {
LOGE("Failed to activate zones VT. Aborting focus.");
}
for (auto& zone : mZones) {
- LOGD(zone.second->getId() << ": being sent to background");
- zone.second->goBackground();
+ if (zone.first == zoneId) {
+ LOGD(zone.first << ": being sent to foreground");
+ zone.second->goForeground();
+ } else {
+ LOGD(zone.first << ": being sent to background");
+ zone.second->goBackground();
+ }
+ }
+ mActiveZoneId = zoneId;
+}
+
+void ZonesManager::refocus()
+{
+ Lock lock(mMutex);
+
+ // check if refocus is required
+ auto oldIter = mZones.find(mActiveZoneId);
+ if (oldIter != mZones.end() && oldIter->second->isRunning()) {
+ return;
+ }
+
+ // try to refocus to defaultId
+ auto iter = mZones.find(mDynamicConfig.defaultId);
+ if (iter != mZones.end() && iter->second->isRunning()) {
+ // focus to default
+ focus(iter->first);
+ } else {
+ // focus to any running or to host if not found
+ auto zoneIsRunning = [](const ZoneMap::value_type& pair) {
+ return pair.second->isRunning();
+ };
+ auto iter = std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
+ if (iter == mZones.end()) {
+ focus(std::string());
+ } else {
+ focus(iter->first);
+ }
}
- mConfig.foregroundId = foregroundZone->getId();
- LOGD(mConfig.foregroundId << ": being sent to foreground");
- foregroundZone->goForeground();
}
void ZonesManager::startAll()
Lock lock(mMutex);
- bool isForegroundFound = false;
-
for (auto& zone : mZones) {
zone.second->start();
-
- if (zone.first == mConfig.foregroundId) {
- isForegroundFound = true;
- LOGI(zone.second->getId() << ": set as the foreground zone");
- zone.second->goForeground();
- }
}
- if (!isForegroundFound) {
- auto foregroundIterator = std::min_element(mZones.begin(), mZones.end(),
- [](ZoneMap::value_type &c1, ZoneMap::value_type &c2) {
- return c1.second->getPrivilege() < c2.second->getPrivilege();
- });
-
- if (foregroundIterator != mZones.end()) {
- mConfig.foregroundId = foregroundIterator->second->getId();
- LOGI(mConfig.foregroundId << ": no foreground zone configured, setting one with highest priority");
- foregroundIterator->second->goForeground();
- }
- }
+ refocus();
}
void ZonesManager::stopAll()
for (auto& zone : mZones) {
zone.second->stop();
}
+
+ refocus();
}
bool ZonesManager::isPaused(const std::string& zoneId)
{
Lock lock(mMutex);
- for (auto& zone : mZones) {
- if (zone.first == mConfig.foregroundId &&
- zone.second->isRunning()) {
- return zone.first;
- }
+ if (!mActiveZoneId.empty() && !mZones.at(mActiveZoneId)->isRunning()) {
+ // Can zone change its state by itself?
+ // Maybe when it is shut down by itself? TODO check it
+ LOGW("Active zone " << mActiveZoneId << " is not running any more!");
+ assert(false);
+ return std::string();
}
- return std::string();
+
+ return mActiveZoneId;
}
std::string ZonesManager::getNextToForegroundZoneId()
}
for (auto it = mZones.begin(); it != mZones.end(); ++it) {
- if (it->first == mConfig.foregroundId &&
- it->second->isRunning()) {
+ if (it->first == mActiveZoneId && it->second->isRunning()) {
auto nextIt = std::next(it);
if (nextIt != mZones.end()) {
return nextIt->first;
}
}
}
- return mZones.begin()->first;
+ return mZones.begin()->first;//TODO fix - check isRunning
}
void ZonesManager::switchingSequenceMonitorNotify()
{
LOGI("switchingSequenceMonitorNotify() called");
- auto nextZoneId = getNextToForegroundZoneId();
+ std::string nextZoneId = getNextToForegroundZoneId();
if (!nextZoneId.empty()) {
focus(nextZoneId);
// get config of currently set zone and switch if switchToDefaultAfterTimeout is true
Lock lock(mMutex);
- const std::string activeZoneName = getRunningForegroundZoneId();
- const auto& activeZone = mZones.find(activeZoneName);
+ auto activeZone = mZones.find(mActiveZoneId);
if (activeZone != mZones.end() &&
- activeZone->second->isSwitchToDefaultAfterTimeoutAllowed()) {
- LOGI("Switching to default zone " << mConfig.defaultId);
- focus(mConfig.defaultId);
+ activeZone->second->isSwitchToDefaultAfterTimeoutAllowed() &&
+ !mDynamicConfig.defaultId.empty() &&
+ mZones.at(mDynamicConfig.defaultId)->isRunning()) {
+
+ LOGI("Switching to default zone " << mDynamicConfig.defaultId);
+ focus(mDynamicConfig.defaultId);
}
}
Lock lock(mMutex);
- if (!mConfig.foregroundId.empty() && mZones[mConfig.foregroundId]->isRunning()){
- result->set(g_variant_new("(s)", mConfig.foregroundId.c_str()));
- } else {
- result->set(g_variant_new("(s)", ""));
- }
+ std::string id = getRunningForegroundZoneId();
+
+ result->set(g_variant_new("(s)", id.c_str()));
}
void ZonesManager::handleGetZoneInfoCall(const std::string& id,
}
const auto& zone = mZones[id];
const char* state;
- //TODO: Use the lookup map.
+
if (zone->isRunning()) {
state = "RUNNING";
} else if (zone->isStopped()) {
return;
}
- if (zone->second->isStopped()){
- LOGE("Could not activate a stopped zone");
- result->setError(api::host::ERROR_ZONE_STOPPED,
- "Could not activate a stopped zone");
+ if (!zone->second->isRunning()){
+ LOGE("Could not activate stopped or paused zone");
+ result->setError(api::host::ERROR_ZONE_NOT_RUNNING,
+ "Could not activate stopped or paused zone");
return;
}
mDynamicConfig.zoneConfigs.push_back(newConfigName);
saveDynamicConfig();
+ updateDefaultId();
result->setVoid();
}
auto shutdown = [id, result, this] {
try {
- mZones[id]->stop();
+ ZoneMap::mapped_type zone;
+ {
+ Lock lock(mMutex);
+ zone = mZones.at(id);
+ }
+ zone->stop();
+ refocus();
+ result->setVoid();
} catch (ZoneOperationException& e) {
LOGE("Error during zone shutdown: " << e.what());
result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
return;
}
- result->setVoid();
};
mWorker->addTask(shutdown);
LOGT("Lock zone");
try {
zone.suspend();
+ refocus();
} catch (ZoneOperationException& e) {
LOGE(e.what());
result->setError(api::ERROR_INTERNAL, e.what());
// to hold InputMonitor pointer to monitor if zone switching sequence is recognized
std::unique_ptr<InputMonitor> mSwitchingSequenceMonitor;
std::unique_ptr<ProxyCallPolicy> mProxyCallPolicy;
- typedef std::unordered_map<std::string, std::shared_ptr<Zone>> ZoneMap;
+ typedef std::unordered_map<std::string, std::shared_ptr<Zone>> ZoneMap;//TODO should keep order of insertions
ZoneMap mZones; // map of zones, id is the key
+ std::string mActiveZoneId;
bool mDetachOnExit;
void saveDynamicConfig();
+ void updateDefaultId();
+ void refocus();
void switchingSequenceMonitorNotify();
void generateNewConfig(const std::string& id,
const std::string& templatePath,
"zoneConfigs" : ["zones/console1-dbus.conf",
"zones/console2-dbus.conf",
"zones/console3-dbus.conf"],
- "foregroundId" : "ut-zones-manager-console1-dbus",
"defaultId" : "ut-zones-manager-console1-dbus",
"zonesPath" : "/tmp/ut-zones",
"zoneImagePath" : "",
${CMAKE_BINARY_DIR}/ut-zones-manager/test-daemon.conf @ONLY)
CONFIGURE_FILE(ut-zones-manager/buggy-daemon.conf.in
${CMAKE_BINARY_DIR}/ut-zones-manager/buggy-daemon.conf @ONLY)
-CONFIGURE_FILE(ut-zones-manager/buggy-default-daemon.conf.in
- ${CMAKE_BINARY_DIR}/ut-zones-manager/buggy-default-daemon.conf @ONLY)
-CONFIGURE_FILE(ut-zones-manager/buggy-foreground-daemon.conf.in
- ${CMAKE_BINARY_DIR}/ut-zones-manager/buggy-foreground-daemon.conf @ONLY)
CONFIGURE_FILE(ut-zones-manager/test-dbus-daemon.conf.in
${CMAKE_BINARY_DIR}/ut-zones-manager/test-dbus-daemon.conf @ONLY)
CONFIGURE_FILE(ut-zones-manager/empty-dbus-daemon.conf.in
"zoneTemplatePath" : "no_need_for_templates_in_this_test",
"zoneNewConfigPrefix" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-server/",
"runMountPointPrefix" : "",
- "foregroundId" : "ut-server-zone1",
"defaultId" : "ut-server-zone1",
"lxcTemplatePrefix" : "@VSM_TEST_LXC_TEMPLATES_INSTALL_DIR@",
"inputConfig" : {"enabled" : false,
"zoneTemplatePath" : "no_need_for_templates_in_this_test",
"zoneNewConfigPrefix" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-server/",
"runMountPointPrefix" : "",
- "foregroundId" : "ut-server-zone1",
"defaultId" : "ut-server-zone1",
"lxcTemplatePrefix" : "@VSM_TEST_LXC_TEMPLATES_INSTALL_DIR@",
"inputConfig" : {"enabled" : false,
"dbPath" : "/tmp/ut-zones/vasum.db",
"zoneConfigs" : ["zones/console1.conf", "missing/file/path/missing.conf", "zones/console3.conf"],
"runMountPointPrefix" : "",
- "foregroundId" : "ut-zones-manager-console1",
"defaultId" : "ut-zones-manager-console1",
"zonesPath" : "/tmp/ut-zones",
"zoneImagePath" : "",
+++ /dev/null
-{
- "dbPath" : "/tmp/ut-zones/vasum.db",
- "zoneConfigs" : ["zones/console1.conf", "zones/console2.conf", "zones/console3.conf"],
- "runMountPointPrefix" : "",
- "foregroundId" : "ut-zones-manager-console1",
- "defaultId" : "in_no_way_there_is_a_valid_id_here",
- "zonesPath" : "/tmp/ut-zones",
- "zoneImagePath" : "",
- "zoneTemplatePath" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/templates/template.conf",
- "zoneNewConfigPrefix" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/",
- "lxcTemplatePrefix" : "@VSM_TEST_LXC_TEMPLATES_INSTALL_DIR@",
- "inputConfig" : {"enabled" : false,
- "device" : "/dev/doesnotexist",
- "code" : 139,
- "numberOfEvents" : 2,
- "timeWindowMs" : 500},
- "proxyCallRules" : []
-}
+++ /dev/null
-{
- "dbPath" : "/tmp/ut-zones/vasum.db",
- "zoneConfigs" : ["zones/console1.conf", "zones/console2.conf", "zones/console3.conf"],
- "runMountPointPrefix" : "",
- "foregroundId" : "this_id_does_not_exist",
- "defaultId" : "ut-zones-manager-console1",
- "zonesPath" : "/tmp/ut-zones",
- "zoneImagePath" : "",
- "zoneTemplatePath" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/templates/template.conf",
- "zoneNewConfigPrefix" : "@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/",
- "lxcTemplatePrefix" : "@VSM_TEST_LXC_TEMPLATES_INSTALL_DIR@",
- "inputConfig" : {"enabled" : false,
- "device" : "/dev/doesnotexist",
- "code" : 139,
- "numberOfEvents" : 2,
- "timeWindowMs" : 500},
- "proxyCallRules" : []
-}
{
"dbPath" : "/tmp/ut-zones/vasum.db",
"zoneConfigs" : [],
- "foregroundId" : "",
"defaultId" : "",
"zonesPath" : "/tmp/ut-zones",
"zoneImagePath" : "",
"dbPath" : "/tmp/ut-zones/vasum.db",
"zoneConfigs" : ["zones/console1.conf", "zones/console2.conf", "zones/console3.conf"],
"runMountPointPrefix" : "",
- "foregroundId" : "ut-zones-manager-console1",
"defaultId" : "ut-zones-manager-console1",
"zonesPath" : "/tmp/ut-zones",
"zoneImagePath" : "",
"zoneConfigs" : ["zones/console1-dbus.conf",
"zones/console2-dbus.conf",
"zones/console3-dbus.conf"],
- "foregroundId" : "ut-zones-manager-console1-dbus",
"defaultId" : "ut-zones-manager-console1-dbus",
"zonesPath" : "/tmp/ut-zones",
"zoneImagePath" : "",
const std::string TEST_DBUS_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/test-dbus-daemon.conf";
const std::string EMPTY_DBUS_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/empty-dbus-daemon.conf";
const std::string BUGGY_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/buggy-daemon.conf";
-const std::string BUGGY_FOREGROUND_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/buggy-foreground-daemon.conf";
-const std::string BUGGY_DEFAULTID_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/buggy-default-daemon.conf";
const std::string TEST_ZONE_CONF_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zones-manager/zones/";
const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/missing-daemon.conf";
const int EVENT_TIMEOUT = 5000;
+const int UNEXPECTED_EVENT_TIMEOUT = EVENT_TIMEOUT / 5;
const int TEST_DBUS_CONNECTION_ZONES_COUNT = 3;
const std::string PREFIX_CONSOLE_NAME = "ut-zones-manager-console";
const std::string TEST_APP_NAME = "testapp";
std::mutex mMutex;
std::condition_variable mNameCondition;
- bool isHost() const {
+ bool isHost() const
+ {
return mId == HOST_ID;
}
}
};
-std::function<bool(const std::exception&)> expectedMessage(const std::string& message) {
+std::function<bool(const std::exception&)> expectedMessage(const std::string& message)
+{
return [=](const std::exception& e) {
return e.what() == message;
};
}
+template<class Predicate>
+bool spinWaitFor(int timeoutMs, Predicate pred)
+{
+ auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
+ while (!pred()) {
+ if (std::chrono::steady_clock::now() >= until) {
+ return false;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ return true;
+}
+
struct Fixture {
vasum::utils::ScopedGlibLoop mLoop;
BOOST_CHECK(cm.getRunningForegroundZoneId() == "ut-zones-manager-console1");
}
-BOOST_AUTO_TEST_CASE(BuggyForegroundTest)
-{
- ZonesManager cm(BUGGY_FOREGROUND_CONFIG_PATH);
- cm.startAll();
- BOOST_CHECK(cm.getRunningForegroundZoneId() == "ut-zones-manager-console2");
-}
-
-BOOST_AUTO_TEST_CASE(BuggyDefaultTest)
-{
- BOOST_REQUIRE_THROW(ZonesManager cm(BUGGY_DEFAULTID_CONFIG_PATH),
- ZoneOperationException);
-}
-
BOOST_AUTO_TEST_CASE(StopAllTest)
{
ZonesManager cm(TEST_CONFIG_PATH);
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.getRunningForegroundZoneId() == "ut-zones-manager-console1-dbus";
};
nullptr);
// check if default zone has focus
- BOOST_CHECK(Condition.wait_for(Lock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
+ BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, cond));
}
}
client->setName(fake_power_manager_api::BUS_NAME);
}
- std::mutex condMutex;
- std::unique_lock<std::mutex> condLock(condMutex);
- std::condition_variable condition;
auto cond = [&cm]() -> bool {
return cm.getRunningForegroundZoneId() == "ut-zones-manager-console1-dbus";
};
nullptr);
// check if default zone has focus
- BOOST_CHECK(condition.wait_for(condLock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
+ BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, cond));
// focus non-default zone with disabled switching
cm.focus("ut-zones-manager-console2-dbus");
nullptr);
// now default zone should not be focused
- BOOST_CHECK(!condition.wait_for(condLock, std::chrono::milliseconds(EVENT_TIMEOUT), cond));
+ BOOST_CHECK(!spinWaitFor(UNEXPECTED_EVENT_TIMEOUT, cond));
}
}
cm.startAll();
+ BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1);
+ cm.focus(zone3);
BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone3);
// destroy zone2
// destroy zone3
dbus.callAsyncMethodDestroyZone(zone3, resultCallback);
BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT));
- //BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1);//TODO fix it
+ BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1);
// destroy zone1
dbus.callAsyncMethodDestroyZone(zone1, resultCallback);
"ut-zones-manager-console2-dbus",
"ut-zones-manager-console3-dbus"};
- for (std::string& zoneId: zoneIds){
+ for (const std::string& zoneId: zoneIds){
dbus.callMethodLockZone(zoneId);
BOOST_CHECK(cm.isPaused(zoneId));
dbus.callMethodUnlockZone(zoneId);