diff --git a/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt b/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt index 08d240a..b77db14 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt @@ -447,8 +447,10 @@ class KazeiaService : Service() { // on their own, pushing Qwen3-4B's 3.2 GB of weights into ZRAM // swap and causing page faults on every inference. Nudging the // background apps out (see MemoryOptimizer.KILL_TARGETS) typically - // reclaims 600–900 MB so the models stay resident. + // reclaims 1.5 GB on first run, then re-killed processes respawn + // over 1–3 minutes so a periodic sweep keeps them contained. MemoryOptimizer.freeRamForModels(this) { msg -> log(msg) } + MemoryOptimizer.startPeriodicOptimizer(this, serviceScope) { msg -> log(msg) } initializeComponents() } diff --git a/kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt b/kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt index 88fee01..3321f2e 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt @@ -2,6 +2,12 @@ package com.kazeia.service import android.app.ActivityManager import android.content.Context +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch /** * Frees RAM for Kazeia's ML models by nudging non-essential background @@ -37,9 +43,20 @@ object MemoryOptimizer { "com.google.android.apps.chromecast.app", "com.google.android.aicore", "com.google.android.apps.messaging", + "com.google.android.apps.tachyon", // Google Meet/Duo (resp. 96 MB) "com.google.android.gm", "com.google.android.as", "com.google.android.as.oss", + // Additional targets discovered as respawning heavy after the + // first kill sweep on OnePlus Pad 3 — all user-facing apps that + // we don't need while Kazeia is the active task. + "com.google.android.apps.photos", + "com.google.android.calendar", + "com.android.providers.calendar", + "com.google.android.contacts", + "com.android.vending", // Play Store (resp. 150 MB) + "com.google.android.rkpdapp", + "android.process.acore", // OPLUS / ColorOS optional features "com.oneplus.deskclock", "com.coloros.smartsidebar", @@ -62,6 +79,10 @@ object MemoryOptimizer { "com.oplus.appplatform", "com.oplus.persist.multimedia", "com.oplus.nas", + "com.oplus.notificationmanager", + "com.oplus.safecenter", + "com.oplus.securitypermission", + "com.oplus.sau", // Qualcomm workload profiler — perf hints only, respawns "com.qualcomm.qti.workloadclassifier" ) @@ -109,4 +130,37 @@ object MemoryOptimizer { am.getMemoryInfo(info) return info.availMem / (1024 * 1024) } + + /** + * Kick off a background coroutine that re-runs [freeRamForModels] + * every [periodMs]. ColorOS's ActivityManager auto-respawns killed + * apps within 1–3 minutes (observed: quicksearchbox coming back at + * 210 MB, photos/calendar/vending spawning fresh), so a single + * startup sweep isn't enough — periodic pruning is needed while + * Kazeia holds its models in memory. + * + * Cheap (~100 ms total per sweep, mostly the intentional 250 ms + * settle sleep in freeRamForModels), so 60 s cadence is safe. The + * returned [kotlinx.coroutines.Job] should be cancelled by the + * caller when the service stops, to avoid waking the CPU after + * shutdown. + */ + fun startPeriodicOptimizer( + context: Context, + scope: CoroutineScope, + periodMs: Long = 60_000L, + log: (String) -> Unit = {} + ): Job = scope.launch(Dispatchers.IO) { + // First run already happened at service onCreate — skip the + // initial interval so we don't double-sweep on startup. + delay(periodMs) + while (currentCoroutineContext()[Job]?.isActive != false) { + try { + freeRamForModels(context, log) + } catch (e: Exception) { + log("$TAG: periodic sweep failed: ${e.message}") + } + delay(periodMs) + } + } }