From 6a958c1a10c75af39ceab11afa9c29e1f91f4c0b Mon Sep 17 00:00:00 2001 From: Kazeia Team Date: Tue, 14 Apr 2026 22:46:58 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20MemoryOptimizer=20=E2=80=94=20reclaim?= =?UTF-8?q?=20wasn't=20worth=20the=20footprint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kill-background approach worked at startup (−1.6 GB) but respawns pulled most of that back within 1–3 min, and the periodic sweep only kept the first sweep's reclaim stable rather than going lower. Net practical benefit over "just let Android manage it" is small, not worth a custom optimizer + normal-perm + file maintenance. Removes: - MemoryOptimizer.kt - KazeiaService.onCreate calls to freeRamForModels / startPeriodicOptimizer - KILL_BACKGROUND_PROCESSES permission from the manifest Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/src/main/AndroidManifest.xml | 7 - .../java/com/kazeia/service/KazeiaService.kt | 9 - .../com/kazeia/service/MemoryOptimizer.kt | 166 ------------------ 3 files changed, 182 deletions(-) delete mode 100644 kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt diff --git a/kazeia-android/app/src/main/AndroidManifest.xml b/kazeia-android/app/src/main/AndroidManifest.xml index 930e896..e2ec260 100644 --- a/kazeia-android/app/src/main/AndroidManifest.xml +++ b/kazeia-android/app/src/main/AndroidManifest.xml @@ -9,13 +9,6 @@ - - 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 deleted file mode 100644 index 3321f2e..0000000 --- a/kazeia-android/app/src/main/java/com/kazeia/service/MemoryOptimizer.kt +++ /dev/null @@ -1,166 +0,0 @@ -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 - * processes out of memory. Uses `ActivityManager.killBackgroundProcesses` - * which: - * - only kills processes that are NOT in foreground or bound to something - * foreground (so no user-visible disruption), - * - leaves the package installed/launchable — the OS will respawn it on - * demand if the user opens the app or a push arrives, - * - requires only `KILL_BACKGROUND_PROCESSES`, a normal permission. - * - * Call [freeRamForModels] once, early in KazeiaService.onCreate, BEFORE - * loading Qwen3-4B (which needs ~3.2 GB resident to avoid swap thrashing). - * - * The list below was picked by reading `dumpsys meminfo` on a fresh boot - * of a OnePlus Pad 3 (ColorOS 16): every package here is either a Google - * background feature Kazeia doesn't use (Wallet, Chromecast, YouTube - * prefetch, …) or an OPLUS feature that re-spawns harmlessly when needed. - * We deliberately avoid Play Services core, input method, launcher, - * system_server, systemui, surfaceflinger, audioserver, HAL services — - * killing any of those would break the UI or audio routing. - */ -object MemoryOptimizer { - - private const val TAG = "MemoryOptimizer" - - /** Packages safe to evict. Order matches approximate RAM savings. */ - private val KILL_TARGETS = listOf( - // Google optional features - "com.google.android.googlequicksearchbox", - "com.google.android.youtube", - "com.google.android.apps.walletnfcrel", - "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", - "com.coloros.assistantscreen", - "com.coloros.weather.service", - "com.heytap.mcs", - "com.heytap.accessory", - "com.oplus.cosa", - "com.oplus.pantanal.ums", - "com.oplus.nhs", - "com.oplus.midas", - "com.oplus.olc", - "com.oplus.deepthinker", - "com.oplus.blur", - "com.oplus.statistics.rom", - "com.oplus.powermonitor", - "com.oplus.romupdate", - "com.oplus.location", - "com.oplus.gesture", - "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" - ) - - /** - * Ask ActivityManager to drop background processes for every package - * in [KILL_TARGETS] that is installed. Returns a (attempted, skipped) - * pair for logging. Safe to call from any thread. - */ - fun freeRamForModels(context: Context, log: (String) -> Unit = {}): Pair { - val am = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager - ?: run { log("$TAG: ActivityManager unavailable"); return 0 to 0 } - val pm = context.packageManager - val memBefore = readAvailMb(am) - - var killed = 0 - var skipped = 0 - for (pkg in KILL_TARGETS) { - try { - // Only attempt for installed packages — unknown packages - // would silently no-op but still spam the audit log. - pm.getPackageInfo(pkg, 0) - am.killBackgroundProcesses(pkg) - killed++ - } catch (_: android.content.pm.PackageManager.NameNotFoundException) { - skipped++ - } catch (e: SecurityException) { - log("$TAG: killBackgroundProcesses($pkg) denied: ${e.message}") - skipped++ - } catch (e: Exception) { - log("$TAG: killBackgroundProcesses($pkg) failed: ${e.message}") - skipped++ - } - } - - // Give the kernel a moment to reclaim pages before we report. - Thread.sleep(250) - val memAfter = readAvailMb(am) - log("$TAG: killed=$killed skipped=$skipped; avail RAM ${memBefore} MB → ${memAfter} MB (+${memAfter - memBefore} MB)") - return killed to skipped - } - - private fun readAvailMb(am: ActivityManager): Long { - val info = ActivityManager.MemoryInfo() - 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) - } - } -}