Merge lp:~dandrader/qtmir/appRestart-lp1527737 into lp:qtmir

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/qtmir/appRestart-lp1527737
Merge into: lp:qtmir
Prerequisite: lp:~dandrader/qtmir/initialSurfaceGeom
Diff against target: 706 lines (+315/-85)
10 files modified
src/modules/Unity/Application/application_manager.cpp (+81/-61)
src/modules/Unity/Application/application_manager.h (+1/-1)
src/modules/Unity/Application/mirsurfacelistmodel.cpp (+21/-0)
src/modules/Unity/Application/mirsurfacelistmodel.h (+2/-0)
src/modules/Unity/Application/session.cpp (+21/-22)
src/modules/Unity/Application/taskcontroller.h (+5/-1)
src/modules/Unity/Application/upstart/taskcontroller.cpp (+8/-0)
src/modules/Unity/Application/upstart/taskcontroller.h (+2/-0)
tests/framework/mock_task_controller.h (+1/-0)
tests/modules/ApplicationManager/application_manager_test.cpp (+173/-0)
To merge this branch: bzr merge lp:~dandrader/qtmir/appRestart-lp1527737
Reviewer Review Type Date Requested Status
Gerry Boland (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity8 CI Bot (community) continuous-integration Needs Fixing
Michał Sawicz Pending
Review via email: mp+284640@code.launchpad.net

This proposal supersedes a proposal from 2016-01-05.

Commit message

Handle case where TaskController requests an application that is already closing

Happens when you quickly close an application and then tap on its icon in the dash

Description of the change

* Are there any related MPs required for this MP to build/function as expected? Please list.
Not strictly needed but I haven't tried without it:
https://code.launchpad.net/~dandrader/qtubuntu/offscreenSurface-lp1527737/+merge/281638

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, on phone and desktop.

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

=== modified file 'src/modules/Unity/Application/application_manager.cpp'
+ // We interpret this as a focus request for a suspended app.
+ // Shell will have this app resumed if it complies with the focus request
     QObject::connect(controller, &TaskController::resumeRequested,
- manager, &ApplicationManager::onResumeRequested);
+ manager, &ApplicationManager::onFocusRequested);

The original purpose of the resumeRequested signal from UAL was to tell AppMan to un-suspend a suspended app, since AppMan used to manage that. But since UAL does suspend/resume now itself, this signal is worthless. Suspect we should remove completely.

Does not the focusRequested signal always get fired if url-dispatcher wants to focus an app? In which case, is this interpretation even necessary? If we can, I think we should remove it.

+ << "ApplicationManager::onProcessStarting(appId="<<appId<<") - User wants to start"
+ " a new instance of an application that is still closing. Queuing its start.";
we should investigate how to launch new instance immediately, instead of waiting for the closing one to quit. I'd be worried if the closing app crashes during close, as it won't be considered closed until apport has completed collecting the crash dump, causing the new instance a long delay. Not for this MP, but perhaps a TODO?

Also nitpick: could you line up the qCDebug strings, in both spots in this method?

+ // TaskController doesn't give us any PID so we have no way of telling apart two instances
+ // of an application.
we should get that fixed. UAL must have way of dealing with multiple instances of the same App, and distinguishing them. If not, we'll need to add it.

+1 on the header for the test. We should do that from now on.

+ // DeferredDelete is special: likes to be called out specifically or it won't come out
weird?? Event loop blocked somehow?

Nice work

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 07/01/2016 14:39, Gerry Boland wrote:
> Review: Needs Fixing
>
> === modified file 'src/modules/Unity/Application/application_manager.cpp'
> + // We interpret this as a focus request for a suspended app.
> + // Shell will have this app resumed if it complies with the focus request
> QObject::connect(controller, &TaskController::resumeRequested,
> - manager, &ApplicationManager::onResumeRequested);
> + manager, &ApplicationManager::onFocusRequested);
>
>
> The original purpose of the resumeRequested signal from UAL was to tell AppMan to un-suspend a suspended app, since AppMan used to manage that. But since UAL does suspend/resume now itself, this signal is worthless. Suspect we should remove completely.
>
> Does not the focusRequested signal always get fired if url-dispatcher wants to focus an app? In which case, is this interpretation even necessary? If we can, I think we should remove it.

When you tap on a suspended app in the dash you immediately get a
appStarting followed by a resumeRequest. If you don't respond to it (ie,
resume&focus the app), ages later you get a focusRequested.

So if I remove resumeRequested I would have to interpret appStarting for
a already running application as a focus request, which actually
wouldn't be wrong.

Btw, I find it odd that ubuntu_app_launch work that way, always sending
an appStarting signal even though the app is already running, but that's
the way it is...

> + << "ApplicationManager::onProcessStarting(appId="<<appId<<") - User wants to start"
> + " a new instance of an application that is still closing. Queuing its start.";
> we should investigate how to launch new instance immediately, instead of waiting for the closing one to quit. I'd be worried if the closing app crashes during close, as it won't be considered closed until apport has completed collecting the crash dump, causing the new instance a long delay. Not for this MP, but perhaps a TODO?

Agreed. The delay is noticeable. Added the TODO comment.

>
> Also nitpick: could you line up the qCDebug strings, in both spots in this method?

Wrapped the qCDebug boilerplate in a macro.

>
>
> + // TaskController doesn't give us any PID so we have no way of telling apart two instances
> + // of an application.
> we should get that fixed. UAL must have way of dealing with multiple instances of the same App, and distinguishing them. If not, we'll need to add it.

As far as I understand it ubuntu-app-launch (and the upstart service?)
have the concept that you can have only one running instance of an
application (like a service, that you start/stop using upstart).

This sounds like a bigger architectural change, out of scope for this MP.

> + // DeferredDelete is special: likes to be called out specifically or it won't come out
> weird?? Event loop blocked somehow?
>

QObject::deleteLater() generates a DeferredDelete event which is handled
in a special way in QCoreApplication etc, unlike any other QEvent.
There's code in there specifically for leaving it out of forced event
processing (like sendPostedEvents) unless you explicitly ask for that
particular event type.

Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

> On 07/01/2016 14:39, Gerry Boland wrote:
> > Review: Needs Fixing
> >
> > === modified file 'src/modules/Unity/Application/application_manager.cpp'
> > + // We interpret this as a focus request for a suspended app.
> > + // Shell will have this app resumed if it complies with the focus
> request
> > QObject::connect(controller, &TaskController::resumeRequested,
> > - manager, &ApplicationManager::onResumeRequested);
> > + manager, &ApplicationManager::onFocusRequested);
> >
> >
> > The original purpose of the resumeRequested signal from UAL was to tell
> AppMan to un-suspend a suspended app, since AppMan used to manage that. But
> since UAL does suspend/resume now itself, this signal is worthless. Suspect we
> should remove completely.
> >
> > Does not the focusRequested signal always get fired if url-dispatcher wants
> to focus an app? In which case, is this interpretation even necessary? If we
> can, I think we should remove it.
>
> When you tap on a suspended app in the dash you immediately get a
> appStarting followed by a resumeRequest. If you don't respond to it (ie,
> resume&focus the app), ages later you get a focusRequested.
>
> So if I remove resumeRequested I would have to interpret appStarting for
> a already running application as a focus request, which actually
> wouldn't be wrong.
>
> Btw, I find it odd that ubuntu_app_launch work that way, always sending
> an appStarting signal even though the app is already running, but that's
> the way it is...

