summaryrefslogtreecommitdiffstats
path: root/src/modules/Unity/Application/application_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/Unity/Application/application_manager.cpp')
-rw-r--r--src/modules/Unity/Application/application_manager.cpp302
1 files changed, 53 insertions, 249 deletions
diff --git a/src/modules/Unity/Application/application_manager.cpp b/src/modules/Unity/Application/application_manager.cpp
index ea316e7..0624552 100644
--- a/src/modules/Unity/Application/application_manager.cpp
+++ b/src/modules/Unity/Application/application_manager.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013,2014 Canonical, Ltd.
+ * Copyright (C) 2013-2015 Canonical, Ltd.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3, as published by
@@ -96,11 +96,13 @@ void connectToTaskController(ApplicationManager *manager, TaskController *contro
manager, &ApplicationManager::onProcessStarting);
QObject::connect(controller, &TaskController::processStopped,
manager, &ApplicationManager::onProcessStopped);
+ QObject::connect(controller, &TaskController::processSuspended,
+ manager, &ApplicationManager::onProcessSuspended);
QObject::connect(controller, &TaskController::processFailed,
manager, &ApplicationManager::onProcessFailed);
- QObject::connect(controller, &TaskController::requestFocus,
+ QObject::connect(controller, &TaskController::focusRequested,
manager, &ApplicationManager::onFocusRequested);
- QObject::connect(controller, &TaskController::requestResume,
+ QObject::connect(controller, &TaskController::resumeRequested,
manager, &ApplicationManager::onResumeRequested);
}
@@ -180,16 +182,12 @@ ApplicationManager::ApplicationManager(
: ApplicationManagerInterface(parent)
, m_mirServer(mirServer)
, m_focusedApplication(nullptr)
- , m_mainStageApplication(nullptr)
- , m_sideStageApplication(nullptr)
, m_dbusWindowStack(new DBusWindowStack(this))
, m_taskController(taskController)
, m_desktopFileReaderFactory(desktopFileReaderFactory)
, m_procInfo(procInfo)
, m_sharedWakelock(sharedWakelock)
, m_settings(settings)
- , m_suspended(false)
- , m_forceDashActive(false)
{
qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
setObjectName("qtmir::ApplicationManager");
@@ -198,7 +196,7 @@ ApplicationManager::ApplicationManager(
m_roleNames.insert(RoleFullscreen, "fullscreen");
if (settings.data()) {
- m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
+ Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
connect(m_settings.data(), &Settings::changed, this, &ApplicationManager::onSettingsChanged);
}
}
@@ -289,98 +287,6 @@ QString ApplicationManager::focusedApplicationId() const
}
}
-bool ApplicationManager::suspended() const
-{
- return m_suspended;
-}
-
-void ApplicationManager::setSuspended(bool suspended)
-{
- if (suspended == m_suspended) {
- return;
- }
- m_suspended = suspended;
- Q_EMIT suspendedChanged();
-
- if (m_suspended) {
- suspendApplication(m_mainStageApplication);
- suspendApplication(m_sideStageApplication);
- if (m_focusedApplication) {
- m_focusedApplication->setFocused(false);
- m_dbusWindowStack->FocusedWindowChanged(0, QString(), 0);
- }
- } else {
- resumeApplication(m_mainStageApplication);
- resumeApplication(m_sideStageApplication);
- if (m_focusedApplication) {
- m_focusedApplication->setFocused(true);
- m_dbusWindowStack->FocusedWindowChanged(0, m_focusedApplication->appId(), m_focusedApplication->stage());
- }
- }
-}
-
-bool ApplicationManager::forceDashActive() const
-{
- return m_forceDashActive;
-}
-
-void ApplicationManager::setForceDashActive(bool forceDashActive)
-{
- if (m_forceDashActive == forceDashActive) {
- return;
- }
-
- m_forceDashActive = forceDashActive;
- Q_EMIT forceDashActiveChanged();
-
- Application *dashApp = findApplication("unity8-dash");
- if (!dashApp) {
- qCWarning(QTMIR_APPLICATIONS) << "Dash doesn't seem to be running... Ignoring.";
- return;
- }
-
- if (m_forceDashActive && dashApp->state() != Application::Running) {
- resumeApplication(dashApp);
- } else if (!m_forceDashActive && dashApp->state() == Application::Running
- && m_mainStageApplication != dashApp
- && m_sideStageApplication != dashApp) {
- suspendApplication(dashApp);
- }
-}
-
-bool ApplicationManager::suspendApplication(Application *application)
-{
- if (application == nullptr)
- return false;
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId();
-
- // Present in exceptions list, explicitly release wakelock and return. There's no need to keep the wakelock
- // as the process is never suspended and thus has no cleanup to perform when (for example) the display is blanked
- if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty()) {
- m_sharedWakelock->release(application);
- return false;
- }
-
- if (m_forceDashActive && application->appId() == "unity8-dash") {
- return false;
- }
-
- if (application->state() == Application::Running)
- application->setState(Application::Suspended);
-
- return true;
-}
-
-void ApplicationManager::resumeApplication(Application *application)
-{
- if (application == nullptr)
- return;
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::resumeApplication - appId=" << application->appId();
-
- if (application->state() == Application::Suspended || application->state() == Application::Stopped)
- application->setState(Application::Running);
-}
-
bool ApplicationManager::focusApplication(const QString &inputAppId)
{
const QString appId = toShortAppIdIfPossible(inputAppId);
@@ -392,27 +298,8 @@ bool ApplicationManager::focusApplication(const QString &inputAppId)
return false;
}
- resumeApplication(application);
-
- // set state of previously focused app to suspended
if (m_focusedApplication) {
m_focusedApplication->setFocused(false);
- Application *lastApplication = applicationForStage(application->stage());
- if (lastApplication != application) {
- suspendApplication(lastApplication);
- }
- }
-
- if (application->stage() == Application::MainStage) {
- m_mainStageApplication = application;
- } else {
- m_sideStageApplication = application;
- }
-
- if (!m_suspended) {
- resumeApplication(application); // in case unfocusCurrentApplication() was last called
- } else {
- suspendApplication(application); // Make sure we also have this one suspended if everything is suspended
}
m_focusedApplication = application;
@@ -422,9 +309,6 @@ bool ApplicationManager::focusApplication(const QString &inputAppId)
Q_EMIT focusedApplicationIdChanged();
m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());
- // FIXME(dandrader): lying here. The operation is async. So we will only know whether
- // the focusing was successful once the server replies. Maybe the API in unity-api should
- // reflect that?
return true;
}
@@ -432,9 +316,6 @@ void ApplicationManager::unfocusCurrentApplication()
{
qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication";
- suspendApplication(m_sideStageApplication);
- suspendApplication(m_mainStageApplication);
-
m_focusedApplication = nullptr;
Q_EMIT focusedApplicationIdChanged();
}
@@ -484,10 +365,8 @@ Application *ApplicationManager::startApplication(const QString &inputAppId, Exe
application->setArguments(arguments);
} else {
application = new Application(
- m_taskController,
m_sharedWakelock,
m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
- Application::Starting,
arguments,
this);
@@ -514,10 +393,8 @@ void ApplicationManager::onProcessStarting(const QString &appId)
Application *application = findApplication(appId);
if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
application = new Application(
- m_taskController,
m_sharedWakelock,
m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
- Application::Starting,
QStringList(),
this);
@@ -530,17 +407,17 @@ void ApplicationManager::onProcessStarting(const QString &appId)
Q_EMIT focusRequested(appId);
}
else {
- // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
- // application and focus it immediately (as user expects app to still be running).
if (application->state() == Application::Stopped) {
+ // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
+ // application and focus it immediately (as user expects app to still be running).
qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally";
- application->setState(Application::Starting);
Q_EMIT focusRequested(appId);
} else {
qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId"
<< appId;
}
}
+ application->setProcessState(Application::ProcessRunning);
}
/**
@@ -559,14 +436,7 @@ bool ApplicationManager::stopApplication(const QString &inputAppId)
return false;
}
- if (application == m_focusedApplication) {
- // unfocus, and let shell decide what next to focus
- m_focusedApplication = nullptr;
- Q_EMIT focusedApplicationIdChanged();
- }
-
remove(application);
- m_dbusWindowStack->WindowDestroyed(0, appId);
bool result = m_taskController->stop(application->longAppId());
@@ -583,10 +453,7 @@ bool ApplicationManager::stopApplication(const QString &inputAppId)
void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)
{
- /* Applications fail if they fail to launch, crash or are killed. If failed to start, must
- * immediately remove from list of applications. If crash or kill, instead we set flag on the
- * Application to indicate it can be resumed.
- */
+ // Applications fail if they fail to launch, crash or are killed.
qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId << "duringStartup=" << duringStartup;
@@ -598,20 +465,8 @@ void ApplicationManager::onProcessFailed(const QString &appId, const bool during
}
Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
- if (application->state() == Application::Starting) {
- if (application == m_focusedApplication) {
- m_focusedApplication = nullptr;
- Q_EMIT focusedApplicationIdChanged();
- }
- remove(application);
- m_dbusWindowStack->WindowDestroyed(0, application->appId());
- delete application;
- } else {
- // We need to set flags on the Application to say the app can be resumed, and thus should not be removed
- // from the list by onProcessStopped.
- application->setCanBeResumed(true);
- application->setPid(0);
- }
+ application->setProcessState(Application::ProcessStopped);
+ application->setPid(0);
}
void ApplicationManager::onProcessStopped(const QString &appId)
@@ -626,29 +481,15 @@ void ApplicationManager::onProcessStopped(const QString &appId)
return;
}
- // if shell did not stop the application, but ubuntu-app-launch says it died, we assume the process has been
- // killed, so it can be respawned later. Only exception is if that application is focused or running
- // as then it most likely crashed. Update this logic when ubuntu-app-launch gives some failure info.
- bool removeApplication = true;
-
- if (application == m_focusedApplication) {
- // Very bad case where focused application dies. Remove from list. Should give error message
- m_focusedApplication = nullptr;
- Q_EMIT focusedApplicationIdChanged();
- }
-
- // The following scenario is the only time that we do NOT remove the application from the app list:
- if ((application->state() == Application::Suspended || application->state() == Application::Stopped)
- && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application
- && application->canBeResumed()) {
- removeApplication = false;
- }
+ application->setProcessState(Application::ProcessStopped);
+ application->setPid(0);
+}
- if (removeApplication) {
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - removing appId=" << appId;
- remove(application);
- m_dbusWindowStack->WindowDestroyed(0, application->appId());
- delete application;
+void ApplicationManager::onProcessSuspended(const QString &appId)
+{
+ Application *application = findApplication(appId);
+ if (application) {
+ application->setProcessState(Application::ProcessSuspended);
}
}
@@ -670,10 +511,10 @@ void ApplicationManager::onResumeRequested(const QString& appId)
return;
}
- // If app Stopped, trust that ubuntu-app-launch respawns it itself, and AppManager will
- // be notified of that through the onProcessStartReportReceived slot. Else resume.
+ // We interpret this as a focus request for a suspended app.
+ // Shell will have this app resumed if it complies with the focus request
if (application->state() == Application::Suspended) {
- application->setState(Application::Running);
+ Q_EMIT focusRequested(appId);
}
}
@@ -693,7 +534,7 @@ void ApplicationManager::onAppDataChanged(const int role)
void ApplicationManager::onSettingsChanged(const QString &key)
{
if (key == "lifecycleExemptAppids") {
- m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
+ Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
}
}
@@ -732,7 +573,6 @@ void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized)
if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {
authorized = true;
- m_hiddenPIDs << pid;
return;
}
@@ -792,15 +632,12 @@ void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized)
QStringList arguments(info->asStringList());
application = new Application(
- m_taskController,
m_sharedWakelock,
desktopData,
- Application::Starting,
arguments,
this);
application->setPid(pid);
application->setStage(stage);
- application->setCanBeResumed(false);
add(application);
authorized = true;
}
@@ -812,51 +649,10 @@ void ApplicationManager::onSessionStarting(std::shared_ptr<ms::Session> const& s
void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session)
{
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping - sessionName=" << session->name().c_str();
-
- // in case application closed not by hand of shell, check again here:
Application* application = findApplicationWithSession(session);
if (application) {
- /* Can remove the application from the running apps list immediately in these curcumstances:
- * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do
- * it here)
- * 2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume
- * it crashed on startup, and thus cannot be resumed - so remove it.
- * 3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the
- * application disconnects, it either crashed or stopped itself. Either case, remove it.
- */
- if (!application->canBeResumed()
- || application->state() == Application::Starting
- || application->state() == Application::Running) {
- m_dbusWindowStack->WindowDestroyed(0, application->appId());
- remove(application);
-
- // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup
- QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/"));
- QDir dir(path);
- QStringList apps = dir.entryList();
- for (int i = 0; i < apps.size(); i++) {
- if (apps.at(i).contains(application->appId())) {
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping appId=" << apps.at(i) << " Wiping QML Cache";
- dir.cd(apps.at(i));
- dir.removeRecursively();
- break;
- }
- }
-
- delete application;
-
- if (application == m_focusedApplication) {
- m_focusedApplication = nullptr;
- Q_EMIT focusedApplicationIdChanged();
- }
- } else {
- // otherwise, we do not have enough information to make any changes to the model, so await events from
- // upstart to go further, but set the app state
- application->setState(Application::Stopped);
- }
+ m_dbusWindowStack->WindowDestroyed(0, application->appId());
}
- m_hiddenPIDs.removeOne(session->process_id());
}
void ApplicationManager::onSessionCreatedSurface(ms::Session const* session,
@@ -866,12 +662,8 @@ void ApplicationManager::onSessionCreatedSurface(ms::Session const* session,
Q_UNUSED(surface);
Application* application = findApplicationWithSession(session);
- if (application && application->state() == Application::Starting) {
+ if (application) {
m_dbusWindowStack->WindowCreated(0, application->appId());
- application->setState(Application::Running);
- if ((application != m_mainStageApplication && application != m_sideStageApplication) || m_suspended) {
- suspendApplication(application);
- }
}
}
@@ -900,16 +692,6 @@ Application* ApplicationManager::findApplicationWithPid(const qint64 pid)
return nullptr;
}
-Application* ApplicationManager::applicationForStage(Application::Stage stage)
-{
- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusedApplicationForStage" << stage;
-
- if (stage == Application::MainStage)
- return m_mainStageApplication;
- else
- return m_sideStageApplication;
-}
-
void ApplicationManager::add(Application* application)
{
Q_ASSERT(application != nullptr);
@@ -920,6 +702,28 @@ void ApplicationManager::add(Application* application)
connect(application, &Application::stateChanged, this, [this](Application::State) { onAppDataChanged(RoleState); });
connect(application, &Application::stageChanged, this, [this](Application::Stage) { onAppDataChanged(RoleStage); });
+ QString appId = application->appId();
+ QString longAppId = application->longAppId();
+ QStringList arguments = application->arguments();
+
+ // The connection is queued as a workaround an issue in the PhoneStage animation that
+ // happens when you tap on a killed app in the spread to bring it to foreground, causing
+ // a Application::respawn() to take place.
+ // In any case, it seems like in general QML works better when don't do too many things
+ // in the same event loop iteration.
+ connect(application, &Application::startProcessRequested,
+ this, [=]() { m_taskController->start(appId, arguments); },
+ Qt::QueuedConnection);
+
+ connect(application, &Application::suspendProcessRequested, this, [=]() { m_taskController->suspend(longAppId); } );
+ connect(application, &Application::resumeProcessRequested, this, [=]() { m_taskController->resume(longAppId); } );
+
+ connect(application, &Application::stopped, this, [=]() {
+ remove(application);
+ application->deleteLater();
+ });
+
+
beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());
m_applications.append(application);
endInsertRows();
@@ -935,11 +739,6 @@ void ApplicationManager::remove(Application *application)
Q_ASSERT(application != nullptr);
qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();
- if (application == m_sideStageApplication)
- m_sideStageApplication = nullptr;
- if (application == m_mainStageApplication)
- m_mainStageApplication = nullptr;
-
application->disconnect(this);
int i = m_applications.indexOf(application);
@@ -953,6 +752,11 @@ void ApplicationManager::remove(Application *application)
Q_EMIT emptyChanged();
}
}
+
+ if (application == m_focusedApplication) {
+ m_focusedApplication = nullptr;
+ Q_EMIT focusedApplicationIdChanged();
+ }
}
void ApplicationManager::move(int from, int to) {