Cell broadcasts are a notification systems in mobile broadband networks to inform all mobile devices (like smartphones) in a certain area about security hazards like extreme weather or human made disasters. It’s somewhat similar to SMS but instead of being sent from one device to another it’s sent to all devices in that area. The messages can be sent to all devices in a certain area or a whole country.

In most countries there’s every now and then an announced test event where a cell broadcast message is sent out although there’s no imminent threat. This was the case in the Netherlands on December 4th.

Unfortunately we weren’t in a position to receive and display cell broadcast messages with Phosh on that date. This is due to the component in the system responsible for interacting with the modem (ModemManager) which handles voice calls, SMS, USSD, positioning information, etc. just fine but not knowing anything about cell broadcast messages yet.

Due to some initial debugging logs for ModemManager from an earlier cell broadcast test in Ontario (provided by Chris McGee) we at least knew that “something” is received by the modem and (via QMI¹) ends up in ModemManagers SMS code path where (as expected) parsing fails. Is this the actual cell broadcast message already?

Parsing cell broadcast messages Link to heading

The 3GPP TS 23.041 describes how cell broadcast messages (CBM) look like. Roughly speaking it’s some fixed length metadata and then the actual message text. The maximum length of the message is 88 Bytes with a 6 byte header and a maximum of 82 Bytes for the message itself.

This is what the debug logs had:

<<<<<<   value      = 00:FF:FF:FF:FF:07:58:00:67:60:11:12:0F:16:54:74:7A:0E:4A:CF:41:61:10:BD:3C:A7:83:DE:66:10:1D:5D:06:3D:DD:F4:B0:3C:FD:06:05:D9:65:39:1D:24:2D:87:C9:79:D0:34:3F:A7:97:DB:2E:10:15:5D:96:97:41:E9:39:C8:FD:06:91:C3:EE:73:59:0E:A2:BF:41:F9:77:5D:0E:42:97:C3:6C:3A:1A:F4:96:83:E6:61:73:99:9E:07
[1637171704.716921] [modem1] parsing PDU (0)...
[1637171704.716987] [modem1]   This is a transfer-route message
[1637171704.717063] [modem1] error parsing PDU (0): Unhandled message type: 0x03

A first try could be to take the last 82 bytes and assume each byte is an ASCII character. That doesn’t look too well though:


Let’s try the GSM 7bit encoding (which is also used in SMS) on the same data. We could do that with a small e.g. Python script (where a web search gives plenty of results). We took a slightly different approach and added a small test case to ModemManager (MM) that would feed the data into MMs decoding function for SMS text:

This is a test of the Ontario Alert Ready System. There is no danger to your health or safety

Bingo! This looks somewhat cut off at the end (no punctuation) but is readable text. So now we knew what we receive is the actual CBM text. The 6 Bytes in front of that is then likely the metadata and the 8 Bytes before that are likely part of QMI.

The metadata has multiples parts. The first two bytes is the serial number that is used to uniquely identify a CBM. Part of the serial number contains an update number (to indicate that a CBM is an update to previous CBM).

The 3rd and 4th byte is the message identifier which indicates the source of the CBM. This is country specific. In our case the 4370 means Emergency Alert in Canada. The AOSP has a list we can use to look that up.

The 5th byte is the data coding scheme (how the text is encoded, in our case GSM 7bit) and finally the 6th byte specifies how many parts a message has and which part this is. With that information we can extend our test case and see if the values make sense. And indeed our CBM from Ontario above is the first part of a six part CBM so it’s not surprising that the text doesn’t end properly. That’s also why we call the structure that stores the data MMCbmPart (the 3gpp standard calls this a page).

The nice thing about having this in form a of an executable test case is that we can easily rerun it each time we make a change to the parsing code:

meson test -C _build test-cbs-part-3gpp --print-errorlogs

Assembling cell broadcast messages and exposing them on DBus Link to heading

