How I built a Serverless, P2P Parental Control app using WebRTC and Kotlin

As a parent and an Android developer, I faced a dilemma. I wanted to monitor my child’s activity on TikTok and YouTube to ensure their safety, but I hated the privacy implications of existing parental control apps.

Almost every commercial solution works the same way:

  1. Collect child’s data (location, history, app usage).

  2. Upload everything to a centralized Cloud Database.

  3. Display it on the parent’s phone.

I didn’t want my child’s location history sitting on a third-party server. So, I decided to over-engineer a solution for myself. I spent the last 6 months building SafeStream, a completely serverless, Peer-to-Peer monitoring system.

Here is how I built it using Kotlin, Jetpack Compose, and WebRTC.

The Architecture: “No Backend” approach
The core idea is simple: The Parent device is the server. Data should flow directly from Device A (Child) to Device B (Parent) without persisting anywhere in between.

To achieve this, I used WebRTC (Web Real-Time Communication) not for video calls, but for its DataChannels.

Guardian App (Child): Runs a background service that collects data locally. It acts as a WebRTC peer.

Parent App: Acts as the viewer peer. It receives JSON payloads directly and stores them in a local Room (SQLite) database.

Signaling: I use Firebase Realtime Database strictly for the handshake (SDP Offer/Answer exchange). No user data touches Firebase.

The Tech Stack
Language: 100% Kotlin.

UI: Jetpack Compose (Material3) for a modern “Reel-like” feed.

P2P: Google’s WebRTC library wrapped in a custom WebRTCConnectionManager.

Traversal: STUN/TURN servers to bypass NATs and firewalls (essential for 4G/5G connections).

Encryption: AES-256-GCM on top of WebRTC’s DTLS (Paranoia level: High).

Key Technical Challenges
1. Extracting Video Metadata (The ethical hack)
TikTok and YouTube don’t offer APIs for “watch history”. I implemented an Accessibility Service (GuardianAccessibilityService.kt) that scans the UI hierarchy.

It detects when the user is in a video app (com.zhiliaoapp.musically or com.google.android.youtube).

It scrapes the contentDescription or text of the visible views to extract the video title and channel.

Optimization: To avoid battery drain, this logic is heavily debounced and only runs when the screen content changes.

2. The WebRTC Handshake in Background
Keeping a WebRTC connection alive in the background on modern Android is… painful. Android’s Doze Mode and App Standby Buckets are aggressive.

Solution: I implemented a foreground service with a persistent notification.

Turn-on-demand: The connection isn’t always open. The Parent sends a high-priority FCM (Firebase Cloud Message) to “wake up” the Child device, which then initiates the WebRTC signaling process.

3. Transferring Images over P2P
Sending high-res screenshots over a DataChannel requires chunking. WebRTC has a limit on message size (approx 16KB to be safe). I wrote a chunking algorithm that splits the Bitmap (compressed as WebP) into small binary packets, sends them sequentially, and reassembles them on the Parent side.

The Business Model (Indie Dev Reality)
I’m releasing this app for free. However, running TURN servers (bandwidth relay) costs money. To make it sustainable without subscriptions or selling data, I implemented a Credit System:

Text logs and real-time location are free (low bandwidth).

Downloading high-res screenshots costs “Credits”.

Users get free daily credits or can watch a Rewarded Ad (AdMob) to refill them. This pays for the TURN bandwidth.

What’s Next?
I’m launching the Open Beta next week. I’m looking for feedback on the P2P stability across different network carriers (CGNAT is my enemy).

If you are curious about the P2P implementation or want to test it out, check it out here: safestreamguardian Architecture – beta subscription

Happy coding! 🚀

Similar Posts