Fair enough. This will do for now.

>

> >
> >
> > + // TaskController doesn't give us any PID so we have no way of telling
> apart two instances
> > + // of an application.
> > we should get that fixed. UAL must have way of dealing with multiple
> instances of the same App, and distinguishing them. If not, we'll need to add
> it.
>
> As far as I understand it ubuntu-app-launch (and the upstart service?)
> have the concept that you can have only one running instance of an
> application (like a service, that you start/stop using upstart).
>
> This sounds like a bigger architectural change, out of scope for this MP.

Oh of course. Was just observation.

> > + // DeferredDelete is special: likes to be called out specifically or it
> won't come out
> > weird?? Event loop blocked somehow?
> >
>
> QObject::deleteLater() generates a DeferredDelete event which is handled
> in a special way in QCoreApplication etc, unlike any other QEvent.
> There's code in there specifically for leaving it out of forced event
> processing (like sendPostedEvents) unless you explicitly ask for that
> particular event type.

Interesting, I didn't know that.

Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

Just ran UITK AP tests to ensure sanity, no issues found. Happy to approve

review: Approve
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

QA found a problem with this and I can confirm on devel-proposed/mako.

Trying to follow steps from Victor [1] I got into a state where unity8 rejects the camera app:

qtmir.applications: ApplicationManager::onProcessStarting(appId="com.ubuntu.camera_camera") - User wants to start a new instance of an application that is still closing and is already queued to start later.
qtmir.mir: SessionAuthorizer::connection_is_allowed - this= SessionAuthorizer(0xb16defe4) pid= 22192
qtmir.applications: ApplicationManager::authorizeSession - pid= 22192
ApplicationManager REJECTED connection from app with pid 22192 as it was not launched by upstart, and no desktop_file_hint is specified
qtmir.applications: ApplicationManager::onProcessFailed - appId= "com.ubuntu.camera_camera"
qtmir.applications: ApplicationManager::onProcessStopped - appId= "com.ubuntu.camera_camera"

Victor can confirm the same happened to him with camera and settings apps.

[1] https://trello.com/c/nAccJ8Qd/2639-854-ubuntu-landing-030-gsettings-qt-unity8-qtmir-qtubuntu-saviq

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 12/01/2016 14:39, Michał Sawicz wrote:
> Review: Needs Fixing
>
> QA found a problem with this and I can confirm on devel-proposed/mako.
>
> Trying to follow steps from Victor [1] I got into a state where unity8 rejects the camera app:
>
> qtmir.applications: ApplicationManager::onProcessStarting(appId="com.ubuntu.camera_camera") - User wants to start a new instance of an application that is still closing and is already queued to start later.
> qtmir.mir: SessionAuthorizer::connection_is_allowed - this= SessionAuthorizer(0xb16defe4) pid= 22192
> qtmir.applications: ApplicationManager::authorizeSession - pid= 22192
> ApplicationManager REJECTED connection from app with pid 22192 as it was not launched by upstart, and no desktop_file_hint is specified
> qtmir.applications: ApplicationManager::onProcessFailed - appId= "com.ubuntu.camera_camera"
> qtmir.applications: ApplicationManager::onProcessStopped - appId= "com.ubuntu.camera_camera"
>
> Victor can confirm the same happened to him with camera and settings apps.
>
> [1] https://trello.com/c/nAccJ8Qd/2639-854-ubuntu-landing-030-gsettings-qt-unity8-qtmir-qtubuntu-saviq

I followed the steps below:

"""

  * Open messaging app.
  * Slide up to create a new message.
  * Tap on the camera icon.
  * In content hub tap on the camera app.
  * Take a photo in the camera app.
  * Tap on the big tick. Returned to the messaging app with the image in
    the text window.
  * Swipe in from the right to reveal the spread.
  * Open spread.
  * Switch to the camera app.

Expected result:

  * Camera app should display the live view.

Actual result:

  * Camera app shows a black screen

"""

A second after switching to camera-app it goes away.
Log shows that camera-app quit (closed itself). Which is expected as it
was summoned by the content hub as a helper and was no longer needed.
Different from the log you got.

In this situation there's nothing unity8 can do. If the app closes
itself shortly after it's focused, I don't think unity8 should go and
restart it.

Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

Furthermore, in this "messaging -> content-hub -> camera-app" use case, unity8 is *not* asking camera-app to close itself, unlike the use case of the bug report which is what this MP is fixing.

From unity8's point of view, camera-app is simply quitting on its own accord.

Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

it probably shouldn't even be in the spread in the first place, like if it were a surface from a prompt helper session.

Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

Then you didn't get the issue I described. There are no reliable steps to reproduce, but after I followed those steps (and camera crashed due to bug #1524131, I couldn't start camera at all after it closed. The above messages were printed in unity8's log, not how it says that camera was not started by upstart and that's why it's rejected. But camera was, in fact, started by upstart, from the apps scope. Victor reported this same problem with settings app, so unrelated to content hub. I've not seen it happen without this change.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:440
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/3/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/3/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

I just can't reproduce it. I'm stuck. Added the handling of a special case though, where upstart would send a bogus signal.

Could you please install the latest version of this patch + lp:~dandrader/qtubuntu/offscreenSurface-lp1527737 and then send me the log of this failure? Ah, and not just the last few lines!

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:441
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/13/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/13/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

Eh, you'll need to rebase this one, too...

review: Needs Fixing
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:444
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/47/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/47/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
439. By Nick Dedekind

Added fix and test for closing app during the StoppedResumable state. Fixes: #1541388
Approved by: Michael Zanetti

440. By CI Train Bot Account

Releasing 0.4.7+16.04.20160208-0ubuntu1

441. By Nick Dedekind

Moved test framework into a static library for quicker recompilation.
Approved by: Gerry Boland

442. By Daniel d'Andrada

Remove the useless TaskController

It was just forwarding calls between ApplicationManager and ApplicationCrontroller.
Had no logic of its own.

ApplicationCrontroller was then renamed to TaskController as the latter has a better API and it also keeps ApplicationManager code more or less untouched.

Tests have been improved a bit by better emulating TaskController behavior
Approved by: Nick Dedekind

443. By Daniel d'Andrada

Let shell decide the initial surface size Fixes: #1532974
Approved by: Nick Dedekind

444. By Daniel d'Andrada

Surface Size Hints
Approved by: Lukáš Tinkl

445. By Albert Astals Cid

Reset start time if the timestamp travels to the past
 Fixes: #1524488
Approved by: Nick Dedekind

446. By Albert Astals Cid

Provide branch prediction information to the if in compressTimestamp
Approved by: Gerry Boland

447. By Michał Sawicz

Use QStandardPaths to determine QML cache location

Also use temporary XDG_CACHE_HOME in ApplicationManagerTests so we don't pollute user's $HOME
Approved by: Unity8 CI Bot, Albert Astals Cid

