Web Bluetooth characteristicvaluechanged not triggered in Android Chrome but works in Desktop Chrome

I’m working on a web application that connects to a heart rate monitor using the Web Bluetooth API. The code works perfectly in Desktop Chrome, where the characteristicvaluechanged event is triggered as expected after connecting to the device. However, when I try this on Android Chrome, everything seems to go well — the device connects and starts notifications, but the characteristicvaluechanged event is never fired.

Here’s the code:

async connectHeartRateMonitor() {
    if (this.device) return this.connect();
    if (!(navigator as any).bluetooth?.requestDevice) {
        return alert("Your device does not support Web Bluetooth API.");
    }

    try {
        this.device = await (navigator as any).bluetooth.requestDevice({
            filters: [{ services: ['heart_rate'] }],
            optionalServices: ['battery_service', 'device_information']
        });

        this.device.addEventListener('gattserverdisconnected', this.onDisconnected.bind(this));
        this.connect();
    } catch (error) {
        console.error('Error:', error);
    }
}

private connect() {
    this.device.gatt.connect().then(async () => {
        const service = await this.device.gatt.getPrimaryService('heart_rate');
        this.characteristic = await service.getCharacteristic('heart_rate_measurement');
        await this.characteristic.startNotifications();
        this.characteristic.addEventListener('characteristicvaluechanged', this.handleHeartRateData.bind(this));
        console.log('Connected and notifications started.');
    }).catch(error => {
        console.error('Error connecting:', error);
    });
}

private handleHeartRateData(event) {
    const heartRate = event.target.value.getUint8(1);
    console.log('Heart Rate:', heartRate);
}

In both Desktop Chrome and Android Chrome, I can connect to the heart rate monitor. However, on Android, the characteristicvaluechanged event never triggers after notifications are started.

Any ideas why this event isn’t firing on Android Chrome? Is there something different about how Web Bluetooth works on mobile that I need to account for?

Edit:

Here is the live demo on Plunker. Unfortunately, Bluetooth connections don’t work inside an iframe, so I’m also sharing the direct URLs for testing:

https://plnkr.co/edit/phLtTl0l8JWBWwm2?preview
https://run.plnkr.co/preview/cm1a1vs4500063j75dwrozsfm/

If the direct preview URLs expire, you can go to the Plunker editor and click “open the preview in separate window” where the page will load directly and allow connecting to Bluetooth devices.