A user opens a ticket: the mobile app is sluggish. You check your dashboards. CPU utilization is flat. Error rates are zero. Your APM lights are a reassuring green. The backend, by every visible measure, is healthy. But the user is not wrong. The slowness is real, and it is happening somewhere in the long, shadowy corridor between an Android device and your server.
The problem is that most observability tools stop at the application boundary. They measure what happens after your framework parses a request. They track database queries, cache hits, and downstream service calls. What they miss is the mechanics of the request itself: the time an OkHttp call spends inside the Android HTTP stack, the transit across a volatile mobile network, the TLS handshake negotiation, and the silent waiting that happens inside kernel queues before your code ever runs. These gaps swallow milliseconds—or entire seconds—while your APM remains silent.
Encryption makes the blindness complete. Modern Android apps route everything through BoringSSL. By the time a packet hits the kernel network stack, the HTTP headers are encrypted. A standard tcpdump or network hook sees only opaque TLS records. You can observe that traffic is flowing, but you cannot read it. You certainly cannot tie a specific kernel-level TCP segment back to a specific user's API call. The trace context you need is trapped inside the ciphertext.
eBPF changes the equation because it lets you instrument the system from the inside out without altering your application code. Instead of asking your app to report its own latency, you attach small programs directly to the kernel and to critical userspace libraries. These programs observe events as they happen, extract what you need, and ship it to a ring buffer. There is no SDK bloat inside your Android APK beyond a lightweight header, and there is no instrumentation agent rewriting your backend classes.
The Four-Part Setup
Building this pipeline involves four distinct layers of observation.
1. The traceparent anchor. On the Android device, you add an OkHttp interceptor that injects a W3C traceparent header into every outbound request. This is the only change required on the mobile side, and it is minimal. The header travels inside the encrypted payload all the way to your backend. Because it sits inside the HTTP layer, it survives inside the plaintext that the TLS engine eventually exposes.
2. TCP arrival timing. On the backend host, you use eBPF Traffic Control hooks attached to the network interface. These programs fire as individual TCP segments arrive. They capture sequence numbers and timestamps at the edge. You now know precisely when bits left the wire and entered your machine, long before your application reads a single byte.
3. Decryption probes. Your backend terminates TLS using either OpenSSL or BoringSSL. Here is where the architecture gets interesting. Using uprobes—dynamic userspace probes—you attach to SSL_write and SSL_read inside the TLS library. These functions fire the instant plaintext data passes through the encryption engine. Your eBPF program reads that decrypted buffer, scans for the traceparent header, and extracts it. The kernel now possesses a direct mapping between a raw TCP flow and a specific mobile request, without you ever handling certificates or keys in a custom tool.
4. Kernel queueing. Even after the data is decrypted and ready, your application may not consume it immediately. You attach kprobes to relevant kernel functions handling socket buffers and scheduling events. This measures queueing latency: the time requests spend waiting in kernel land because your process is contending for CPU or simply has not called read() yet.
All four signal sources write events into an eBPF ring buffer. A sidecar process running in userspace drains this buffer, correlates events by traceparent ID, and reconstructs a single, coherent timeline for every request. What was previously a scattering of disconnected kernel noise becomes a structured trace.
Reading the Full Path
The assembled output is typically rendered as a flame graph or a structured span tree that splits latency into four concrete parts:
- Temps de transit réseau : La durée entre l'envoi du dernier octet de la requête par la radio Android et sa réception par la carte réseau (NIC) du backend. C'est là que réside la volatilité cellulaire.
- Durée du handshake TLS : Le temps passé à négocier le tunnel chiffré. Sur des réseaux instables, cela peut éclipser le transfert de données réel.
- Latence de mise en file d'attente du noyau : Temps passé dans les tampons du noyau et les files d'attente de l'ordonnanceur après l'arrivée du segment, mais avant que l'espace utilisateur ne le consomme.
- Temps de traitement de l'application : La part que votre framework backend et votre logique métier consomment réellement.
Vous pourriez découvrir qu'une requête mobile de 800 ms ne passe que 40 ms dans votre parseur JSON. 200 ms supplémentaires s'évaporent dans un handshake TLS bloqué sur une connexion sujette aux pertes. 300 ms de plus disparaissent dans le backlog du noyau sur un hôte surchargé. Votre APM ne rapportait que les 40 ms. Sans visibilité au niveau du noyau, vous auriez optimisé la mauvaise chose du tout.
Un surcoût qui passe réellement à l'échelle
Les agents APM traditionnels obtiennent leur visibilité en interceptant les appels à l'intérieur de l'environnement d'exécution (runtime) de votre langage. Ils enveloppent les méthodes, allouent des objets span et sérialisent les données de télémétrie dans le tas (heap) de votre processus. Sous la charge, ce surcoût s'accumule rapidement. Les coûts de sérialisation augmentent. La pression sur le ramasse-miettes (garbage collection) s'accroît. Vous payez la visibilité avec les propres ressources de votre application.
Les programmes eBPF s'exécutent à l'intérieur d'une machine virtuelle du noyau et sont compilés JIT en instructions machine natives. Un vérificateur contrôle leur sécurité avant leur chargement. Chaque sonde ajoute des microsecondes au chemin, pas des millisecondes. Le travail lourd de mise en correspondance des événements et de rendu des graphiques s'effectue dans le sidecar, en dehors du chemin critique (hot path) de votre service. Vous ne gonflez pas les allocations de tas. Vous n'ajoutez pas de taxes de sérialisation lors du traitement des requêtes. Pour les services traitant des milliers de requêtes par seconde, cette distinction est cruciale.
Ce qu'il faut pour l'exécuter
Ce n'est pas une solution miracle, et ce n'est pas un SaaS managé que l'on peut activer d'un simple clic. Vous avez besoin d'un noyau backend prenant en charge l'eBPF moderne, y compris les informations de type BTF pour que vos sondes puissent parcourir les structures du noyau en toute sécurité. Votre bibliothèque TLS doit exposer des symboles que les uprobes peuvent cibler ; si vous livrez un binaire lié statiquement avec une version d'OpenSSL stripped ou fortement personnalisée, vous devrez en tenir compte. Vous devez également vérifier que votre en-tête traceparent survit aux proxys ou aux passerelles (edge gateways) situés entre le client mobile et le point de terminaison TLS.
Mais pour les équipes épuisées par les tickets « le mobile est lent » qui défient toute analyse de cause racine, cette architecture remplace les suppositions rituelles par un signal concret. Vous cessez de spéculer sur la « météo réseau » pour commencer à mesurer des requêtes spécifiques à travers des canaux spécifiques.
Ce qu'il faut retenir
Vous n'avez pas à traiter le trafic mobile chiffré comme un flux opaque qui devient magiquement visible une fois qu'il atteint votre framework. En combinant un simple en-tête OkHttp avec des sondes eBPF placées stratégiquement sur le backend, vous pouvez suivre une requête unique depuis un appareil Android, à travers le déchiffrement TLS, les files d'attente du noyau, et jusqu'à votre logique applicative — sans réécrire votre application mobile et sans instrumenter votre code backend comme l'exige l'APM traditionnel. Ce n'est pas seulement une amélioration incrémentale. C'est une observabilité de bout en bout à travers