448. By Daniel d'Andrada

Update mir version requirement
Approved by: Nick Dedekind

449. By Michał Sawicz

Bump unity-api dependencies
Approved by: Michael Terry

450. By CI Train Bot Account

Releasing 0.4.7+16.04.20160212-0ubuntu1

451. By Alan Griffiths

Mir 0.20 Release
Approved by: Cemil Azizoglu

452. By CI Train Bot Account

Releasing 0.4.7+16.04.20160219-0ubuntu1

Revision history for this message
Gerry Boland (gerboland) wrote :

/me picks this up again. Will see what light I can shed on the thing QA found

Revision history for this message
Gerry Boland (gerboland) wrote :

Eep, lots of conflicts with trunk

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) wrote :

I fixed conflicts here: lp:~gerboland/qtmir/appRestart-lp1527737-rebased save you the trouble

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

On 08/03/2016 12:15, Gerry Boland wrote:
> I fixed conflicts here: lp:~gerboland/qtmir/appRestart-lp1527737-rebased save you the trouble

Dednick had some hot fixes on problems similar to this one recently.
Which makes me think how relevant this MP still is (haven't spent any
time perusing it though).
And I also made a lot of changes in lifecycle code in the surface-based
window management work. If we want to proceed with this MP I would
rather put it on top of the surface WM work. It's like a whole new world
(surface-based versus application/single-surface based lifecycle)

453. By Gerry Boland

Allow Mir remove command line arguments it understands, before letting Qt process them.
Approved by: Daniel d'Andrada

454. By Alan Griffiths

Copy the Window Management work-in-progress from Mir examples.
Approved by: Gerry Boland

455. By Michael Zanetti

Add a dedicated property for the inputMethodSurface

When we're hot-plugging screens, the OSK ui might only be created after surfaceCreated() is emitted and we miss that signal. Fixes: #1545286
Approved by: Daniel d'Andrada

456. By Gerry Boland

Screen: only enable orientation sensor for internal display.

It's not great, but works around orientation sensor not working after device unplug from monitor. I believe this is because QtUbuntu-Sensors and Platform-api are not dealing properly with multiple accessors of the sensors. Instead only the last accessor gets correct state, and the earlier ones get nothing. Fixes: #1545286
Approved by: Unity8 CI Bot, Michael Zanetti

457. By Daniel d'Andrada

Ensure QmlEngine doesn't delete our MirSurfaces
Approved by: Gerry Boland, Unity8 CI Bot

458. By Gerry Boland

Ensure ScreenWindow geometry correctly set and used after moving Screen

NB: this will conflict with lp:~unity-team/qtmir/set-display-config, from which this is a cherry-pick Fixes: #1545286
Approved by: Gerry Boland, Unity8 CI Bot

459. By Michael Terry

Fix some mocks to use more realistic code paths.

Specifically, MockApplicationController::doAppIdHasProcessId() was returning -1 if the appId was unknown. But that function returns a bool, so the -1 got translated into a true value. So that mocked method returned true for appIds that hadn't been registered yet.

In order to fix that, I decided to rewrite doAppIdHasProcessId to call primaryPidForAppId so that it could be overridden easier in tests. And adjusted the affected tests to all set an ON_CALL(primaryPidForAppId).

(Because otherwise the test has no way to influence the pid used for children -- why do we actually fork off a while(true) process?)

There are several other fixes here for pieces of the mocks that I found were missing while exploring solutions for the above problem:

- I'm actually not 100% clear on why this started being called, but MockSession::name() couldn't create a default value for QString and the gtest framework was bailing when it was called. So I added a default.

- Similarly, if ApplicationManager::authorizeSession() tries to examine an app's commandline, gtest will abort because MockProcInfo::command_line() doesn't have a mock return value. So I added a default.

- And if authorizeSession() lasted long enough to see that the Application object already existed, it deleted the desktop reader object it had made. But the test mocks always returned the same object, so this caused a segfault. I retooled the ApplicationManager tests to return a new object each time DesktopFileReaderFactory::createInstance was called.
Approved by: Gerry Boland, Unity8 CI Bot

460. By Alberto Aguirre

Hook MirOpenGLContext::doneCurrent to Screen::doneCurrent

Actually unbind the opengl/egl context when requested.
Approved by: Gerry Boland

461. By Michał Sawicz

Drop dummy autopkgtest

It doesn't make sense after all - DEP-8 tests are meant to test packages as-installed, not rebuild them from scratch. This meant that new Mir releases got blocked on this, because old qtmir was trying to build against new Mir, which usually fails, as needs adaptation for API changes.
Approved by: Gerry Boland

462. By CI Train Bot Account

Releasing 0.4.7+16.04.20160310.1-0ubuntu1

463. By Nick Dedekind

Added setStage for sidestage redesign and removed ExecFlags
Approved by: Daniel d'Andrada

464. By Michał Sawicz

Add support for low shell chrome. Fixes: #1535397
Approved by: Gerry Boland

465. By Michał Sawicz

Support for switching keyboard layouts Fixes: #1491340, #1524400
Approved by: Michael Terry

466. By CI Train Bot Account

Releasing 0.4.8+16.04.20160322.1-0ubuntu1

467. By Daniel van Vugt

Don't use preferred_mode_index. Some devices like TVs don't provide a
preferred mode, so preferred_mode_index may be out of range (a clumsy
way to represent 'none'). Instead use the resolution of the current display mode, which should always be in range.

Fixes LP: #1560497 Fixes: #1560497
Approved by: Gerry Boland

468. By CI Train Bot Account

Releasing 0.4.8+16.04.20160329.1-0ubuntu1

469. By Michał Sawicz

Drop leftover Xs-Testsuite header
Approved by: Unity8 CI Bot, Gerry Boland

470. By CI Train Bot Account

Releasing 0.4.8+16.04.20160330-0ubuntu1

471. By Daniel d'Andrada

Logging of Qt's OpenGL debug messages now must be enabled via CMake option
Approved by: Gerry Boland

472. By Daniel d'Andrada

Application: improve debug logging

Minimize boilerplate in logging code, as well as standardise its format.
Approved by: Gerry Boland

473. By Daniel d'Andrada

Remove application screenshot provider

It's no longer needed now that QML provides a "item-snapshot" feature.

Besides, it has no purpose in a surface-based window management.
Approved by: Lukáš Tinkl, Gerry Boland

474. By Daniel d'Andrada

MirSurface: replace keymapLayout and keymapVariant with keymap

That's easier for unity8 to use, leads to declarative code.
Approved by: Lukáš Tinkl

475. By Daniel d'Andrada

Surface-based window management

- Session is no longer exported to QML. It's now an internal qtmir concept.
Approved by: Gerry Boland

476. By Michał Sawicz

Inline -gles packaging.
Approved by: Gerry Boland

477. By CI Train Bot Account

Releasing 0.4.8+16.04.20160413-0ubuntu1

478. By Alberto Aguirre

Mir 0.22 compatibility
Approved by: Cemil Azizoglu, Kevin DuBois, Brandon Schaefer

479. By CI Train Bot Account

Releasing 0.4.8+16.04.20160426-0ubuntu1