The other advantage is that we can check other CBMs easily that somebody captured. We leveraged this for the NL test day on December 4th. Jan Vlug and Luca Weiss provided logs that contained three QMI debug messages related to CBMs. Sticking the bytes from those into our test parser we could see that the complete CBM should have 3 parts and the parts we received had part number one to three. So decoding the text from each part…

  • part 1

NL-Alert 04-12-2023 12:00: TESTBERICHT. De overheid waarschuwt je tijdens noodsituaties via N

  • part 2

L-Alert. Je leest dan wat je moet doen en waar je meer informatie kan vinden. *** TEST MESSAG

  • part 3

E Netherlands Government Public Warning System. No action required.

…and concatenating it …we should get a complete message:

NL-Alert 04-12-2023 12:00: TESTBERICHT. De overheid waarschuwt je tijdens noodsituaties via NL-Alert. Je leest dan wat je moet doen en waar je meer informatie kan vinden. *** TEST MESSAGE Netherlands Government Public Warning System. No action required.

Which was the case, yay!

So it seems we’re on the right track and can assemble these parts into one message and expose it on DBus so applications like Chatty and Phosh can make use of it. For that we introduce a MMBaseCbm that keeps track of the different parts and a MMModemCellbroadcast that groups all CBMs received by a modem in the system into the Modemmanager codebase. The code part isn’t very exciting as it’s very similar to what is done for SMS.

Picking up the message out of the noise Link to heading

With the above we can parse the incoming message bits, assemble them and expose them on DBus but we don’t get the message bits out of the data the modem sends to ModemManager. As mentioned above we were seeing those in the QMI stream already but the logs provided from the NL test showed something else:

ModemManager[34764]: <debug> [1701687612.026406] [modem1/ttyUSB2/at] <-- '<CR><LF>+CBM: 88<CR><LF>46A0111305134E662BC82ECBE92018AD1593B56430D90C1493E960301D885A9C528545697288A4BA40C432E86D2FCBD1E53419740F87E5F331BA7EA783D465103DAD2697DD7390FBFD26CFD3F47A989E2ECF41F67418E404<CR><LF><CR><LF>+CBM: 88<CR><LF>46A011130523CC56905D96D35D206519C42E97E7741039EC06DDC37490BA0C6ABFCB7410F95D7683CA6ED03D1C9683D46550BB5C9683D26EF35BDE0ED3D365D03AEC06D9D36E72D9ED02A9542A10B538A5829AC5E9347804<CR><LF><CR><LF>+CBM: 65<CR><LF>46A0111305334590B34C4797E5ECB09B3C071DDFF6B2DCDD2EBBE920685DCC4E8F41D7B0DC9D769F41D3FC9C5E6EBB40CE37283CA6A7DF6E90BC1CAFA7E565B2AB<CR><LF>'

This means that the CBM parts not only end up in QMI but (at least for the Librem 5 and Fair Phone 3) also end up in the serial communication and are indicated by a +CBM: <lengh>. Equipped with that information we can set up a (what ModemManager) calls an “unsolicited event handler” that fetches the messages out of the serial stream for us and passes it to the “message assembly” step.

Now it would be nice to test if things actually work end to end. For that we first wrote a small tool called mmcbmmonior that listens ModemManager’s DBus interfaces to see if there are new cell broadcast messages. Now the only thing left to do is wait for the next cell broadcast test?

We’re basically adhering to xkcd 303 here: “I’m not slacking off, I’m waiting for a cell broadcast” …which could be a rather long way off but there’s a shortcut: As we’re able to leverage serial communication we can inject fake +CBM messages into the serial stream at various places. One place is within ModemManager itself where it parses the serial stream in parse_response_buffer. Injecting it there (e.g. triggered by a SIGUSR1) allows us to simulate pretty closely to what happens in a real situation as all the code we added can’t tell the difference. MM should then grab the CBM parts out of the serial stream, assemble them and expose an object on DBus. We can check that with mmcbmmonior:

