Merge lp:~mterry/qtmir/fix-wakelocks into lp:qtmir

Proposed by Michael Terry
Status: Superseded
Proposed branch: lp:~mterry/qtmir/fix-wakelocks
Merge into: lp:qtmir
Diff against target: 351 lines (+160/-6)
8 files modified
CMakeLists.txt (+1/-1)
debian/control (+2/-2)
src/modules/Unity/Application/application.cpp (+39/-2)
src/modules/Unity/Application/application.h (+4/-0)
src/modules/Unity/Application/application_manager.cpp (+2/-0)
src/modules/Unity/Application/application_manager.h (+1/-1)
tests/modules/ApplicationManager/application_manager_test.cpp (+108/-0)
tests/modules/common/qtmir_test.cpp (+3/-0)
To merge this branch: bzr merge lp:~mterry/qtmir/fix-wakelocks
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada Pending
Review via email: mp+279481@code.launchpad.net

This proposal has been superseded by a proposal from 2015-12-03.

Commit message

Don't hold a wakelock on apps that are exempt from lifecycle management.

This supports the new exemptFromLifecycle flag for applications as well as bringing back the internal RunningInBackground state.

The tests were revived from the same branch that removed them when we removed RunningInBackground (revision 400)

Description of the change

Don't hold a wakelock on apps that are exempt from lifecycle management.

This supports the new exemptFromLifecycle flag for applications as well as bringing back the internal RunningInBackground state.

The tests were revived from the same branch that removed them when we removed RunningInBackground [1] (and in hindsight, I should have thought harder about the implications of simply removing a test that confirmed we were releasing wakelocks).

This branch is related to unity-api [2] and unity8 [3] MPS. All of which are in service of fixing bug 1518764.

[1] https://code.launchpad.net/~mterry/qtmir/no-touch-no-lifecycle/+merge/272791

[2] https://code.launchpad.net/~mterry/unity-api/fix-wakelocks/+merge/279476
[3] https://code.launchpad.net/~mterry/unity8/fix-wakelocks/+merge/279489

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mterry/qtmir/fix-wakelocks updated
425. By Michael Terry

Merge from surfaceItemFillMode

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

In Application::setInternalState(Application::InternalState state):

"""
        case InternalState::RunningInBackground:
            releaseWakelock();
            break;
"""

could be simply:

"""
        case InternalState::RunningInBackground:

"""

as the next case does the same.

lp:~mterry/qtmir/fix-wakelocks updated
426. By Michael Terry