480. By Michael Terry

Use latest UbuntuAppLaunch API which supports libertine apps.
Approved by: Nick Dedekind

481. By Lukáš Tinkl

Regression fix: restore window title handling Fixes: #1563522
Approved by: Nick Dedekind

482. By CI Train Bot Account

Releasing 0.4.8+16.04.20160428-0ubuntu1

483. By Gerry Boland

Enhance ScreenController & the DisplayConfigurationPolicy to implement dynamic grid units.

- Rename ScreenController to ScreenModel, as it just reflects current screen state, does not offer means to configure it
- ScreenController can update state of existing Screens, based on Mir DisplayConfiguration changes.
- Expand Screen to include scale & form factor properties, with getter/notifier in NativeInterface. This enables the dynamic grid units in the shell
- Add a Unity.Screens qml module to give QML better information about connected screens, and allow basic reconfiguring.
- Implement a basic display configuration policy to set suitable scale and form factor on an external display (needed for dynamic grid units) Fixes: #1573532
Approved by: Unity8 CI Bot, Daniel d'Andrada

484. By CI Train Bot Account

Releasing 0.4.8+16.04.20160429.1-0ubuntu1

485. By Daniel d'Andrada

Session: Add a blank surface to the public list if it already has child prompt surfaces

A prompt session may come in too early, before its parent session got a chance
to draw its surface's first frame.

Normally that wouldn't be a problem and we could withhold that parent surface until
it's finally drawn to. But unfortunately the application process might be blocked,
unable to draw anything, until its child prompt session gets dismissed.

Because of that we have no option but to expose this blank surface to shell so that
it can display it along with the prompt surface on top of it, so that the user can
interact with it right away and finally unblock the application. Fixes: #1578665
Approved by: Unity8 CI Bot

486. By CI Train Bot Account

Releasing 0.4.8+16.04.20160511-0ubuntu1

487. By Daniel d'Andrada

Fix ProxySurfaceListModel::countChanged signal
Approved by: Unity8 CI Bot

488. By Daniel d'Andrada

Move prompt surfaces from MirSurface to Application and emit firstChanged signal

489. By CI Train Bot Account

Releasing 0.4.8+16.04.20160518.1-0ubuntu1

490. By Gerry Boland

UAL can throw on creating Application if invalid appId, catch instead of aborting Fixes: #1578258
Approved by: Unity8 CI Bot, Michael Terry

491. By Daniel d'Andrada

Application: Don't respawn if closed while still starting up

+ refactoring of Application::onSessionStateChanged to explicitly cover every single internal state on session stopping. Fixes: #1575577
Approved by: Gerry Boland, Unity8 CI Bot, Michael Terry

492. By Alan Griffiths

Reduce coupling to MirServer - it has been misused as a context object.
Approved by: Alan Griffiths, Unity8 CI Bot, Daniel d'Andrada

493. By Michał Sawicz

Centralize logging categories

The header part is already centralized. Make the cpp the same.

Moves all logging category symbols to the mirserver lib.

Enables mirserver to use logging categories that were previously declared in the Unity.Application module.
Approved by: Unity8 CI Bot, Michael Terry

494. By Daniel d'Andrada

Also interpret the cursor names used by Mir client API Fixes: #1447839
Approved by: Unity8 CI Bot, Michał Sawicz

495. By Timo Jyrinki

Use FindQt5PlatformSupport to find platform support, other methods not available on Qt 5.6 anymore. Fixes: #1554404
Approved by: Gerry Boland

496. By CI Train Bot Account

Releasing 0.4.8+16.10.20160520.1-0ubuntu1

497. By Daniel d'Andrada

Bump unity-api versions
Approved by: Lukáš Tinkl

498. By Alan Griffiths

Drop the (unused) prototype Window Management code
Approved by: Gerry Boland, Unity8 CI Bot

499. By Daniel d'Andrada

Fix mir::scene::Surface focus attribute updates
Approved by: Unity8 CI Bot

500. By Albert Astals Cid

Enable workaround_brokenFBOReadBack on various chipsets

Gives for impressive improvements in QSGDefaultDistanceFieldGlyphCache::storeGlyphs
impressive = 788ms -> 6ms

Copied from qtubuntu
 Fixes: #1581123
Approved by: Gerry Boland, Unity8 CI Bot

501. By Alan Griffiths

Remove workaround for lp:1502200
Approved by: Unity8 CI Bot, Daniel d'Andrada

502. By CI Train Bot Account

Releasing 0.4.8+16.10.20160525.2-0ubuntu1

503. By Kevin DuBois

rebuild for mir 0.23

504. By CI Train Bot Account

Releasing 0.4.8+16.10.20160602-0ubuntu1

505. By Michael Zanetti

Adding ApplicationInfo::surfaceCount property

506. By Albert Astals Cid

Add support for compiler sanitizers via ECM

507. By Albert Astals Cid

Compile with -fsanitize=undefined

508. By Albert Astals Cid

