Re: [PATCH v1] gdbus: Skip BLE random-address devices and remove broad match rule

Xiuzhuo Shang <[email protected]> Mon, 13 Jul 2026 14:03:26 +0800
Newsgroups dev.linux.lists.ofono,org.kernel.vger.linux-bluetooth
Message-ID <[email protected]>

On 7/10/2026 10:12 PM, Luiz Augusto von Dentz wrote:
> Hi Xiuzhuo,
> 
> On Fri, Jul 10, 2026 at 3:55 AM Xiuzhuo Shang
> <[email protected]> wrote:
>>
>> Two related changes to reduce dbus-daemon load caused by high-rate
>> BLE advertising signals:
>>
>> 1. In parse_properties(), skip creating a GDBusProxy for Device1
>>    objects whose AddressType is 'random' (BLE-only devices). Each
>>    proxy registers a per-device PropertiesChanged watch via
>>    g_dbus_add_properties_watch(); with hundreds of BLE peripherals
>>    advertising simultaneously, this results in hundreds of match rules
>>    and dbus-daemon routing thousands of RSSI PropertiesChanged signals
>>    per second to ofono, none of which ofono processes. The
>>    AddressType property is available in the InterfacesAdded dict at
>>    the time parse_properties() is called, so the check is reliable
>>    and race-free.
>>
>>    BR/EDR devices (AddressType='public') are unaffected: their proxies
>>    are created as before, and the property_changed callback continues
>>    to receive Paired and ServicesResolved updates needed for HFP/HSP.
>>
>> 2. In g_dbus_client_new_full(), remove the broad
>>    type='signal',sender=<service>,path_namespace=<path> match rule.
>>    This rule was the sole feeder for the signal_func path in
>>    message_filter(). ofono never calls g_dbus_client_set_signal_watch()
>>    so signal_func is always NULL; the broad rule therefore served no
>>    purpose and caused dbus-daemon to route every bluetoothd signal
>>    (including all BLE PropertiesChanged) to ofono.
>>
>>    InterfacesAdded and InterfacesRemoved are already covered by the
>>    precise watches registered via g_dbus_add_signal_watch() earlier in
>>    g_dbus_client_new_full(), so no functionality is lost.
> 
> Looks like this is on ofono though, why are you chaning the BlueZ side?

Regarding your suggestion to move the BLE filter to hfp_hf_bluez5.c: filtering in proxy_added()
would only prevent ofono from processing BLE devices at the application level. The per-device
PropertiesChanged watch is registered inside proxy_new(), which is called before proxy_added(). So
dbus-daemon would still route all RSSI signals to the process — the D-Bus pressure problem would
remain unchanged.

I will drop the change from the gdbus patch per your feedback