Collapse a switch statement

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-11-25 15:38:50 +0000
3+++ CMakeLists.txt 2015-12-03 17:30:39 +0000
4@@ -75,7 +75,7 @@
5 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
6 pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
7 pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=11)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=12)
10
11 include_directories(${APPLICATION_API_INCLUDE_DIRS})
12
13
14=== modified file 'debian/control'
15--- debian/control 2015-11-25 15:38:50 +0000
16+++ debian/control 2015-12-03 17:30:39 +0000
17@@ -22,7 +22,7 @@
18 libubuntu-app-launch2-dev,
19 libubuntu-application-api-dev (>= 2.1.0),
20 libudev-dev,
21- libunity-api-dev (>= 7.103),
22+ libunity-api-dev (>= 7.104),
23 liburl-dispatcher1-dev,
24 libxkbcommon-dev,
25 libxrender-dev,
26@@ -93,7 +93,7 @@
27 Conflicts: libqtmir,
28 libunity-mir1,
29 Provides: unity-application-impl,
30- unity-application-impl-11,
31+ unity-application-impl-12,
32 Description: Qt plugin for Unity specific Mir APIs
33 QtMir provides Qt/QML bindings for Mir features that are exposed through the
34 qtmir-desktop or qtmir-android QPA plugin such as Application management
35
36=== modified file 'src/modules/Unity/Application/application.cpp'
37--- src/modules/Unity/Application/application.cpp 2015-10-01 17:20:40 +0000
38+++ src/modules/Unity/Application/application.cpp 2015-12-03 17:30:39 +0000
39@@ -52,6 +52,7 @@
40 , m_session(nullptr)
41 , m_requestedState(RequestedRunning)
42 , m_processState(ProcessUnknown)
43+ , m_exemptFromLifecycle(false)
44 {
45 qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId();
46
47@@ -78,6 +79,7 @@
48 switch (m_state) {
49 case InternalState::Starting:
50 case InternalState::Running:
51+ case InternalState::RunningInBackground:
52 case InternalState::SuspendingWaitSession:
53 case InternalState::SuspendingWaitProcess:
54 wipeQMLCache();
55@@ -209,6 +211,8 @@
56 return "Starting";
57 case InternalState::Running:
58 return "Running";
59+ case InternalState::RunningInBackground:
60+ return "RunningInBackground";
61 case InternalState::SuspendingWaitSession:
62 return "SuspendingWaitSession";
63 case InternalState::SuspendingWaitProcess:
64@@ -277,6 +281,7 @@
65 case InternalState::Starting:
66 return Starting;
67 case InternalState::Running:
68+ case InternalState::RunningInBackground:
69 case InternalState::SuspendingWaitSession:
70 case InternalState::SuspendingWaitProcess:
71 case InternalState::Closing:
72@@ -327,6 +332,7 @@
73 case InternalState::Running:
74 // already where it's wanted to be
75 break;
76+ case InternalState::RunningInBackground:
77 case InternalState::SuspendingWaitSession:
78 case InternalState::Suspended:
79 resume();
80@@ -359,6 +365,7 @@
81 Q_ASSERT(m_processState == ProcessUnknown);
82 }
83 break;
84+ case InternalState::RunningInBackground:
85 case InternalState::SuspendingWaitSession:
86 case InternalState::SuspendingWaitProcess:
87 case InternalState::Suspended:
88@@ -408,6 +415,7 @@
89 case InternalState::Running:
90 setInternalState(InternalState::Closing);
91 break;
92+ case InternalState::RunningInBackground:
93 case InternalState::SuspendingWaitSession:
94 case InternalState::SuspendingWaitProcess:
95 case InternalState::Suspended:
96@@ -452,6 +460,7 @@
97 switch (m_state) {
98 case InternalState::Starting:
99 case InternalState::Running:
100+ case InternalState::RunningInBackground:
101 case InternalState::Closing:
102 m_session->resume();
103 break;
104@@ -510,6 +519,9 @@
105 case InternalState::Running:
106 acquireWakelock();
107 break;
108+ case InternalState::RunningInBackground:
109+ releaseWakelock();
110+ break;
111 case InternalState::Suspended:
112 releaseWakelock();
113 break;
114@@ -606,8 +618,15 @@
115 Q_ASSERT(m_state == InternalState::Running);
116 Q_ASSERT(m_session != nullptr);
117
118- setInternalState(InternalState::SuspendingWaitSession);
119- m_session->suspend();
120+ if (exemptFromLifecycle()) {
121+ // There's no need to keep the wakelock as the process is never suspended
122+ // and thus has no cleanup to perform when (for example) the display is
123+ // blanked.
124+ setInternalState(InternalState::RunningInBackground);
125+ } else {
126+ setInternalState(InternalState::SuspendingWaitSession);
127+ m_session->suspend();
128+ }
129 }
130
131 void Application::resume()
132@@ -622,6 +641,8 @@
133 } else if (m_state == InternalState::SuspendingWaitSession) {
134 setInternalState(InternalState::Running);
135 m_session->resume();
136+ } else if (m_state == InternalState::RunningInBackground) {
137+ setInternalState(InternalState::Running);
138 }
139 }
140
141@@ -639,6 +660,22 @@
142 return m_desktopData->isTouchApp();
143 }
144
145+bool Application::exemptFromLifecycle() const
146+{
147+ return m_exemptFromLifecycle;
148+}
149+
150+void Application::setExemptFromLifecycle(bool exemptFromLifecycle)
151+{
152+ if (m_exemptFromLifecycle != exemptFromLifecycle)
153+ {
154+ // We don't adjust current suspension state, we only care about exempt
155+ // status going into a suspend.
156+ m_exemptFromLifecycle = exemptFromLifecycle;
157+ Q_EMIT exemptFromLifecycleChanged(m_exemptFromLifecycle);
158+ }
159+}
160+
161 QString Application::longAppId() const
162 {
163 return m_longAppId;
164
165=== modified file 'src/modules/Unity/Application/application.h'
166--- src/modules/Unity/Application/application.h 2015-11-17 14:16:22 +0000
167+++ src/modules/Unity/Application/application.h 2015-12-03 17:30:39 +0000
168@@ -68,6 +68,7 @@
169 enum class InternalState {
170 Starting,
171 Running,
172+ RunningInBackground,
173 SuspendingWaitSession,
174 SuspendingWaitProcess,
175 Suspended,
176@@ -104,6 +105,8 @@
177 Qt::ScreenOrientations supportedOrientations() const override;
178 bool rotatesWindowContents() const override;
179 bool isTouchApp() const override;
180+ bool exemptFromLifecycle() const override;
181+ void setExemptFromLifecycle(bool) override;
182
183 void setStage(Stage stage);
184
185@@ -178,6 +181,7 @@
186 SessionInterface *m_session;
187 RequestedState m_requestedState;
188 ProcessState m_processState;
189+ bool m_exemptFromLifecycle;
190
191 friend class ApplicationManager;
192 friend class SessionManager;
193
194=== modified file 'src/modules/Unity/Application/application_manager.cpp'
195--- src/modules/Unity/Application/application_manager.cpp 2015-11-17 14:16:22 +0000
196+++ src/modules/Unity/Application/application_manager.cpp 2015-12-03 17:30:39 +0000
197@@ -229,6 +229,8 @@
198 return QVariant::fromValue(application->focused());
199 case RoleIsTouchApp:
200 return QVariant::fromValue(application->isTouchApp());
201+ case RoleExemptFromLifecycle:
202+ return QVariant::fromValue(application->exemptFromLifecycle());
203 case RoleSession:
204 return QVariant::fromValue(application->session());
205 case RoleFullscreen:
206
207=== modified file 'src/modules/Unity/Application/application_manager.h'
208--- src/modules/Unity/Application/application_manager.h 2015-11-17 14:16:22 +0000
209+++ src/modules/Unity/Application/application_manager.h 2015-12-03 17:30:39 +0000
210@@ -68,7 +68,7 @@
211
212 // FIXME: these roles should be added to unity-api and removed from here
213 enum MoreRoles {
214- RoleSession = RoleIsTouchApp+1,
215+ RoleSession = RoleExemptFromLifecycle+1,
216 RoleFullscreen,
217 };
218
219
220=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
221--- tests/modules/ApplicationManager/application_manager_test.cpp 2015-11-17 14:16:22 +0000
222+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-12-03 17:30:39 +0000
223@@ -1737,6 +1737,114 @@
224 }
225 }
226
227+TEST_F(ApplicationManagerTests, lifecycleExemptAppIsNotSuspended)
228+{
229+ using namespace ::testing;
230+
231+ const QString appId("testAppId");
232+ const quint64 procId = 12345;
233+
234+ // Set up Mocks & signal watcher
235+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
236+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
237+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
238+
239+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
240+
241+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
242+ .Times(1)
243+ .WillOnce(Return(true));
244+
245+ auto the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
246+ applicationManager.onProcessStarting(appId);
247+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
248+ bool authed = true;
249+ applicationManager.authorizeSession(procId, authed);
250+ onSessionStarting(session);
251+
252+ // App creates surface, focuses it so state is running
253+ FakeMirSurface *surface = new FakeMirSurface;
254+ onSessionCreatedSurface(session.get(), surface);
255+ surface->drawFirstFrame();
256+
257+ // Test normal lifecycle management as a control group
258+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
259+ ASSERT_EQ(Application::ProcessState::ProcessRunning, the_app->processState());
260+
261+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(mir_lifecycle_state_will_suspend));
262+ the_app->setRequestedState(Application::RequestedSuspended);
263+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
264+
265+ static_cast<qtmir::Session*>(the_app->session())->doSuspend();
266+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
267+ applicationManager.onProcessSuspended(the_app->appId());
268+ ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
269+
270+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(mir_lifecycle_state_resumed));
271+ the_app->setRequestedState(Application::RequestedRunning);
272+
273+ EXPECT_EQ(Application::Running, the_app->state());
274+
275+ // Now mark the app as exempt from lifecycle management and retest
276+ the_app->setExemptFromLifecycle(true);
277+
278+ EXPECT_EQ(Application::Running, the_app->state());
279+
280+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(_)).Times(0);
281+ the_app->setRequestedState(Application::RequestedSuspended);
282+
283+ // And expect it to be running still
284+ ASSERT_EQ(Application::InternalState::RunningInBackground, the_app->internalState());
285+
286+ the_app->setRequestedState(Application::RequestedRunning);
287+
288+ EXPECT_EQ(Application::Running, the_app->state());
289+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
290+}
291+
292+/*
293+ * Test lifecycle exempt applications have their wakelocks released when shell tries to suspend them
294+ */
295+TEST_F(ApplicationManagerTests, lifecycleExemptAppHasWakelockReleasedOnAttemptedSuspend)
296+{
297+ using namespace ::testing;
298+
299+ const QString appId("testAppId");
300+ const quint64 procId = 12345;
301+
302+ // Set up Mocks & signal watcher
303+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
304+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
305+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
306+
307+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
308+
309+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
310+ .Times(1)
311+ .WillOnce(Return(true));
312+
313+ auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
314+ applicationManager.onProcessStarting(appId);
315+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
316+ bool authed = true;
317+ applicationManager.authorizeSession(procId, authed);
318+ onSessionStarting(session);
319+
320+ // App creates surface, focuses it so state is running
321+ FakeMirSurface *surface = new FakeMirSurface;
322+ onSessionCreatedSurface(session.get(), surface);
323+ surface->drawFirstFrame();
324+
325+ // Mark app as exempt
326+ application->setExemptFromLifecycle(true);
327+
328+ application->setRequestedState(Application::RequestedSuspended);
329+
330+ EXPECT_FALSE(sharedWakelock.enabled());
331+ ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());
332+ EXPECT_EQ(Application::Running, application->state());
333+}
334+
335 /*
336 * Test that when user stops an application, application does not delete QML cache
337 */
338
339=== modified file 'tests/modules/common/qtmir_test.cpp'
340--- tests/modules/common/qtmir_test.cpp 2015-10-01 16:48:38 +0000
341+++ tests/modules/common/qtmir_test.cpp 2015-12-03 17:30:39 +0000
342@@ -26,6 +26,9 @@
343 case Application::InternalState::Running:
344 *os << "Running";
345 break;
346+ case Application::InternalState::RunningInBackground:
347+ *os << "RunningInBackground";
348+ break;
349 case Application::InternalState::SuspendingWaitSession:
350 *os << "SuspendingWaitSession";
351 break;

Subscribers

People subscribed via source and target branches