2026-09-28 β€’ 10 min read

On-Device AI & Local RAG in Android: Running Gemma with MediaPipe

Android
On-Device AI
Gemma
MediaPipe
Local RAG
Kotlin

1. The Paradigm Shift: Edge AI vs. Cloud APIs

When mobile engineers consider adding conversational intelligence to an Android application, the standard default is consuming a Cloud REST API (OpenAI, Gemini Cloud, Claude). However, when building software for real-world independent professionalsβ€”such as private in-home tutors managing minors' schedules, lesson notes, and payment recordsβ€”that conventional cloud path introduces non-negotiable liabilities:

Dimension Cloud AI (REST APIs) On-Device AI (MediaPipe + Gemma in HomeTutorPro)
Operating Cost Recurring token fees that scale linearly with usage. High margin vulnerability. 0.00 € per invocation. Computation runs directly on device silicon. Infinite scale at zero marginal cost.
Privacy & GDPR Personal data of students (names, phone numbers, prices, addresses) sent to third-party data centers. Privacy by Design. Zero bytes of sensitive data leave the physical device. Automatic European GDPR compliance.
Offline Reliability Requires continuous, low-latency mobile connectivity. Unusable in basements or low-signal classrooms. 100% Offline-First. Fully operational on airplane mode with zero mobile data consumption.
Network Latency 400 ms – 1500 ms round-trip overhead for TLS handshakes and payload delivery. 0 ms network overhead. Immediate deterministic fast-path or on-chip hardware inference.

2. Choosing the Inference Engine: Why Google MediaPipe Tasks GenAI?

Running Large Language Models (LLMs) on resource-constrained smartphones requires a specialized inference runtime. While alternatives like llama.cpp and ExecuTorch exist in the open-source ecosystem, Google MediaPipe Tasks GenAI was selected for HomeTutorPro due to several architectural advantages:

β€’

First-Class Android NDK Acceleration: Direct GPU, OpenCL, Vulkan, and NPU pipeline optimization tailored by Google for Qualcomm Snapdragon, MediaTek Dimensity, and Tensor chipsets.

β€’

Pure Kotlin Gradle Integration: Eliminates the burden of maintaining fragile manual JNI/C++ wrappers inside the application repository.

β€’

Native Gemma Compatibility: Seamless execution of .task and .bin quantized bundles.

β€’

16 KB Memory Page Size Support: Fully compliant with Android 15 and Android 17 page size requirements in releases 0.10.27+.

3. Clean Architecture Implementation

To prevent vendor lock-in and keep our domain logic pure, we implemented a strict 3-tier Clean Architecture with Unidirectional Data Flow (UDF):

ARCHITECTURE
Copy
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               PRESENTATION LAYER (UI)                  β”‚
β”‚   Jetpack Compose β€’ ViewModels β€’ TTS Engine β€’ StateFlowβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ Observes StateFlow / Dispatches Events
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 DOMAIN LAYER (Core)                    β”‚
β”‚   Entities β€’ UseCases β€’ SueAgent                       β”‚
β”‚   (100% Pure Kotlin β€” Zero Android SDK Dependencies)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ Implements Repository Interfaces
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  DATA LAYER (Infra)                    β”‚
β”‚   Room DB β€’ MediaPipe GenAI C++ β€’ Native Audio Servicesβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The domain agent (SueAgent) orchestrates prompt construction and intent detection without knowing whether inference is executed by MediaPipe or a mock test double.

4. Local RAG (Retrieval-Augmented Generation) Over SQLite

What is Local RAG?

Small Language Models (SLMs) with 1B–2B parameters cannot store vast factual databases without severe hallucinations. RAG solves this by:

β€’

1. Retrieving: Querying the local Room (SQLite) database for relevant records (student balances, weekly schedules, shared lesson PDFs).

β€’

2. Augmenting: Injecting structured database results directly into the prompt's --- AVAILABLE DATA --- block.

β€’

3. Generating: Constraining Gemma to answer using exclusively the injected context.

RAG PIPELINE
Copy
User Speech -> Android SpeechRecognizer -> Text Query
       β”‚
       β–Ό
SueViewModel -> sueAgent.detectActionIntent(query)
       β”œβ”€β”€ [Fast-Path: SQL query in 0 ms]
       └── [Generative Path: Local RAG + MediaPipe]
              β”œβ”€β”€ sueAgent.gatherRelevantContext(query)
              β”‚   (Selective Room DB extraction)
              β”œβ”€β”€ Token Budgeting & Relational Grouping
              β”‚   (Kotlin pre-compression)
              β”œβ”€β”€ Gemma Chat Template (2048 token window)
              └── On-Device Inference -> Audio Playback

Token Budgeting & Pre-Compression on GPU/NPU

Mobile GPUs operate with strictly bounded VRAM. Dumping uncompressed database rows risks graphics subsystem stalls and Out-Of-Memory (OOM) termination. We enforce proactive compression rules before prompt construction:

β€’

Relational Aggregation: When querying shared study resources: if total files ≀ 15, inject the complete chronological breakdown; if total files > 15, group semantically by student (groupBy { it.studentId }), injecting total counts and only the latest resource per student.

β€’

Context Window Expansion (MAX_TOKENS = 2048): Configured in MediaPipeModelRepository (TEMPERATURE = 0.3f, TOP_K = 20) to allow multi-part queries (e.g. today's classes + free slots + pending debts) without truncating output sentences.

5. Gemma Chat Template & Prompt Engineering

The assembled prompt follows Google's official Gemma formatting:

PROMPT TEMPLATE
Copy
<start_of_turn>user
You are Sue, the intelligent on-device assistant for HomeTutorPro.
Your role is to answer the tutor's questions concisely and accurately.
NEVER invent facts. Use ONLY the data provided under --- AVAILABLE DATA ---.

--- TEMPORAL CONTEXT ---
Current date/time: Tuesday, 05 Oct 2026, 09:30
--- END OF TEMPORAL CONTEXT ---

--- WORKING HOURS (AGENDA BOUNDARIES) ---
Working hours: 08:00 to 22:00
--- END OF WORKING HOURS ---

--- AVAILABLE DATA ---
Schedule for Tuesday:
  16:30–18:00: Lucia Moreno (Algebra)
  18:30–19:30: Carlos (Physics)
Free slots within working hours:
  08:00–16:30, 19:30–22:00
--- END OF DATA ---

User query:
What lessons do I have this afternoon and when do I have free time?
<end_of_turn>
<start_of_turn>model

6. Key Takeaways for Mobile Engineers

β€’

Quantization is Essential: Gemma 3 INT4 reduces model footprint from 4 GB to ~550 MB on disk and ~800 MB active RAM, making on-device execution viable on standard Android hardware (2.5+ GB RAM).

β€’

Pre-Filter in Kotlin: Do not rely on LLMs to perform heavy data filtering. Use Room SQL indexes and relational grouping in Kotlin before feeding context into the model.

β€’

Decouple Inference: Wrap your ML engine behind a clean domain interface to guarantee deterministic unit testability.

πŸ’‘ Architectural Rule of Thumb: Treat the on-device LLM as an articulate summarizer, never as an unconstrained logic engine or database. When combined with deterministic SQLite queries and strict token budgeting, on-device AI delivers zero latency, absolute privacy, and 0€ operating costs.

Β© 2026 Susana CΓ³rdoba Serrano β€’ Built with Kotlin & Kobweb β€’ Deployed with Firebase Hosting
βŒπŸ•ΈοΈ