canonical / dbus.dart

Native Dart client library to use DBus.
https://pub.dev/packages/dbus
Mozilla Public License 2.0
93 stars 33 forks source link

[Flatpak] SocketException: Broken pipe (Write failed) and Invalid client serial #257

Closed proninyaroslav closed 3 years ago

proninyaroslav commented 3 years ago

This happens with Flatpak and a Flutter application if I call DBusClient.session() before calling the runApp() method.

Example (just show notification by button clicking) https://github.com/proninyaroslav/dbus_broken_pipe:

import 'package:dbus/dbus.dart';
import 'package:flutter/material.dart';

late final DBusRemoteObject object;

void main() {
  object = DBusRemoteObject(
    DBusClient.session(),
    'org.freedesktop.Notifications',
    DBusObjectPath('/org/freedesktop/Notifications'),
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'DBus Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('DBus Demo'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              await object.callMethod(
                'org.freedesktop.Notifications',
                'Notify',
                <DBusValue>[
                  // app_name
                  DBusString('Test'),
                  // replaces_id
                  DBusUint32(0),
                  // app_icon
                  DBusString(''),
                  // summary
                  DBusString('Summary'),
                  // body
                  DBusString('Body'),
                  // actions
                  DBusArray.string([]),
                  // hints
                  DBusDict.stringVariant({}),
                  // expire_timeout
                  DBusInt32(-1),
                ],
                replySignature: DBusSignature('u'),
              );
            },
            child: Text('Show notification'),
          ),
        ),
      ),
    );
  }
}

If you run this example just like flutter run or create Snap package, it will work fine. If you create the following Flatpak configuration, the built package will produce an error and notification is not displayed:

https://github.com/proninyaroslav/dbus_broken_pipe/tree/master/flatpak

$ cd flatpak
$ flatpak-builder --user --install build-dir org.test.flutter_application_1.yaml --force-clean
app-id: org.test.flutter_application_1
runtime: org.gnome.Platform
runtime-version: '40'
sdk: org.gnome.Sdk
command: flutter_application_1
separate-locales: false
finish-args:
  # Required due to being a GUI application
  - --socket=x11
  - --socket=fallback-x11
  # Required to make sure x11 performance is achived on all platforms
  # At least that's what the legends tell. it might be worth experimenting
  # with dropping this permission.
  - --share=ipc
  # Required for experimental wayland support
  - --socket=wayland
  # Required to provide notification functionality
  - --socket=pulseaudio
  - --device=all
  # Required for notifications in various desktop environments
  - --talk-name=org.freedesktop.Notifications
  - --talk-name=org.kde.StatusNotifierWatcher
  # Required due to KDE's special way of handling notifications
  - --own-name=org.kde.StatusNotifierItem-2-1
