Syome - I Extracted All Voice Prompts From Bluetooth Earbuds Without Hardware Measure
Loading...

I Extracted All Voice Prompts From Bluetooth Earbuds Without Hardware Measure

not gonna save its battery...

Due to copyright issues, will not show details about the scripts or hand out what I extracted. This is just my experience post.

I used this earbuds for one and a half years, months ago a problem of TWS became severe: the battery life of one of them pretty worse than the other(8h vs 4h). Then I decided to research the earbuds.

Battery Level

I can only see the battery level by using the their app. When connected to my earbuds, my Blueman-manager always shows a “0” until I realized they use BLE to transmit device informations. But after connected to that BLE device, it only shows one of the earbuds battery level (primary sink).

So I tried bluetoothctl, check whether gatt attributes can give me an answer.


menu gatt
list-attributes ...mac

But after reading all of these, they only told me Error org.bluez.Error.NotPermitted: Read not permitted or something not related to battery level (actually one of them was the primary sink’s battery level, but cannot find the other one)…

So I started to do some research on their app using jadx…


The result is, this earbuds using Airoha’s RACE protocol. Also, since TWS that using Bluetooth Classic only report informations by the primary sink, the other one only communicate with the primary sink, so getting the other’s should use a Relay(or forwarding) method.

Firstly we need to find the destination of the secondary sink in TWS’s own network. Send a RACE command 0x0D00 to list them. This will return many devices or some others. For me the bytes after 6th are the payload, containing (role, destination) of one device.

Then goes to reading the battery level. For the primary sink, it’s easy, just send a 0x0CD6 with payload 0x00 to get. For the secondary sink, we should compose a RACE frame: starting with 05 5A, then length 03 00 (little endian, 3 bytes come from 1 payload + 2 opcode), then opcode D6 0C (“0C D6“‘s little endian), last payload 00. Finally we can compose the relay payload: destination + header + length + opcode + payload. And now it’s time to encapsulate it into a RACE frame: send a RACE command 0x0D01 (Relay) with that relay payload.

That’s all. Sometimes we can see battery level in the app is literally reversed(left and right), that’s because the app could not figure out which one is the primary, so hardcoded like left earbud is that one. Perhaps that’s a good news because your earbuds are not always asking one earbud to play that energy-intensive role. (idk why one of my earbuds’ battery is dying then)

ANC

The second requirement for me is ANC. In contrast, ANC using RFCOMM RACE protocol, via RFCOMM socket.

The frame’s composing is similar to the battery level, but the opcode is 0x0E06, and payload: 00 0B for disable ANC, 00 0A 01 00 for ANC noise cancelling, 00 0A 09 04 for ANC transparency.

If forgot to relay to the secondary sink, you can experience a - one of your ears is deaf and the other one is clear. XD

Voice Prompts

After that I was busy and thought that’s enough for my use until one earbud’s battery life is only 2h to consume a full level while the other is still 8h, and that’s the only one way to trigger random voice by tapping that earbud.

At first I just wonder whether I can make it play that random voice via command. Their app does not provide a function that can play a random voice, even the modes switching such as ANC does not, only tapping does. So at first I didn’t spend many time on their app, also making no progress.

But unfortunately I caught this while finding some RACE commands:


public static final int RACE_STORAGE_PAGE_READ = 1027;

Following the clues, I found extracting data that was flashed into the earbuds is kind of available:


StageReadPage stageReadPage = new StageReadPage(this);
stageReadPage.payload[0] = 0;
stageReadPage.payload[1] = (byte) i4;
stageReadPage.payload[2~5] = address;
...

So… have a try? Command 0x0403, 6 bytes payload: 00 01 00 00 00 00, it returns:


05 5b 0a 01 03 04 ... c0 41 08

HOW’S IT POSSIBLE?!!

That means, I can extract data from a connecting earbuds without any hardware measure! Remembering I bought this earbuds is just for the illustrations and the system voice, that dying earbud is not a big deal. (actually not because they are so expensive and they have many functions)

Reading the pages of the flash memory, dump all the data into a file and analyse it, found the hex:


FF FB 54 C0 50 80 07 0C ...

So 0xFFF is an mp3’s header, and finally I extracted 21 mp3 files.

all voice prompts

Dunno why everyone has the permission to access the flash, or the firmware somehow needs an update one day, this is reachable… But I’m just a regular user after all, that’s enough.


😋