Fix leak in ScreensModelTest (LP: #1585502)

509. By Albert Astals Cid

Fix leak in SessionManager test (LP: #1585498)

510. By Albert Astals Cid

Remove unused m_sessions member

511. By Albert Astals Cid

Add missing breaks

512. By Albert Astals Cid

Initialize m_lastX and m_lastY

513. By Albert Astals Cid

Initialize m_sessionId

514. By Albert Astals Cid

Give the locker a name

515. By Albert Astals Cid

Fix memory leak in QtEventFeederTest (LP: #1585503)

516. By Albert Astals Cid

Fix leaks in application_manager_test (LP: #1585501)

517. By Daniel d'Andrada

Improve Session debug logging

518. By Daniel d'Andrada

MirSurfaceListModel: prepending a surface always causes firstChanged() emission

519. By Gerry Boland

Release temporary GL context ASAP, fixes QtMir on X11. Add some hotpath branching hints (LP: #1588921)

520. By CI Train Bot Account

Releasing 0.4.8+16.10.20160614-0ubuntu1

521. By Daniel d'Andrada

Handle case where TaskController requests an application that is already closing

Happens when you quickly close an application and then tap on its icon in the dash

522. By Daniel d'Andrada

Optimize code

523. By Daniel d'Andrada

Fix bad rebase on top of latest trunk

Code was still using the old, deprecated signal.

Unmerged revisions

523. By Daniel d'Andrada

Fix bad rebase on top of latest trunk

Code was still using the old, deprecated signal.

522. By Daniel d'Andrada

Optimize code

521. By Daniel d'Andrada

Handle case where TaskController requests an application that is already closing

Happens when you quickly close an application and then tap on its icon in the dash

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/modules/Unity/Application/application_manager.cpp'
2--- src/modules/Unity/Application/application_manager.cpp 2016-05-06 08:41:15 +0000
3+++ src/modules/Unity/Application/application_manager.cpp 2016-06-16 20:19:32 +0000
4@@ -56,6 +56,8 @@
5 namespace unityapi = unity::shell::application;
6
7 #define DEBUG_MSG qCDebug(QTMIR_APPLICATIONS).nospace() << "ApplicationManager::" << __func__
8+#define WARNING_MSG qCWarning(QTMIR_APPLICATIONS).nospace() << "ApplicationManager::" << __func__
9+#define CRITICAL_MSG qCCritical(QTMIR_APPLICATIONS).nospace() << "ApplicationManager::" << __func__
10
11 namespace qtmir
12 {
13@@ -102,8 +104,10 @@
14 manager, &ApplicationManager::onProcessFailed);
15 QObject::connect(controller, &TaskController::focusRequested,
16 manager, &ApplicationManager::onFocusRequested);
17+ // We interpret this as a focus request for a suspended app.
18+ // Shell will have this app resumed if it complies with the focus request
19 QObject::connect(controller, &TaskController::resumeRequested,
20- manager, &ApplicationManager::onResumeRequested);
21+ manager, &ApplicationManager::onFocusRequested);
22 }
23
24 } // namespace
25@@ -368,35 +372,61 @@
26 void ApplicationManager::onProcessStarting(const QString &appId)
27 {
28 tracepoint(qtmir, onProcessStarting);
29- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting - appId=" << appId;
30-
31- Application *application = findApplication(appId);
32- if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
33- auto appInfo = m_taskController->getInfoForApp(appId);
34- if (!appInfo) {
35- qCWarning(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting - Unable to instantiate application with appId" << appId;
36- return;
37- }
38-
39- application = new Application(
40- m_sharedWakelock,
41- appInfo,
42- QStringList(),
43- this);
44- add(application);
45- application->requestFocus();
46- }
47- else {
48- if (application->internalState() == Application::InternalState::StoppedResumable) {
49- // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
50- // application and focus it immediately (as user expects app to still be running).
51- qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally";
52- application->requestFocus();
53- } else {
54- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId"
55- << appId;
56- }
57- }
58+
59+ Application *application = nullptr;
60+
61+ application = findClosingApplication(appId);
62+ if (application) {
63+ // TODO: We should create a new Application instance right away in order to give immediate
64+ // feedback to the user, even though there's no actual process backing it up yet.
65+ if (!m_queuedStartApplications.contains(appId)) {
66+ m_queuedStartApplications.append(appId);
67+ DEBUG_MSG << "(" << appId << ") User wants to start a new instance of an application that is still closing."
68+ " Queuing its start.";
69+ connect(application, &QObject::destroyed, this, [this, appId]() {
70+ m_queuedStartApplications.removeAll(appId);
71+ startApplication(appId, QStringList());
72+ }, Qt::QueuedConnection); // Queued so that we finish the app removal before starting again.
73+ } else {
74+ // Just ignore it.
75+ DEBUG_MSG << "(" << appId << ") User wants to start a new instance of an application that is still closing"
76+ " and is already queued to start later.";
77+ }
78+ } else {
79+ application = findApplication(appId);
80+ if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
81+ createNewApplication(appId);
82+ } else {
83+ if (application->internalState() == Application::InternalState::StoppedResumable) {
84+ // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
85+ // application and focus it immediately (as user expects app to still be running).
86+ DEBUG_MSG << "(" << appId << ") Stopped application is being resumed externally";
87+ application->requestFocus();
88+ } else {
89+ DEBUG_MSG << "(" << appId << ") application already exists";
90+ }
91+ application->setProcessState(Application::ProcessRunning);
92+ }
93+ }
94+}
95+
96+void ApplicationManager::createNewApplication(const QString &appId)
97+{
98+ DEBUG_MSG << "(" << appId << ")";
99+
100+ auto appInfo = m_taskController->getInfoForApp(appId);
101+ if (!appInfo) {
102+ WARNING_MSG << "(" << appId << ") Unable to instantiate application";
103+ return;
104+ }
105+
106+ auto application = new Application(
107+ m_sharedWakelock,
108+ appInfo,
109+ QStringList(),
110+ this);
111+ add(application);
112+ application->requestFocus();
113 application->setProcessState(Application::ProcessRunning);
114 }
115
116@@ -412,7 +442,7 @@
117
118 Application *application = findApplication(appId);
119 if (!application) {
120- qCritical() << "No such running application with appId" << appId;
121+ CRITICAL_MSG << "(" << inputAppId << ") No such running application";
122 return false;
123 }
124
125@@ -454,14 +484,18 @@
126 tracepoint(qtmir, onProcessStopped);
127 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - appId=" << appId;
128
129- Application *application = findApplication(appId);
130+ // TaskController doesn't give us any PID so we have no way of telling apart two instances
131+ // of an application.
132+ // The only situation where we expect to have two simultaneous instances is where there's one
133+ // closing and a newer one just starting, in which case we assume he's referring to the former.
134+ Application *application = findClosingApplication(appId);
135 if (!application) {
136- application = findClosingApplication(appId);
137+ application = findApplication(appId);
138 }
139
140 if (!application) {
141- qDebug() << "ApplicationManager::onProcessStopped reports stop of appId=" << appId
142- << "which AppMan is not managing, ignoring the event";
143+ // Might happen if the onProcessFailed() was called for this app and it already got deleted in response.
144+ DEBUG_MSG << "(" << appId << ") Not managing it, ignoring.";
145 return;
146 }
147
148@@ -490,29 +524,15 @@
149
150 void ApplicationManager::onFocusRequested(const QString& appId)
151 {
152- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onFocusRequested - appId=" << appId;
153-
154- Application *application = findApplication(appId);
155- if (application) {
156- application->requestFocus();
157- }
158-}
159-
160-void ApplicationManager::onResumeRequested(const QString& appId)
161-{
162- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onResumeRequested - appId=" << appId;
163-
164- Application *application = findApplication(appId);
165-
166- if (!application) {
167- qCritical() << "ApplicationManager::onResumeRequested: No such running application" << appId;
168- return;
169- }
170-
171- // We interpret this as a focus request for a suspended app.
172- // Shell will have this app resumed if it complies with the focus request
173- if (application->state() == Application::Suspended) {
174- application->requestFocus();
175+ Application *application = findApplication(appId);
176+
177+ if (m_queuedStartApplications.contains(appId) || findClosingApplication(appId) != nullptr) {
178+ DEBUG_MSG << "(" << appId << ") Ignoring request as the application is closing and/or queued to start";
179+ } else if (application) {
180+ DEBUG_MSG << "(" << appId << ") Emitting focusRequested";
181+ Q_EMIT focusRequested(appId);
182+ } else {
183+ WARNING_MSG << "(" << appId << ") No such running application";
184 }
185 }
186
187@@ -702,9 +722,9 @@
188
189 connect(application, &Application::stopProcessRequested, this, [=]() {
190 if (!m_taskController->stop(appId) && application->pid() > 0) {
191- qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
192- << "Sending SIGTERM to process:" << appId;
193- kill(application->pid(), SIGTERM);
194+ qCWarning(QTMIR_APPLICATIONS) << "FAILED to ask Upstart to stop application with appId" << appId
195+ << "Sending SIGTERM to process";
196+ m_taskController->kill(application->pid());
197 application->setProcessState(Application::ProcessStopped);
198 }
199 });
200
201=== modified file 'src/modules/Unity/Application/application_manager.h'
202--- src/modules/Unity/Application/application_manager.h 2016-05-05 15:02:01 +0000
203+++ src/modules/Unity/Application/application_manager.h 2016-06-16 20:19:32 +0000
204@@ -111,7 +111,6 @@
205 void onProcessSuspended(const QString& appId);
206 void onProcessFailed(const QString& appId, TaskController::Error error);
207 void onFocusRequested(const QString& appId);
208- void onResumeRequested(const QString& appId);
209
210 Q_SIGNALS:
211 void emptyChanged();
212@@ -132,6 +131,7 @@
213 QModelIndex findIndex(Application* application);
214 void resumeApplication(Application *application);
215 QString toString() const;
216+ void createNewApplication(const QString &appId);
217
218 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);
219 Application *findClosingApplication(const QString &inputAppId) const;
220
221=== modified file 'src/modules/Unity/Application/mirsurfacelistmodel.cpp'
222--- src/modules/Unity/Application/mirsurfacelistmodel.cpp 2016-05-17 19:57:52 +0000
223+++ src/modules/Unity/Application/mirsurfacelistmodel.cpp 2016-06-16 20:19:32 +0000
224@@ -20,6 +20,9 @@
225
226 #include <paths.h>
227
228+#include <QDebug>
229+#include <QDebugStateSaver>
230+
231 namespace unityapp = unity::shell::application;
232 using namespace qtmir;
233
234@@ -284,3 +287,21 @@
235
236 return m_sourceList->data(index, role);
237 }
238+
239+QDebug operator<<(QDebug dbg, const unityapp::MirSurfaceListInterface &surfaceListConst)
240+{
241+ auto surfaceList = const_cast<unityapp::MirSurfaceListInterface*>(&surfaceListConst);
242+
243+ QDebugStateSaver saver(dbg);
244+ dbg.nospace();
245+ dbg << "MirSurfaceList(";
246+ for (int i = 0; i < surfaceList->count(); ++i) {
247+ if (i > 0) {
248+ dbg << ", ";
249+ }
250+ auto surface = surfaceList->get(i);
251+ dbg << (void*)surface;
252+ }
253+ dbg << ')';
254+ return dbg;
255+}
256
257=== modified file 'src/modules/Unity/Application/mirsurfacelistmodel.h'
258--- src/modules/Unity/Application/mirsurfacelistmodel.h 2016-05-10 21:06:21 +0000
259+++ src/modules/Unity/Application/mirsurfacelistmodel.h 2016-06-16 20:19:32 +0000
260@@ -94,4 +94,6 @@
261
262 } // namespace qtmir
263
264+QDebug operator<<(QDebug, const unity::shell::application::MirSurfaceListInterface &);
265+
266 #endif // QTMIR_MIRSURFACELISTMODEL_H
267
268=== modified file 'src/modules/Unity/Application/session.cpp'
269--- src/modules/Unity/Application/session.cpp 2016-05-17 19:18:44 +0000
270+++ src/modules/Unity/Application/session.cpp 2016-06-16 20:19:32 +0000
271@@ -38,6 +38,8 @@
272
273 using unity::shell::application::ApplicationInfoInterface;
274
275+#define DEBUG_MSG qCDebug(QTMIR_SURFACES).nospace() << "Session[" << (void*)this << ",name=" << name() << "]::" << __func__
276+
277 namespace qtmir
278 {
279
280@@ -75,7 +77,7 @@
281 , m_live(true)
282 , m_promptSessionManager(promptSessionManager)
283 {
284- qCDebug(QTMIR_SESSIONS) << "Session::Session() " << this->name();
285+ DEBUG_MSG << "()";
286
287 setSuspendTimer(new Timer);
288
289@@ -84,7 +86,7 @@
290
291 Session::~Session()
292 {
293- qCDebug(QTMIR_SESSIONS) << "Session::~Session() " << name();
294+ DEBUG_MSG << "()";
295 stopPromptSessions();
296
297 QList<SessionInterface*> children(m_children->list());
298@@ -107,7 +109,7 @@
299 Q_ASSERT(m_state == Session::Suspending);
300
301 if (m_surfaceList.count() == 0) {
302- qCDebug(QTMIR_SESSIONS) << "Application::suspend - no surface to call stopFrameDropper() on!";
303+ DEBUG_MSG << " no surface to call stopFrameDropper() on!";
304 } else {
305 for (int i = 0; i < m_surfaceList.count(); ++i) {
306 auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
307@@ -153,8 +155,7 @@
308 return;
309 }
310
311- qCDebug(QTMIR_SESSIONS) << "Session::setState - session=" << name()
312- << "state=" << sessionStateToString(state);
313+ DEBUG_MSG << "(state=" << sessionStateToString(state) << ")";
314
315 if (m_state == Suspending) {
316 m_suspendTimer->stop();
317@@ -198,7 +199,7 @@
318
319 void Session::registerSurface(MirSurfaceInterface *newSurface)
320 {
321- qCDebug(QTMIR_SESSIONS) << "Session::resgisterSurface - session=" << name() << "surface=" << newSurface;
322+ DEBUG_MSG << "(surface=" << newSurface << ")";
323
324 // Only notify QML of surface creation once it has drawn its first frame.
325 if (newSurface->isFirstFrameDrawn()) {
326@@ -214,7 +215,7 @@
327
328 void Session::prependSurface(MirSurfaceInterface *newSurface)
329 {
330- qCDebug(QTMIR_SESSIONS) << "Session::prependSurface - session=" << name() << "surface=" << newSurface;
331+ DEBUG_MSG << "(surface=" << newSurface << ")";
332
333 connect(newSurface, &MirSurfaceInterface::stateChanged,
334 this, &Session::updateFullscreenProperty);
335@@ -246,7 +247,7 @@
336
337 void Session::removeSurface(MirSurfaceInterface* surface)
338 {
339- qCDebug(QTMIR_SESSIONS) << "Session::removeSurface - session=" << name() << "surface=" << surface;
340+ DEBUG_MSG << "(surface=" << surface << ")";
341
342 surface->disconnect(this);
343
344@@ -277,8 +278,8 @@
345
346 void Session::setFullscreen(bool fullscreen)
347 {
348- qCDebug(QTMIR_SESSIONS) << "Session::setFullscreen - session=" << this << "fullscreen=" << fullscreen;
349 if (m_fullscreen != fullscreen) {
350+ DEBUG_MSG << "(" << fullscreen << ")";
351 m_fullscreen = fullscreen;
352 Q_EMIT fullscreenChanged(m_fullscreen);
353 }
354@@ -286,7 +287,7 @@
355
356 void Session::suspend()
357 {
358- qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << sessionStateToString(m_state);
359+ DEBUG_MSG << " state=" << sessionStateToString(m_state);
360 if (m_state == Running) {
361 session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
362 m_suspendTimer->start();
363@@ -305,7 +306,7 @@
364
365 void Session::resume()
366 {
367- qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << sessionStateToString(m_state);
368+ DEBUG_MSG << " state=" << sessionStateToString(m_state);
369
370 if (m_state == Suspending || m_state == Suspended) {
371 doResume();
372@@ -336,7 +337,7 @@
373
374 void Session::close()
375 {
376- qCDebug(QTMIR_SESSIONS) << "Session::close - " << name();
377+ DEBUG_MSG << "()";
378
379 if (m_state == Stopped) return;
380
381@@ -348,7 +349,7 @@
382
383 void Session::stop()
384 {
385- qCDebug(QTMIR_SESSIONS) << "Session::stop - " << name();
386+ DEBUG_MSG << "()";
387
388 if (m_state != Stopped) {
389
390@@ -372,7 +373,7 @@
391 void Session::setLive(const bool live)
392 {
393 if (m_live != live) {
394- qCDebug(QTMIR_SESSIONS) << "Session::setLive - " << name() << "live=" << live;
395+ DEBUG_MSG << "(" << live << ")";
396
397 m_live = live;
398 Q_EMIT liveChanged(m_live);
399@@ -396,7 +397,7 @@
400
401 void Session::insertChildSession(uint index, SessionInterface* session)
402 {
403- qCDebug(QTMIR_SESSIONS) << "Session::insertChildSession - " << session->name() << " to " << name() << " @ " << index;
404+ DEBUG_MSG << "(index=" << index << ", Session[" << (void*)session << ",name=" << session->name() << "])";
405 Q_ASSERT(!m_children->contains(session));
406
407 m_children->insert(index, session);
408@@ -424,7 +425,7 @@
409
410 void Session::removeChildSession(SessionInterface* session)
411 {
412- qCDebug(QTMIR_SESSIONS) << "Session::removeChildSession - " << session->name() << " from " << name();
413+ DEBUG_MSG << "(Session[" << (void*)session << ",name=" << session->name() << "])";
414
415 disconnect(session, 0, this, 0);
416
417@@ -452,16 +453,14 @@
418
419 void Session::appendPromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
420 {
421- qCDebug(QTMIR_SESSIONS) << "Session::appendPromptSession session=" << name()
422- << "promptSession=" << (promptSession ? promptSession.get() : nullptr);
423+ DEBUG_MSG << "(promptSession=" << (promptSession ? promptSession.get() : nullptr) << ")";
424
425 m_promptSessions.append(promptSession);
426 }
427
428 void Session::removePromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
429 {
430- qCDebug(QTMIR_SESSIONS) << "Session::removePromptSession session=" << name()
431- << "promptSession=" << (promptSession ? promptSession.get() : nullptr);
432+ DEBUG_MSG << "(promptSession=" << (promptSession ? promptSession.get() : nullptr) << ")";
433
434 m_promptSessions.removeAll(promptSession);
435 }
436@@ -477,7 +476,7 @@
437 QListIterator<std::shared_ptr<ms::PromptSession>> it(copy);
438 for ( it.toBack(); it.hasPrevious(); ) {
439 std::shared_ptr<ms::PromptSession> promptSession = it.previous();
440- qCDebug(QTMIR_SESSIONS) << "Session::stopPromptSessions - promptSession=" << promptSession.get();
441+ DEBUG_MSG << " - promptSession=" << promptSession.get();
442
443 m_promptSessionManager->stop_prompt_session(promptSession);
444 }
445@@ -500,7 +499,7 @@
446 void Session::deleteIfZombieAndEmpty()
447 {
448 if (!m_live && m_children->rowCount() == 0 && m_surfaceList.isEmpty()) {
449- qCDebug(QTMIR_SESSIONS).nospace() << "Session::deleteIfZombieAndEmpty[" << name() << "] - deleteLater()";
450+ DEBUG_MSG << " - deleteLater()";
451 deleteLater();
452 }
453 }
454
455=== modified file 'src/modules/Unity/Application/taskcontroller.h'
456--- src/modules/Unity/Application/taskcontroller.h 2016-04-22 14:03:26 +0000
457+++ src/modules/Unity/Application/taskcontroller.h 2016-06-16 20:19:32 +0000
458@@ -1,5 +1,5 @@
459 /*
460- * Copyright (C) 2014-2015 Canonical, Ltd.
461+ * Copyright (C) 2014-2016 Canonical, Ltd.
462 *
463 * This program is free software: you can redistribute it and/or modify it under
464 * the terms of the GNU Lesser General Public License version 3, as published by
465@@ -54,6 +54,10 @@
466
467 virtual QSharedPointer<qtmir::ApplicationInfo> getInfoForApp(const QString &appId) const = 0;
468
469+ // Not a perfect fit for this class but we need the kill(pid_t pid, int sig) function
470+ // behind an interface so we can test it (replacing with a mock or fake)
471+ virtual void kill(pid_t pid) = 0;
472+
473 Q_SIGNALS:
474 void processStarting(const QString &appId);
475 void applicationStarted(const QString &appId);
476
477=== modified file 'src/modules/Unity/Application/upstart/taskcontroller.cpp'
478--- src/modules/Unity/Application/upstart/taskcontroller.cpp 2016-05-04 16:35:50 +0000
479+++ src/modules/Unity/Application/upstart/taskcontroller.cpp 2016-06-16 20:19:32 +0000
480@@ -30,6 +30,9 @@
481 }
482 #include <ubuntu-app-launch/registry.h>
483
484+// std
485+#include <csignal>
486+
487 namespace ual = ubuntu::app_launch;
488
489 namespace qtmir
490@@ -243,5 +246,10 @@
491 return QSharedPointer<qtmir::ApplicationInfo>(appInfo);
492 }
493
494+void TaskController::kill(pid_t pid)
495+{
496+ ::kill(pid, SIGTERM);
497+}
498+
499 } // namespace upstart
500 } // namespace qtmir
501
502=== modified file 'src/modules/Unity/Application/upstart/taskcontroller.h'
503--- src/modules/Unity/Application/upstart/taskcontroller.h 2016-04-22 14:03:26 +0000
504+++ src/modules/Unity/Application/upstart/taskcontroller.h 2016-06-16 20:19:32 +0000
505@@ -41,6 +41,8 @@
506
507 QSharedPointer<qtmir::ApplicationInfo> getInfoForApp(const QString &appId) const override;
508
509+ void kill(pid_t pid) override;
510+
511 private:
512 struct Private;
513 QScopedPointer<Private> impl;
514
515=== modified file 'tests/framework/mock_task_controller.h'
516--- tests/framework/mock_task_controller.h 2016-04-22 14:03:26 +0000
517+++ tests/framework/mock_task_controller.h 2016-06-16 20:19:32 +0000
518@@ -38,6 +38,7 @@
519 MOCK_METHOD2(start, bool(const QString&, const QStringList&));
520 MOCK_METHOD1(suspend, bool(const QString&));
521 MOCK_METHOD1(resume, bool(const QString&));
522+ MOCK_METHOD1(kill, void(pid_t pid));
523
524 bool doAppIdHasProcessId(const QString& appId, pid_t pid);
525
526
527=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
528--- tests/modules/ApplicationManager/application_manager_test.cpp 2016-05-03 15:27:26 +0000
529+++ tests/modules/ApplicationManager/application_manager_test.cpp 2016-06-16 20:19:32 +0000
530@@ -1900,3 +1900,176 @@
531
532 EXPECT_EQ(1, focusRequestedSpy.count());
533 }
534+
535+/*
536+ * Context:
537+ * - we are waiting for an application to quit following a close()
538+ *
539+ * Event:
540+ * - TaskController requests that application to be resumed.
541+ * Most likely because the user tapped the app icon in the Dash. From the user's
542+ * perspective the application is already closed and he wants to start a new
543+ * instance of it.
544+ * But from TaskController's perspective the application is still running and
545+ * therefore should just be resumed/focused.
546+ *
547+ * Expected outcome:
548+ * - ApplicationManager should wait for the closing application to finally end (or
549+ be killed) and then finally launch a new instance of it.
550+ *
551+ * Regression test for https://bugs.launchpad.net/ubuntu/+source/qtmir/+bug/1527737
552+ * "Apps do not start if restarted quickly after closing"
553+ *
554+ * Two test versions:
555+ * _appQuits - Application complies and quits
556+ * _appKilled - Application ignores close request and thus get killed
557+ */
558+TEST_F(ApplicationManagerTests, focusRequestedForClosingApplication_appQuits)
559+{
560+ using namespace ::testing;
561+
562+ int argc = 0;
563+ char* argv[0];
564+ QCoreApplication qtApp(argc, argv); // app for deleteLater event
565+
566+ const QString appId("testAppId");
567+ quint64 procId = 5551;
568+
569+ ON_CALL(*taskController, appIdHasProcessId(appId, procId)).WillByDefault(Return(true));
570+
571+ EXPECT_CALL(*taskController, start(appId, _))
572+ .Times(1)
573+ .WillOnce(Return(true));
574+
575+ auto app = applicationManager.startApplication(appId);
576+ applicationManager.onProcessStarting(appId);
577+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
578+ bool authed = true;
579+ applicationManager.authorizeSession(procId, authed);
580+ ASSERT_TRUE(authed);
581+ onSessionStarting(session);
582+
583+ FakeMirSurface *surface = new FakeMirSurface;
584+ onSessionCreatedSurface(session.get(), surface);
585+ surface->drawFirstFrame();
586+
587+ ASSERT_EQ(Application::InternalState::Running, app->internalState());
588+
589+ QSignalSpy closeRequestedSpy(surface, SIGNAL(closeRequested()));
590+
591+ // User swipes away the application, visually closing/removing it.
592+ applicationManager.stopApplication(appId);
593+
594+ // But asking ApplicationManager to stop the application just makes it request its
595+ // surfaces to be closed
596+ EXPECT_EQ(Application::InternalState::Closing, app->internalState());
597+ EXPECT_EQ(1, closeRequestedSpy.count());
598+
599+ // User then taps on the app icon
600+ applicationManager.onProcessStarting(appId);
601+ applicationManager.onFocusRequested(appId);
602+
603+ // But no application instance should have been created because of that
604+ EXPECT_TRUE(applicationManager.findApplication(appId) == nullptr);
605+
606+ // ApplicationManager will ask TaskController to start the application again
607+ // once the closing instance is gone
608+ Mock::VerifyAndClearExpectations(taskController);
609+ EXPECT_CALL(*taskController, start(appId, _))
610+ .Times(1)
611+ .WillOnce(Return(true));
612+
613+ // Simulates that the application complied to the close() request and stopped itself
614+ onSessionStopping(session);
615+ applicationManager.onProcessStopped(appId);
616+
617+ // DeferredDelete is special: likes to be called out specifically or it won't come out
618+ qtApp.sendPostedEvents(app, QEvent::DeferredDelete);
619+ qtApp.sendPostedEvents();
620+
621+ EXPECT_TRUE(applicationManager.findApplication(appId) != nullptr);
622+}
623+TEST_F(ApplicationManagerTests, focusRequestedForClosingApplication_appKilled)
624+{
625+ using namespace ::testing;
626+
627+ int argc = 0;
628+ char* argv[0];
629+ QCoreApplication qtApp(argc, argv); // app for deleteLater event
630+
631+ const QString appId("testAppId");
632+ quint64 procId = 5551;
633+
634+ ON_CALL(*taskController, appIdHasProcessId(appId, procId)).WillByDefault(Return(true));
635+
636+ EXPECT_CALL(*taskController, start(appId, _))
637+ .Times(1)
638+ .WillOnce(Return(true));
639+
640+ auto app = applicationManager.startApplication(appId);
641+ applicationManager.onProcessStarting(appId);
642+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
643+ bool authed = true;
644+ applicationManager.authorizeSession(procId, authed);
645+ ASSERT_TRUE(authed);
646+ onSessionStarting(session);
647+
648+ FakeMirSurface *surface = new FakeMirSurface;
649+ onSessionCreatedSurface(session.get(), surface);
650+ surface->drawFirstFrame();
651+
652+ EXPECT_EQ(Application::InternalState::Running, app->internalState());
653+
654+ QSharedPointer<FakeTimeSource> fakeTimeSource(new FakeTimeSource);
655+ FakeTimer *fakeStopTimer = new FakeTimer(fakeTimeSource);
656+ app->setStopTimer(fakeStopTimer);
657+
658+ QSignalSpy closeRequestedSpy(surface, SIGNAL(closeRequested()));
659+
660+ // User swipes away the application, visually closing/removing it.
661+ applicationManager.stopApplication(appId);
662+
663+ // But asking ApplicationManager to stop the application just makes it request its
664+ // surfaces to be closed
665+ EXPECT_EQ(Application::InternalState::Closing, app->internalState());
666+ EXPECT_EQ(1, closeRequestedSpy.count());
667+
668+ // User then taps on the app icon
669+ applicationManager.onProcessStarting(appId);
670+ applicationManager.onFocusRequested(appId);
671+
672+ // But no application instance should have been created because of that
673+ EXPECT_TRUE(applicationManager.findApplication(appId) == nullptr);
674+
675+ // ApplicationManager will ask TaskController to start the application again
676+ // once the closing instance is gone
677+ Mock::VerifyAndClearExpectations(taskController);
678+ EXPECT_CALL(*taskController, start(appId, _))
679+ .Times(1)
680+ .WillOnce(Return(true));
681+
682+ EXPECT_CALL(*taskController, stop(appId))
683+ .Times(1)
684+ .WillOnce(Return(false));
685+
686+ EXPECT_CALL(*taskController, kill(procId))
687+ .Times(1)
688+ .WillOnce(Invoke(
689+ [this, session](pid_t) {
690+ // simulate application dying and thus severing the mir connection
691+ onSessionStopping(session);
692+ }
693+ ));
694+
695+ if (fakeStopTimer->isRunning()) {
696+ // Simulate that it has timed out.
697+ fakeTimeSource->m_msecsSinceReference = fakeStopTimer->nextTimeoutTime() + 1;
698+ fakeStopTimer->update();
699+ }
700+
701+ // DeferredDelete is special: likes to be called out specifically or it won't come out
702+ qtApp.sendPostedEvents(app, QEvent::DeferredDelete);
703+ qtApp.sendPostedEvents();
704+
705+ EXPECT_TRUE(applicationManager.findApplication(appId) != nullptr);
706+}

Subscribers

People subscribed via source and target branches