Silence a bunch of -Wunused-but-set-variable warnings
[platform/upstream/glib.git] / gio / gdbusproxy.c
index fa2db2f..67af1fb 100644 (file)
  * name is tracked and can be read from
  * #GDBusProxy:g-name-owner. Connect to the #GObject::notify signal to
  * get notified of changes. Additionally, only signals and property
- * changes emitted from the current name owner are considered. This
- * avoids a number of race conditions when the name is lost by one
- * owner and claimed by another.
+ * changes emitted from the current name owner are considered and
+ * calls are always sent to the current name owner. This avoids a
+ * number of race conditions when the name is lost by one owner and
+ * claimed by another. However, if no name owner currently exists,
+ * then calls will be sent to the well-known name which may result in
+ * the message bus launching an owner (unless
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is set).
  *
  * The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal
  * signals are not very convenient to work with. Therefore, the recommended
@@ -152,7 +156,8 @@ g_dbus_proxy_finalize (GObject *object)
     g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
                                           proxy->priv->signals_subscriber_id);
 
-  g_object_unref (proxy->priv->connection);
+  if (proxy->priv->connection != NULL)
+    g_object_unref (proxy->priv->connection);
   g_free (proxy->priv->name);
   g_free (proxy->priv->name_owner);
   g_free (proxy->priv->object_path);
@@ -161,7 +166,10 @@ g_dbus_proxy_finalize (GObject *object)
     g_hash_table_unref (proxy->priv->properties);
 
   if (proxy->priv->expected_interface != NULL)
-    g_dbus_interface_info_unref (proxy->priv->expected_interface);
+    {
+      g_dbus_interface_info_cache_release (proxy->priv->expected_interface);
+      g_dbus_interface_info_unref (proxy->priv->expected_interface);
+    }
 
   G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
 }
@@ -539,6 +547,13 @@ g_dbus_proxy_init (GDBusProxy *proxy)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gint
+property_name_sort_func (const gchar **a,
+                         const gchar **b)
+{
+  return g_strcmp0 (*a, *b);
+}
+
 /**
  * g_dbus_proxy_get_cached_property_names:
  * @proxy: A #GDBusProxy.
@@ -569,7 +584,7 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy  *proxy)
   g_hash_table_iter_init (&iter, proxy->priv->properties);
   while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
     g_ptr_array_add (p, g_strdup (key));
-  g_ptr_array_sort (p, (GCompareFunc) g_strcmp0);
+  g_ptr_array_sort (p, (GCompareFunc) property_name_sort_func);
   g_ptr_array_add (p, NULL);
 
   names = (gchar **) g_ptr_array_free (p, FALSE);
@@ -628,8 +643,7 @@ g_dbus_proxy_get_cached_property (GDBusProxy   *proxy,
   value = g_hash_table_lookup (proxy->priv->properties, property_name);
   if (value == NULL)
     {
-      const GDBusPropertyInfo *info;
-      info = lookup_property_info_or_warn (proxy, property_name);
+      lookup_property_info_or_warn (proxy, property_name);
       /* no difference */
       goto out;
     }
