draft: linux push notifications

This commit is contained in:
Colin 2023-11-06 22:10:42 +00:00
parent 7c7c528807
commit 8b738eb5e2
1 changed files with 68 additions and 0 deletions

View File

@ -0,0 +1,68 @@
+++
title = "Wake-on-LAN and Push Notifications in Mobile Linux"
description = "how to suspend the application cores without missing VoIP calls or messages"
+++
## DRAFT
- motivation
- battery life -> suspend SoC
- receive messages over modem or WLAN
- modem wakes SoC for calls; WLAN doesn't do anything
- wowlan
- how to enable
- flakiness, race conditions (and: "i'll come back to that")
- notification server
- "why" (so that we can wake only on the "important" packets)
- implementations (ntfy)
- ntfy integrations
- how to: Matrix
- how to: Prosody
- resolving the wowlan race condition
- thoughts and future
- e.g. Chatty wants to implement ntfy support
## Motivation
the more i carry my Pinephone for long stretches, the more i feel limited by battery life. there's a lot one can do to deal with that, and among those options is to reduce idle power draw.
in fact if you use something like [SXMO](https://sxmo.org), it'll keep a charge for the whole day if you were to keep it screen-off in your pocket. it does that by suspending the CPU to RAM, but keeping the modem powered -- great if all you care about is being notified on incoming calls/texts, not so great if you want to be notified by apps running on the CPU.
a typical solution to this is to wake the CPU whenever the system gets an IP packet, yield that to userspace & give your applications a chance to sound a ringer, vibrate, etc, and then suspend again when they're done.
the more you can decide "this packet isn't urgent" without CPU intervention, the more you can avoid waking or even passing it on to the CPU, the more you can extend battery life. and so most systems provide tooling to help you tune IP-based wakeups if you know where to poke.
## Wake on LAN
a.k.a. "Wake-On-Wireless LAN" or "wowlan". [Wake on LAN](https://en.wikipedia.org/wiki/Wake-on-LAN) is an actual established standard for remotely waking one ethernet device from another device on the same LAN. the user or OS enables this feature in the BIOS, the PC suspends, some other device sends a specially crafted packet (a "magic packet"), **magic happens**, and the BIOS wakes the system.
this can work on WiFi, too, but on a mobile system you enable this by speaking directly to the WiFi chip, rather than BIOS/UEFI. if you're lucky, `sudo iw phy0 wowlan enable any` will do just that.
then enter sleep (`rtcwake -m mem -s 300` to suspend to RAM for 300 seconds), and from a different device on the same WiFi network, wake your phone with `wol <your_phones_mac_address>` e.g. `wol 02:ba:7c:9c:cc:78` (find the MAC address with `iw dev`).
for the Pinephone, whether or not this works depends on how your kernel is configured. firstly, your kernel needs to know to keep the WiFi chip powered during suspend. second, there's a GPIO routed from the WiFi chip to the main SoC: the WiFi chip toggles this GPIO when it sees the magic packet, and the SoC needs to be told to recognize that as an interrupt source that the kernel can respond to. [megi's](https://xnux.eu/devices/pine64-pinephone.html) kernel does these things, i cannot say about other kernels like Mobian's.
so if you're on megi's kernel, this should work... at least once if you repeat the process enough. ugh, WiFi and Bluetooth share a lot of the same resources and don't always play nicely together. a quick hack is to just disable bluetooth: `rm /lib/firmware/rtl_bt` (back it up first). there's also a `CONFIG_BT_COEXIST` kernel option you could mess with if you're pursuing a longer-term fix (i don't really use bluetooth, but would appreciate to hear better fixes if anyone pursues them).
and now, `wol` should pretty reliably wake the phone. there's a race condition if you try that _during_ the `rtcwake` call instead of waiting a second for it to complete -- i'll address that further down.
### Wowlan for Userspace
you're probably not interested in manually calling `wol` from some other computer on the same network to wake your phone.
you'd probably prefer it to wake anytime you get a Matrix or XMPP message, or something.
for that, things diverge quickly from any sort of standard.
but `wol` actually just sends out an ordinary UDP packet with a specific payload, so getting from there to "wake on TCP traffic sent to port 22", or "wake on TCP traffic sent from IP foo" shouldn't be _technically_ difficult.
the Pinephone's Realtek WiFi chip exposes a programmable pattern-matching facility. just give it a byte string and a mask, and it'll wake on any packet whose bytes within the masked regions match those within the byte string.
so if you know that a packet carrying TCP specifies its destination port at bytes 37-38, it's easy to tell the WiFi chip to "wake on TCP traffic sent to port 22": `sudo iwpriv wlan0 wow_set_pattern pattern=-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:00:16` (fields are hexadecimal, `:` indicates byte boundaries, `-` indicates "i don't care what value this byte is set to").
do this before the `rtcwake` call, then try `ssh`ing into your phone while it's asleep: with any luck it should wake!
figuring out the byte pattern which achieves what you want is painful, so i've got a script for that [here](TODO). equivalent to the above is `rtl8723cs-wowlan tcp --dest-port 22`. if you've a chat application which is talking over https, you might try `rtl8723cs-wowlan tcp --source-port 443` and see that the phone wakes every time you get a message. but then you'll get spurious wakes if you leave a web browser open, etc. you could use `lsof -i4` to locate the local port(s) your IM app is using and wake more specifically on those. but you'll still hit limits with this approach, especially if you're using a chatty protocol like Matrix, or if you idle in a bunch of channels configured to notify you only on @mention.
enter... notification servers!
## Notification Servers