Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,20 @@ Add it to the kernel command line of your boot loader, reboot, and switch the
power supply off once so the display recovers. Details and measurements are in
issue #28.

### Display stops answering during Loop or Shuffle playback

With Loop or Shuffle, the runtime hands the playlist to the display, and the
firmware switches media on its own. On a Panorama SE `391a:1021` with firmware
v2.0.6 and a carousel of still images, the display stopped answering anywhere
from half an hour to several hours into a session. It stays on USB but no
longer accepts data, and the runtime reports that the keepalive stopped after
3 retries. Restarting the runtime or rebinding the USB device does not help;
only switching the power supply off does. One still image ran for more than
10 hours on the same setup.

Until TRYX fixes the firmware, use Single playback with one image or video.
Details are in issue #37.

### The display session never starts after the first media upload

Before 2.5.3 the runtime created its directories with permissions from the
Expand Down
22 changes: 11 additions & 11 deletions src/portalusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,12 @@ Access::Access(Registry &registry, QObject *parent) : Access(registry, Options{}
Access::Access(Registry &registry, const Options &options, QObject *parent)
: QObject(parent), registry_(registry), options_(options), bus_(QString()) {
registerTypes();
permissionDeadline_.setSingleShot(true);
connect(&permissionDeadline_, &QTimer::timeout, this, [this]() {
logPortalEvent(QStringLiteral("acquire_timeout"));
fail(tr("The USB permission request timed out. Restart the Flatpak background runtime to try again."));
// A permission dialog that nobody answered after boot used to time out
// and leave the runtime without USB access until it was restarted (#37).
// The dialog now stays open until the user allows or denies access.
pendingNotice_.setSingleShot(true);
connect(&pendingNotice_, &QTimer::timeout, this, [this]() {
logPortalEvent(QStringLiteral("acquire_still_pending"));
});
connectionCheck_.setInterval(250);
connect(&connectionCheck_, &QTimer::timeout, this, [this]() {
Expand Down Expand Up @@ -440,17 +442,15 @@ bool Access::maybeAcquire() {
// fail() already published the terminal state.
return true;
}
permissionDeadline_.start(qMax(1, options_.permissionTimeoutMs));
pendingNotice_.start(qMax(1, options_.pendingNoticeMs));
{
QMutexLocker lock(&registry_.mutex_);
// A new open request supersedes an earlier denial text.
registry_.acquisitionPending_ = true;
registry_.error_.clear();
}
logPortalEvent(QStringLiteral("acquire_requested"),
{{QStringLiteral("product"), printerProductIdString(device.productId)},
{QStringLiteral("timeout_ms"),
QString::number(qMax(1, options_.permissionTimeoutMs))}});
{{QStringLiteral("product"), printerProductIdString(device.productId)}});
const QString request = requestPath_;
call(owner_, kDesktopPath, kUsbInterface, QStringLiteral("AcquireDevices"),
{QString(), QVariant::fromValue(Devices{{device.id, {{QStringLiteral("writable"), true}}}}),
Expand Down Expand Up @@ -499,7 +499,7 @@ void Access::processAcquisitionResponse(uint response) {
logPortalEvent(QStringLiteral("acquire_response"),
{{QStringLiteral("code"), QString::number(response)}});
if (response != 0) {
permissionDeadline_.stop();
pendingNotice_.stop();
requestPath_.clear();
pendingId_.clear();
pendingEndpoint_.clear();
Expand Down Expand Up @@ -579,7 +579,7 @@ void Access::finishAcquire() {
: QStringLiteral("acquire_discarded"));
if (!accepted)
releaseDevices({pendingId_});
permissionDeadline_.stop();
pendingNotice_.stop();
requestPath_.clear();
pendingId_.clear();
pendingEndpoint_.clear();
Expand Down Expand Up @@ -641,7 +641,7 @@ void Access::stop() {
running_ = false;
++generation_;
sessionReady_ = false;
permissionDeadline_.stop();
pendingNotice_.stop();
connectionCheck_.stop();
closeRemote(requestPath_, kRequestInterface);
closeRemote(sessionPath_, kSessionInterface);
Expand Down
6 changes: 4 additions & 2 deletions src/portalusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ class Access final : public QObject {
struct Options {
QString service = QStringLiteral("org.freedesktop.portal.Desktop");
int callTimeoutMs = 2000;
int permissionTimeoutMs = 120000;
// The permission request stays open until the user answers it; after
// this long the runtime only logs once that it is still waiting.
int pendingNoticeMs = 120000;
};
explicit Access(Registry &registry, QObject *parent = nullptr);
Access(Registry &registry, const Options &options, QObject *parent = nullptr);
Expand Down Expand Up @@ -128,7 +130,7 @@ private slots:
QString connectionName_;
QDBusConnection bus_;
QDBusServiceWatcher *watcher_ = nullptr;
QTimer permissionDeadline_;
QTimer pendingNotice_;
QTimer connectionCheck_;
QString owner_;
QString sessionPath_;
Expand Down
23 changes: 14 additions & 9 deletions tests/flatpak/portalusb_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,23 +311,28 @@ private slots:
QVERIFY(!registry.snapshot().monitoring);
}

void permissionTimeoutClosesRequestAndIgnoresLateSuccess() {
// An unanswered permission dialog stays open: a user who only allows
// access later, for example after login, still gets the device (#37).
void unansweredPermissionRequestStaysOpenUntilAllowed() {
FakePortal portal;
portal.replyAutomatically = false;
QVERIFY(portal.install());
Registry registry;
Access::Options options;
options.permissionTimeoutMs = 100;
options.pendingNoticeMs = 50;
Access access(registry, options);
QVERIFY(access.start());
QTRY_COMPARE(portal.acquisitions, 1);
QTRY_VERIFY(!registry.snapshot().error.isEmpty());
QTRY_VERIFY(portal.request.closed);
QVERIFY(!registry.snapshot().acquisitionPending);
QTest::qWait(200);
QVERIFY(!portal.request.closed);
QVERIFY(registry.snapshot().acquisitionPending);
QVERIFY(registry.snapshot().error.isEmpty());
QVERIFY(registry.snapshot().monitoring);
emit portal.request.Response(0, {});
QTest::qWait(25);
QCOMPARE(portal.finishes, 0);
QVERIFY(!registry.snapshot().monitoring);
QTRY_VERIFY(registry.snapshot().devices.size() == 1 &&
registry.snapshot().devices.first().granted);
QVERIFY(!registry.snapshot().acquisitionPending);
QCOMPARE(portal.acquisitions, 1);
}

void portalOwnerLossRevokesWithoutReacquiring() {
Expand Down Expand Up @@ -511,7 +516,7 @@ private slots:
"product=\"391a:1021\" readable=\"true\" writable=\"true\" "
"same_identity=\"false\" granted=\"false\"$"),
QStringLiteral("^tryx_usb_portal event=\"acquire_requested\" "
"product=\"391a:1021\" timeout_ms=\"[0-9]+\"$"),
"product=\"391a:1021\"$"),
QStringLiteral("^tryx_usb_portal event=\"acquire_acknowledged\"$"),
QStringLiteral("^tryx_usb_portal event=\"acquire_response\" code=\"0\"$"),
QStringLiteral("^tryx_usb_portal event=\"acquire_granted\"$"),
Expand Down