/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.process; import android.content.ComponentCallbacks2; import android.content.res.Configuration; import android.util.Log; import androidx.annotation.NonNull; import org.mozilla.gecko.GeckoAppShell; public class MemoryController implements ComponentCallbacks2 { private static final String LOGTAG = "MemoryController"; private long mLastLowMemoryNotificationTime = 0; // Allowed elapsed time between full GCs while under constant memory pressure private static final long LOW_MEMORY_ONGOING_RESET_TIME_MS = 10000; private static final int LOW = 0; private static final int MODERATE = 1; private static final int CRITICAL = 2; private int memoryLevelFromTrim(final int level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE || level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) { return CRITICAL; } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { return MODERATE; } return LOW; } public void onTrimMemory(final int level) { Log.i(LOGTAG, "onTrimMemory(" + level + ")"); onMemoryNotification(memoryLevelFromTrim(level)); } @Override public void onConfigurationChanged(final @NonNull Configuration newConfig) {} public void onLowMemory() { Log.i(LOGTAG, "onLowMemory"); onMemoryNotification(CRITICAL); } private void onMemoryNotification(final int level) { if (level == LOW) { // The trim level is too low to be actionable return; } // See nsIMemory.idl for descriptions of the various arguments to the "memory-pressure" // observer. final String observerArg; final long currentNotificationTime = System.currentTimeMillis(); if (level == CRITICAL || (currentNotificationTime - mLastLowMemoryNotificationTime) >= LOW_MEMORY_ONGOING_RESET_TIME_MS) { // We do a full "low-memory" notification for both new and last-ditch onTrimMemory requests. observerArg = "low-memory"; mLastLowMemoryNotificationTime = currentNotificationTime; } else { // If it has been less than ten seconds since the last time we sent a "low-memory" // notification, we send a "low-memory-ongoing" notification instead. // This prevents Gecko from re-doing full GC's repeatedly over and over in succession, // as they are expensive and quickly result in diminishing returns. observerArg = "low-memory-ongoing"; } GeckoAppShell.notifyObservers("memory-pressure", observerArg); } }