$ _build/test/mmcbmmonitor
[/org/freedesktop/ModemManager1/CBM/0] new cbm: received
    4371: NL-Alert 04-12-2023 12:00: TESTBERICHT. De overheid waarschuwt je tijdens noodsituaties via NL-Alert. Je leest dan wat je moet doen en waar je meer informatie kan vinden. *** TEST MESSAGE Netherlands Government Public Warning System. No action required.

alternatively we can use busctl to see more details about the DBus property changes:

$ sudo busctl monitor org.freedesktop.ModemManager1
Monitoring bus message stream.
‣ Type=signal  Endian=l  Flags=1  Version=1 Cookie=91  Timestamp="Sun 2023-12-10 12:12:13.372705 UTC"
  Sender=:1.88  Path=/org/freedesktop/ModemManager1/Modem/0  Interface=org.freedesktop.DBus.Properties  Member=PropertiesChanged
  MESSAGE "sa{sv}as" {
          STRING "org.freedesktop.ModemManager1.Modem.Cellbroadcast";
          ARRAY "{sv}" {
                  DICT_ENTRY "sv" {
                          STRING "Cellbroadcasts";
                          VARIANT "ao" {
                                  ARRAY "o" {
                                          OBJECT_PATH "/org/freedesktop/ModemManager1/CBM/0";
          ARRAY "s" {

‣ Type=signal  Endian=l  Flags=1  Version=1 Cookie=92  Timestamp="Sun 2023-12-10 12:12:13.388456 UTC"
  Sender=:1.88  Path=/org/freedesktop/ModemManager1/Modem/0  Interface=org.freedesktop.ModemManager1.Modem.Cellbroadcast  Member=Added
  MESSAGE "o" {
          OBJECT_PATH "/org/freedesktop/ModemManager1/CBM/0";

‣ Type=signal  Endian=l  Flags=1  Version=1 Cookie=93  Timestamp="Sun 2023-12-10 12:12:13.389142 UTC"
  Sender=:1.88  Path=/org/freedesktop/ModemManager1/CBM/0  Interface=org.freedesktop.DBus.Properties  Member=PropertiesChanged
  MESSAGE "sa{sv}as" {
          STRING "org.freedesktop.ModemManager1.Cbm";
          ARRAY "{sv}" {
                  DICT_ENTRY "sv" {
                          STRING "State";
                          VARIANT "u" {
                                  UINT32 2;
                  DICT_ENTRY "sv" {
                          STRING "Text";
                          VARIANT "s" {
                                  STRING "NL-Alert 04-12-2023 12:00: TESTBERICHT. De overheid waarschuwt je tijdens noodsituaties via NL-Alert. Je leest dan wat je moet doen en waar je meer informatie kan vinden. *** TEST MESSAGE Netherlands Government Public Warning System. No action required.";
          ARRAY "s" {

Informing the user Link to heading

With that sorted out the last bit left is alerting the user. As CBMs are important we want them to be shown in Phosh even when the shell is locked and the screen is off. This is how it currently looks:

Outlook Link to heading

As you can see from the links the code isn’t merged yet. We can land an initial version in Phosh as soon as the ModemManager DBus interface bits are in. From there on the Phosh and MM parts can develop mostly independently.

On the Phosh side we want to add proper queuing so multiple cell broadcasts (or update to previous messages) are displayed one after another. We also need to pick the right channel information (the title of the dialog above) from the provided identifier, add a unique event to feedbackd and the sound theme to ensure the user gets alerted (for the demo I’ve justmessage-new-email). Compared to what we have already this is all pretty simple to add.

On the MM side we want to add support for accepting messages via QMI as well (for modems that don’t use +CBS) and finally we want chatty to keep a history of received CBMs.

How can you help? You can run the ModemManager from the cellbroadcast merge request and have mmcbmmonior running in the terminal. Whenever there’s a CBM you should see it there.

Together with the earlier addition of emergency calls this is one more step to make Phosh safe to use for end users.

¹: QMI is the (proprietary) way of Qualcomm processors (which are very, very common in modems) to talk to the host processor. Another way to do that is serial communication (which will become useful below). Both are (in our Linux phones) transported over USB.