cleanup:
  - /include
  - /lib/cmake
  - /lib/pkgconfig
  - /lib/*.a
modules:
  - name: flutter_application_1
    buildsystem: simple
    only-arches:
      - x86_64
    build-commands:
      - mkdir -p /app
      - cp -r flutter_application_1 /app/flutter_application_1
      - chmod +x /app/flutter_application_1/flutter_application_1
      - mkdir -p /app/bin /app/lib
      - ln -s /app/flutter_application_1/flutter_application_1 /app/bin/flutter_application_1
      - install -Dm644 org.test.flutter_application_1.desktop /app/share/applications/org.test.flutter_application_1.desktop
    sources:
      - type: archive
        path: ./bundle.tar.gz
        dest: flutter_application_1
        strip-components: 0
      - type: file
        path: org.test.flutter_application_1.desktop

journalctl log (tested on Fedora 34):

xdg-dbus-proxy[13808]: Invalid client serial
org.test.flutter_application_1.desktop[13811]: [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: SocketException: Write failed (OS Error: Broken pipe, errno = 32), a>
org.test.flutter_application_1.desktop[13811]: #0      _NativeSocket.write (dart:io-patch/socket_patch.dart:1114:34)
org.test.flutter_application_1.desktop[13811]: #1      _RawSocket.write (dart:io-patch/socket_patch.dart:1759:15)
org.test.flutter_application_1.desktop[13811]: #2      _Socket._write (dart:io-patch/socket_patch.dart:2192:18)
org.test.flutter_application_1.desktop[13811]: #3      _SocketStreamConsumer.write (dart:io-patch/socket_patch.dart:1940:26)
org.test.flutter_application_1.desktop[13811]: #4      _SocketStreamConsumer.addStream.<anonymous closure> (dart:io-patch/socket_patch.dart:1914:11)
org.test.flutter_application_1.desktop[13811]: #5      _rootRunUnary (dart:async/zone.dart:1362:47)
org.test.flutter_application_1.desktop[13811]: #6      _CustomZone.runUnary (dart:async/zone.dart:1265:19)
org.test.flutter_application_1.desktop[13811]: #7      _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
org.test.flutter_application_1.desktop[13811]: #8      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
org.test.flutter_application_1.desktop[13811]: #9      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
org.test.flutter_application_1.desktop[13811]: #10     _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:733:19)
org.test.flutter_application_1.desktop[13811]: #11     _StreamController._add (dart:async/stream_controller.dart:607:7)
org.test.flutter_application_1.desktop[13811]: #12     _StreamController.add (dart:async/stream_controller.dart:554:5)
org.test.flutter_application_1.desktop[13811]: #13     _StreamSinkImpl.add (dart:io/io_sink.dart:136:17)
org.test.flutter_application_1.desktop[13811]: #14     _Socket.add (dart:io-patch/socket_patch.dart:2039:38)
org.test.flutter_application_1.desktop[13811]: #15     DBusClient._sendMessage (package:dbus/src/dbus_client.dart:1129:14)
org.test.flutter_application_1.desktop[13811]: <asynchronous suspension>
org.test.flutter_application_1.desktop[13811]: #16     DBusClient._callMethod (package:dbus/src/dbus_client.dart:1059:5)
org.test.flutter_application_1.desktop[13811]: <asynchronous suspension>
org.test.flutter_application_1.desktop[13811]: #17     DBusClient.callMethod (package:dbus/src/dbus_client.dart:563:12)
org.test.flutter_application_1.desktop[13811]: <asynchronous suspension>
org.test.flutter_application_1.desktop[13811]: #18     DBusClient.getNameOwner (package:dbus/src/dbus_client.dart:401:16)
org.test.flutter_application_1.desktop[13811]: <asynchronous suspension>
org.test.flutter_application_1.desktop[13811]: #19     DBusClient._findUniqueName (package:dbus/src/dbus_client.dart:579:22)
org.test.flutter_application_1.desktop[13811]: <asynchronous suspension>

Session bus is used:

$ flatpak enter org.test.flutter_application_1 env|grep DBUS
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

The xdg-dbus-proxy[13808]: Invalid client serial error refers to the follow section of xdg-dbus-proxy, used by Flatpak to proxy and filtering DBus calls (for example, --talk-name=org.freedesktop.Notifications build option): https://github.com/flatpak/xdg-dbus-proxy/blob/master/flatpak-proxy.c#L2160

/* Make sure the client is not playing games with the serials, as that
   could confuse us. */
if (header->serial <= client->last_serial)
  {
    g_warning ("Invalid client serial");
    side_closed (side);
    buffer_unref (buffer);
    return;
  }

The only solution at the moment is to use the --socket=session-bus option during Flatpak build, or to call DBusClient.session() after Flutter is fully initialized or at the moment of using DBus.

robert-ancell commented 3 years ago

Ah, I think what is happening is this.

This means the serial numbers are not in order for the first method call.

proninyaroslav commented 3 years ago

@robert-ancell Thank you, works fine.