> 
>> Together these two changes prevent dbus-daemon from routing BLE
>> advertising PropertiesChanged signals to ofono entirely, eliminating
>> the dbus-daemon memory growth and bluetoothd socket backpressure seen
>> after hours of BLE scanning.
>>
>> Signed-off-by: Xiuzhuo Shang <[email protected]>
>> ---
>>  gdbus/client.c | 46 ++++++++++++++++++++++++++++++++++++++--------
>>  1 file changed, 38 insertions(+), 8 deletions(-)
>>
>> diff --git a/gdbus/client.c b/gdbus/client.c
>> index 48711ae8..dd3c17f9 100644
>> --- a/gdbus/client.c
>> +++ b/gdbus/client.c
>> @@ -937,6 +937,44 @@ static void parse_properties(GDBusClient *client, const char *path,
>>         if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
>>                 return;
>>
>> +       /*
>> +        * Skip BLE devices (AddressType='random') to avoid registering a
>> +        * per-device PropertiesChanged watch for every advertising BLE
>> +        * peripheral. ofono only needs BR/EDR devices for HFP/HSP; the
>> +        * high-rate RSSI PropertiesChanged signals from BLE scanners cause
>> +        * dbus-daemon memory bloat and socket backpressure in bluetoothd.
>> +        *
>> +        * The AddressType property is present in the InterfacesAdded dict
>> +        * (iter) at this point, so it can be checked before proxy_new().
>> +        */
>> +       if (g_str_equal(interface, "org.bluez.Device1") == TRUE) {
>> +               DBusMessageIter props, entry;
>> +               DBusMessageIter copy = *iter;
>> +
>> +               if (dbus_message_iter_get_arg_type(&copy) == DBUS_TYPE_ARRAY) {
>> +                       dbus_message_iter_recurse(&copy, &props);
>> +                       while (dbus_message_iter_get_arg_type(&props) ==
>> +                                               DBUS_TYPE_DICT_ENTRY) {
>> +                               const char *key;
>> +                               dbus_message_iter_recurse(&props, &entry);
>> +                               dbus_message_iter_get_basic(&entry, &key);
>> +                               if (g_str_equal(key, "AddressType") == TRUE) {
>> +                                       DBusMessageIter var;
>> +                                       const char *addr_type;
>> +                                       dbus_message_iter_next(&entry);
>> +                                       dbus_message_iter_recurse(&entry, &var);
>> +                                       dbus_message_iter_get_basic(&var,
>> +                                                               &addr_type);
>> +                                       if (g_str_equal(addr_type,
>> +                                                       "random") == TRUE)
>> +                                               return;
>> +                                       break;
>> +                               }
>> +                               dbus_message_iter_next(&props);
>> +                       }
>> +               }
>> +       }
> 
> Nack, not going to introduce BlueZ specific logic into gdbus like
> this. If we need to rate limit the RSSI notification then it means
> it's not suitable to be a property, but I believe we have a threshold
> logic in the likes of device_set_rssi_with_delta so perhaps we shall
> check why it is not working in your case.

I investigated why device_set_rssi_with_delta is not working in our case.

Root cause in BlueZ                                                                                 
                                                                                                      
  The customer scans with the following bluetoothctl sequence:                                        
                                                                                                      
  menu scan → transport le → duplicate-data off → back → scan le                                      
                                                                                                      
  scan le calls SetDiscoveryFilter({'Transport': 'le'}) — a transport-only filter with no RSSI or     
  pathloss condition. In btd_adapter_device_found() (src/adapter.c):                                  
                                                                                                      
  if (adapter->filtered_discovery)                                                                    
      device_set_rssi_with_delta(dev, rssi, 0);   /* delta forced to 0 */                             
  else                                                                                                
      device_set_rssi(dev, rssi);                  /* delta = RSSI_THRESHOLD = 8 */                   
                                                                                                      
  filtered_discovery is set true whenever current_discovery_filter is non-NULL (adapter.c:1872), which
   happens for any SetDiscoveryFilter() call — including a transport-only filter. With                
  delta_threshold=0, the condition delta < 0 is never true, so g_dbus_emit_property_changed("RSSI")   
  fires on every BLE advertisement regardless of whether the RSSI value changed. With 280+ BLE devices
   advertising continuously, this produces ~95 PropertiesChanged(RSSI) signals per second that flood  
  dbus-daemon.                                                                                        
                                                                                                      
  The delta=0 path was presumably intended for clients that set RSSI/pathloss proximity filters, so   
  that is_filter_match() gets precise RSSI tracking. A transport-only filter does not involve any     
  proximity condition, so forcing delta=0 is unnecessary in that case.                                
                                                                                                      
Proposed BlueZ fix direction    

  Only apply delta=0 when at least one active client filter has a real proximity condition (RSSI or   
  pathloss):                                                                                          
                                                                                                      
  static bool discovery_filter_has_proximity(struct btd_adapter *adapter)                             
  {                                                                                                   
      GSList *l;                                                                                      
      for (l = adapter->discovery_list; l; l = g_slist_next(l)) {                                     
          struct discovery_client *client = l->data;                                                  
          struct discovery_filter *item = client->discovery_filter;                                   
          if (item && (item->rssi != DISTANCE_VAL_INVALID ||
                       item->pathloss != DISTANCE_VAL_INVALID))
              return true;                          
      }
      return false;                                 
  }

  /* in btd_adapter_device_found() */               
  if (adapter->filtered_discovery &&
      discovery_filter_has_proximity(adapter))
      device_set_rssi_with_delta(dev, rssi, 0);
  else                                              
      device_set_rssi(dev, rssi);

  Could you advise whether this is the right approach, or if there is a better place to fix this?

  then I will Submit a separate BlueZ patch for the delta=0 fix once we agree on the approach.

> 
>>         proxy = proxy_lookup(client, path, interface);
>>         if (proxy) {
>>                 update_properties(proxy, iter, FALSE);
>> @@ -1255,14 +1293,6 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
>>                                                 "InterfacesRemoved",
>>                                                 interfaces_removed,
>>                                                 client, NULL);
>> -       g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
>> -                               "sender='%s',path_namespace='%s'",
>> -                               client->service_name, client->base_path));
>> -
>> -       for (i = 0; i < client->match_rules->len; i++) {
>> -               modify_match(client->dbus_conn, "AddMatch",
>> -                               g_ptr_array_index(client->match_rules, i));
>> -       }
> 
> This perhaps has a merit, but I can't recall what this watch was for.
> Is it matching every signal from the sender? It seems it was introduce
> before the watch infra existed, so if we can safely remove it we can
> perhaps remove the whole client-->match_rules.
> 
>>         return g_dbus_client_ref(client);
>>  }
>> --
>> 2.43.0
>>
> 
>