Implement time-based memory balloon inflation
This change implements time-based memory balloon inflation for
FerroChrome, gradually increasing memory reclamation while the
application is stopped.
On application stop, the memory balloon is inflated to an initial 10% of
total memory. It then incrementally inflates by an additional 5% every
60 seconds, up to a maximum of 50%. When the application starts, the
balloon is deflated to 0%, and the time-based balloon inflation task is
cancelled.
Bug: b/400590341
Test: Verify Vm inflate balloon by 5% every 60 seconds when App is in
background. The balloon size stops inflating after reaching 50%.
Change-Id: I5870d71dfad73f04d917b572ac3f064abe64e68a
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index 4bfad62..f0539d6 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -38,6 +38,7 @@
import android.system.virtualmachine.VirtualMachineException
import android.util.Log
import android.widget.Toast
+import com.android.internal.annotations.GuardedBy
import com.android.system.virtualmachine.flags.Flags.terminalGuiSupport
import com.android.virtualization.terminal.MainActivity.Companion.TAG
import com.android.virtualization.terminal.Runner.Companion.create
@@ -55,6 +56,7 @@
import java.io.FileOutputStream
import java.io.IOException
import java.lang.RuntimeException
+import java.lang.Math.min
import java.net.InetSocketAddress
import java.net.SocketAddress
import java.nio.file.Files
@@ -75,6 +77,40 @@
private var server: Server? = null
private var debianService: DebianServiceImpl? = null
private var portNotifier: PortNotifier? = null
+ private var mLock = Object()
+ @GuardedBy("mLock")
+ private var currentMemBalloonPercent = 0;
+
+ @GuardedBy("mLock")
+ private val inflateMemBalloonHandler = Handler(Looper.getMainLooper())
+ private val inflateMemBalloonTask: Runnable = object : Runnable {
+ override fun run() {
+ synchronized(mLock) {
+ if (currentMemBalloonPercent < INITIAL_MEM_BALLOON_PERCENT
+ || currentMemBalloonPercent > MAX_MEM_BALLOON_PERCENT
+ ) {
+ Log.e(
+ TAG, "currentBalloonPercent=$currentMemBalloonPercent is invalid," +
+ " should be in range: " +
+ "$INITIAL_MEM_BALLOON_PERCENT~$MAX_MEM_BALLOON_PERCENT"
+ )
+ return
+ }
+ // Increases the balloon size by MEM_BALLOON_PERCENT_STEP% every time
+ if (currentMemBalloonPercent < MAX_MEM_BALLOON_PERCENT) {
+ currentMemBalloonPercent =
+ min(
+ MAX_MEM_BALLOON_PERCENT,
+ currentMemBalloonPercent + MEM_BALLOON_PERCENT_STEP
+ )
+ virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
+ inflateMemBalloonHandler.postDelayed(this,
+ MEM_BALLOON_INFLATE_INTERVAL_MILLIS)
+ }
+ }
+ }
+ }
+
interface VmLauncherServiceCallback {
fun onVmStart()
@@ -99,13 +135,26 @@
// When the app starts, reset the memory balloon to 0%.
// This gives the app maximum available memory.
ApplicationLifeCycleEvent.APP_ON_START -> {
- virtualMachine?.setMemoryBalloonByPercent(0)
+ synchronized(mLock) {
+ inflateMemBalloonHandler.removeCallbacks(inflateMemBalloonTask);
+ currentMemBalloonPercent = 0;
+ virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
+ }
}
ApplicationLifeCycleEvent.APP_ON_STOP -> {
- // When the app stops, inflate the memory balloon to 10%.
- // This allows the system to reclaim memory while the app is in the background.
- // TODO(b/400590341) Inflate the balloon while the application remains Stop status.
- virtualMachine?.setMemoryBalloonByPercent(10)
+ // When the app stops, inflate the memory balloon to INITIAL_MEM_BALLOON_PERCENT.
+ // Inflate the balloon by MEM_BALLOON_PERCENT_STEP every
+ // MEM_BALLOON_INFLATE_INTERVAL_MILLIS milliseconds until reaching
+ // MAX_MEM_BALLOON_PERCENT of total memory. This allows the system to reclaim
+ // memory while the app is in the background.
+ synchronized(mLock) {
+ currentMemBalloonPercent = INITIAL_MEM_BALLOON_PERCENT;
+ virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
+ inflateMemBalloonHandler.postDelayed(
+ inflateMemBalloonTask,
+ MEM_BALLOON_INFLATE_INTERVAL_MILLIS
+ );
+ }
}
else -> {
Log.e(TAG, "unrecognized lifecycle event: $event")
@@ -376,6 +425,11 @@
private const val RESULT_STOP = 1
private const val RESULT_ERROR = 2
+ private const val INITIAL_MEM_BALLOON_PERCENT = 10
+ private const val MAX_MEM_BALLOON_PERCENT = 50
+ private const val MEM_BALLOON_INFLATE_INTERVAL_MILLIS = 60000L
+ private const val MEM_BALLOON_PERCENT_STEP = 5;
+
private fun getMyIntent(context: Context): Intent {
return Intent(context.getApplicationContext(), VmLauncherService::class.java)
}