@@ -644,7 +658,7 @@ g_dbus_proxy_get_cached_property (GDBusProxy   *proxy,
  * g_dbus_proxy_set_cached_property:
  * @proxy: A #GDBusProxy
  * @property_name: Property name.
- * @value: Value for the property or %NULL to remove it from the cache.
+ * @value: (allow-none): Value for the property or %NULL to remove it from the cache.
  *
  * If @value is not %NULL, sets the cached value for the property with
  * name @property_name to the value in @value.
@@ -737,9 +751,25 @@ on_signal_received (GDBusConnection *connection,
   if (!proxy->priv->initialized)
     goto out;
 
-  if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
+  if (proxy->priv->name_owner != NULL && g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
     goto out;
 
+  if (proxy->priv->expected_interface != NULL)
+    {
+      const GDBusSignalInfo *info;
+      GVariantType *expected_type;
+      info = g_dbus_interface_info_lookup_signal (proxy->priv->expected_interface, signal_name);
+      if (info == NULL)
+        goto out;
+      expected_type = _g_dbus_compute_complete_signature (info->args);
+      if (!g_variant_type_equal (expected_type, g_variant_get_type (parameters)))
+        {
+          g_variant_type_free (expected_type);
+          goto out;
+        }
+      g_variant_type_free (expected_type);
+    }
+
   g_signal_emit (proxy,
                  signals[SIGNAL_SIGNAL],
                  0,
@@ -753,6 +783,36 @@ on_signal_received (GDBusConnection *connection,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+insert_property_checked (GDBusProxy  *proxy,
+                        gchar *property_name,
+                        GVariant *value)
+{
+  if (proxy->priv->expected_interface != NULL)
+    {
+      const GDBusPropertyInfo *info;
+
+      info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name);
+      /* Ignore unknown properties */
+      if (info == NULL)
+       goto invalid;
+
+      /* Ignore properties with the wrong type */
+      if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0)
+       goto invalid;
+    }
+
+  g_hash_table_insert (proxy->priv->properties,
+                      property_name, /* adopts string */
+                      value); /* adopts value */
+
+  return;
+
+ invalid:
+  g_variant_unref (value);
+  g_free (property_name);
+}
+
+static void
 on_properties_changed (GDBusConnection *connection,
                        const gchar     *sender_name,
                        const gchar     *object_path,
@@ -762,7 +822,6 @@ on_properties_changed (GDBusConnection *connection,
                        gpointer         user_data)
 {
   GDBusProxy *proxy = G_DBUS_PROXY (user_data);
-  GError *error;
   const gchar *interface_name_for_signal;
   GVariant *changed_properties;
   gchar **invalidated_properties;
@@ -771,14 +830,13 @@ on_properties_changed (GDBusConnection *connection,
   GVariant *value;
   guint n;
 
-  error = NULL;
   changed_properties = NULL;
   invalidated_properties = NULL;
 
   if (!proxy->priv->initialized)
     goto out;
 
-  if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
+  if (proxy->priv->name_owner != NULL && g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
     goto out;
 
   if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
@@ -800,9 +858,9 @@ on_properties_changed (GDBusConnection *connection,
   g_variant_iter_init (&iter, changed_properties);
   while (g_variant_iter_next (&iter, "{sv}", &key, &value))
     {
-      g_hash_table_insert (proxy->priv->properties,
-                           key, /* adopts string */
-                           value); /* adopts value */
+      insert_property_checked (proxy,
+                              key, /* adopts string */
+                              value); /* adopts value */
     }
 
   for (n = 0; invalidated_properties[n] != NULL; n++)
@@ -842,9 +900,9 @@ process_get_all_reply (GDBusProxy *proxy,
   g_variant_get (result, "(a{sv})", &iter);
   while (g_variant_iter_next (iter, "{sv}", &key, &value))
     {
-      g_hash_table_insert (proxy->priv->properties,
-                           key, /* adopts string */
-                           value); /* adopts value */
+      insert_property_checked (proxy,
+                              key, /* adopts string */
+                              value); /* adopts value */
     }
   g_variant_iter_free (iter);
 
@@ -1129,8 +1187,7 @@ async_init_get_name_owner_cb (GDBusConnection *connection,
             }
           else
             {
-              g_simple_async_result_set_from_error (data->simple, error);
-              g_error_free (error);
+              g_simple_async_result_take_error (data->simple, error);
               g_simple_async_result_complete_in_idle (data->simple);
               async_init_data_free (data);
               goto out;
@@ -1254,8 +1311,7 @@ async_init_start_service_by_name_cb (GDBusConnection *connection,
 
  failed:
   g_warn_if_fail (error != NULL);
-  g_simple_async_result_set_from_error (data->simple, error);
-  g_error_free (error);
+  g_simple_async_result_take_error (data->simple, error);
   g_simple_async_result_complete_in_idle (data->simple);
   async_init_data_free (data);
 }
@@ -1366,6 +1422,7 @@ async_initable_init_first (GAsyncInitable *initable)
                                             "PropertiesChanged",
                                             proxy->priv->object_path,
                                             proxy->priv->interface_name,
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
                                             on_properties_changed,
                                             proxy,
                                             NULL);
@@ -1381,6 +1438,7 @@ async_initable_init_first (GAsyncInitable *initable)
                                             NULL,                        /* member */
                                             proxy->priv->object_path,
                                             NULL,                        /* arg0 */
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
                                             on_signal_received,
                                             proxy,
                                             NULL);
@@ -1395,6 +1453,7 @@ async_initable_init_first (GAsyncInitable *initable)
                                             "NameOwnerChanged",      /* signal name */
                                             "/org/freedesktop/DBus", /* path */
                                             proxy->priv->name,       /* arg0 */
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
                                             on_name_owner_changed,
                                             proxy,
                                             NULL);
@@ -1439,8 +1498,7 @@ get_connection_cb (GObject       *source_object,
                                           data->callback,
                                           data->user_data,
                                           NULL);
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
       g_simple_async_result_complete_in_idle (simple);
       g_object_unref (simple);
     }
@@ -1601,8 +1659,8 @@ initable_iface_init (GInitableIface *initable_iface)
  * g_dbus_proxy_new:
  * @connection: A #GDBusConnection.
  * @flags: Flags used when constructing the proxy.
- * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
- * @name: A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
  * @object_path: An object path.
  * @interface_name: A D-Bus interface name.
  * @cancellable: A #GCancellable or %NULL.
@@ -1612,16 +1670,16 @@ initable_iface_init (GInitableIface *initable_iface)
  * Creates a proxy for accessing @interface_name on the remote object
  * at @object_path owned by @name at @connection and asynchronously
  * loads D-Bus properties unless the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to
  * the #GDBusProxy::g-properties-changed signal to get notified about
  * property changes.
  *
- * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
+ * If the %G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
  * to handle signals from the remote object.
  *
  * If @name is a well-known name and the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
  * owner currently exists, the message bus will be requested to launch
  * a name owner for the name.
  *
@@ -1701,24 +1759,24 @@ g_dbus_proxy_new_finish (GAsyncResult  *res,
  * g_dbus_proxy_new_sync:
  * @connection: A #GDBusConnection.
  * @flags: Flags used when constructing the proxy.
- * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
- * @name: A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
  * @object_path: An object path.
  * @interface_name: A D-Bus interface name.
- * @cancellable: A #GCancellable or %NULL.
- * @error: Return location for error or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: (allow-none): Return location for error or %NULL.
  *
  * Creates a proxy for accessing @interface_name on the remote object
  * at @object_path owned by @name at @connection and synchronously
  * loads D-Bus properties unless the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used.
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used.
  *
- * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
+ * If the %G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
  * to handle signals from the remote object.
  *
  * If @name is a well-known name and the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
  * owner currently exists, the message bus will be requested to launch
  * a name owner for the name.
  *
@@ -1771,7 +1829,7 @@ g_dbus_proxy_new_sync (GDBusConnection     *connection,
  * g_dbus_proxy_new_for_bus:
  * @bus_type: A #GBusType.
  * @flags: Flags used when constructing the proxy.
- * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
  * @name: A bus name (well-known or unique).
  * @object_path: An object path.
  * @interface_name: A D-Bus interface name.
@@ -1836,7 +1894,8 @@ g_dbus_proxy_new_for_bus_finish (GAsyncResult  *res,
  * g_dbus_proxy_new_for_bus_sync:
  * @bus_type: A #GBusType.
  * @flags: Flags used when constructing the proxy.
- * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface
+ *        that @proxy conforms to or %NULL.
  * @name: A bus name (well-known or unique).
  * @object_path: An object path.
  * @interface_name: A D-Bus interface name.
@@ -1891,7 +1950,7 @@ g_dbus_proxy_new_for_bus_sync (GBusType             bus_type,
  *
  * Gets the connection @proxy is for.
  *
- * Returns: A #GDBusConnection owned by @proxy. Do not free.
+ * Returns: (transfer none): A #GDBusConnection owned by @proxy. Do not free.
  *
  * Since: 2.26
  */
@@ -2063,7 +2122,7 @@ g_dbus_proxy_get_interface_info (GDBusProxy *proxy)
 /**
  * g_dbus_proxy_set_interface_info:
  * @proxy: A #GDBusProxy
- * @info: Minimum interface this proxy conforms to or %NULL to unset.
+ * @info: (allow-none): Minimum interface this proxy conforms to or %NULL to unset.
  *
  * Ensure that interactions with @proxy conform to the given
  * interface.  For example, when completing a method call, if the type
@@ -2081,8 +2140,13 @@ g_dbus_proxy_set_interface_info (GDBusProxy         *proxy,
 {
   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
   if (proxy->priv->expected_interface != NULL)
-    g_dbus_interface_info_unref (proxy->priv->expected_interface);
+    {
+      g_dbus_interface_info_cache_release (proxy->priv->expected_interface);
+      g_dbus_interface_info_unref (proxy->priv->expected_interface);
+    }
   proxy->priv->expected_interface = info != NULL ? g_dbus_interface_info_ref (info) : NULL;
+  if (proxy->priv->expected_interface != NULL)
+    g_dbus_interface_info_cache_build (proxy->priv->expected_interface);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -2134,9 +2198,7 @@ reply_cb (GDBusConnection *connection,
                                          &error);
   if (error != NULL)
     {
-      g_simple_async_result_set_from_error (simple,
-                                            error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
     }
   else
     {
@@ -2147,6 +2209,7 @@ reply_cb (GDBusConnection *connection,
 
   /* no need to complete in idle since the method GDBusConnection already does */
   g_simple_async_result_complete (simple);
+  g_object_unref (simple);
 }
 
 static const GDBusMethodInfo *
@@ -2168,13 +2231,39 @@ lookup_method_info_or_warn (GDBusProxy  *proxy,
   return info;
 }
 
+static const gchar *
+get_destination_for_call (GDBusProxy *proxy)
+{
+  const gchar *ret;
+
+  ret = NULL;
+
+  /* If proxy->priv->name is a unique name, then proxy->priv->name_owner
+   * is never NULL and always the same as proxy->priv->name. We use this
+   * knowledge to avoid checking if proxy->priv->name is a unique or
+   * well-known name.
+   */
+  ret = proxy->priv->name_owner;
+  if (ret != NULL)
+    goto out;
+
+  if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
+    goto out;
+
+  ret = proxy->priv->name;
+
+ out:
+  return ret;
+}
+
 /**
  * g_dbus_proxy_call:
  * @proxy: A #GDBusProxy.
  * @method_name: Name of method to invoke.
- * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
  * care about the result of the method invocation.
@@ -2232,9 +2321,9 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   gboolean was_split;
   gchar *split_interface_name;
   const gchar *split_method_name;
-  const GDBusMethodInfo *expected_method_info;
   const gchar *target_method_name;
   const gchar *target_interface_name;
+  const gchar *destination;
   GVariantType *reply_type;
 
   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
@@ -2242,6 +2331,9 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
   g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
 
+  reply_type = NULL;
+  split_interface_name = NULL;
+
   simple = g_simple_async_result_new (G_OBJECT (proxy),
                                       callback,
                                       user_data,
@@ -2251,20 +2343,31 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   target_method_name = was_split ? split_method_name : method_name;
   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
 
-  g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free);
-
   /* Warn if method is unexpected (cf. :g-interface-info) */
-  expected_method_info = NULL;
   if (!was_split)
-    expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+    {
+      const GDBusMethodInfo *expected_method_info;
+      expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+      if (expected_method_info != NULL)
+        reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
+    }
 
-  if (expected_method_info)
-    reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
-  else
-    reply_type = NULL;
+  destination = NULL;
+  if (proxy->priv->name != NULL)
+    {
+      destination = get_destination_for_call (proxy);
+      if (destination == NULL)
+        {
+          g_simple_async_result_set_error (simple,
+                                           G_IO_ERROR,
+                                           G_IO_ERROR_FAILED,
+                                           _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+          goto out;
+        }
+    }
 
   g_dbus_connection_call (proxy->priv->connection,
-                          proxy->priv->name_owner,
+                          destination,
                           proxy->priv->object_path,
                           target_interface_name,
                           target_method_name,
@@ -2276,6 +2379,7 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
                           (GAsyncReadyCallback) reply_cb,
                           simple);
 
+ out:
   if (reply_type != NULL)
     g_variant_type_free (reply_type);
 
@@ -2302,7 +2406,6 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
 {
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   GVariant *value;
-  const char *method_name;
 
   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
@@ -2315,8 +2418,7 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
   if (g_simple_async_result_propagate_error (simple, error))
     goto out;
 
-  value = g_simple_async_result_get_op_res_gpointer (simple);
-  method_name = g_object_get_data (G_OBJECT (simple), "-gdbus-proxy-method-name");
+  value = g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
 
  out:
   return value;
@@ -2326,9 +2428,11 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
  * g_dbus_proxy_call_sync:
  * @proxy: A #GDBusProxy.
  * @method_name: Name of method to invoke.
- * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal
+ *              or %NULL if not passing parameters.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @error: Return location for error or %NULL.
  *
@@ -2381,9 +2485,9 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
   gboolean was_split;
   gchar *split_interface_name;
   const gchar *split_method_name;
-  const GDBusMethodInfo *expected_method_info;
   const gchar *target_method_name;
   const gchar *target_interface_name;
+  const gchar *destination;
   GVariantType *reply_type;
 
   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
@@ -2392,32 +2496,38 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
   g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
+  reply_type = NULL;
+
   was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
   target_method_name = was_split ? split_method_name : method_name;
   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
 
-  if (proxy->priv->expected_interface)
+  /* Warn if method is unexpected (cf. :g-interface-info) */
+  if (!was_split)
     {
-      expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
-      if (expected_method_info == NULL)
-        {
-          g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
-                     target_method_name,
-                     target_interface_name);
-        }
+      const GDBusMethodInfo *expected_method_info;
+      expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+      if (expected_method_info != NULL)
+        reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
     }
-  else
+
+  destination = NULL;
+  if (proxy->priv->name != NULL)
     {
-      expected_method_info = NULL;
+      destination = get_destination_for_call (proxy);
+      if (destination == NULL)
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+          ret = NULL;
+          goto out;
+        }
     }
 
-  if (expected_method_info)
-    reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
-  else
-    reply_type = NULL;
-
   ret = g_dbus_connection_call_sync (proxy->priv->connection,
-                                     proxy->priv->name_owner,
+                                     destination,
                                      proxy->priv->object_path,
                                      target_interface_name,
                                      target_method_name,
@@ -2428,6 +2538,7 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
                                      cancellable,
                                      error);
 
+ out:
   if (reply_type != NULL)
     g_variant_type_free (reply_type);