Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d11fdfbe authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

Merge "Don't freeze bindee process if bound with BIND_ALLOW_OOM_MANAGEMENT" into sc-dev

parents 3a5a8b85 37c5375b
Loading
Loading
Loading
Loading
+65 −17
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -80,6 +82,8 @@ public final class CachedAppOptimizer {
            "compact_full_delta_rss_throttle_kb";
    @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE =
            "compact_proc_state_throttle";
    @VisibleForTesting static final String KEY_FREEZER_DEBOUNCE_TIMEOUT =
            "freeze_debounce_timeout";

    // Phenotype sends int configurations and we map them to the strings we'll use on device,
    // preventing a weird string value entering the kernel.
@@ -116,6 +120,10 @@ public final class CachedAppOptimizer {
    // Format of this string should be a comma separated list of integers.
    @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
            String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
    @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L;

    @VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
                Settings.Global.CACHED_APPS_FREEZER_ENABLED);

    @VisibleForTesting
    interface PropertyChangedCallbackForTest {
@@ -141,9 +149,6 @@ public final class CachedAppOptimizer {
    static final int SET_FROZEN_PROCESS_MSG = 3;
    static final int REPORT_UNFREEZE_MSG = 4;

    //TODO:change this static definition into a configurable flag.
    static final long FREEZE_TIMEOUT_MS = 600000;

    static final int DO_FREEZE = 1;
    static final int REPORT_UNFREEZE = 2;

@@ -198,6 +203,8 @@ public final class CachedAppOptimizer {
                                updateMinOomAdjThrottle();
                            } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
                                updateMaxOomAdjThrottle();
                            } else if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
                                updateFreezerDebounceTimeout();
                            }
                        }
                    }
@@ -207,6 +214,23 @@ public final class CachedAppOptimizer {
                }
            };

    private final class SettingsContentObserver extends ContentObserver {
        SettingsContentObserver() {
            super(mAm.mHandler);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            if (CACHED_APP_FREEZER_ENABLED_URI.equals(uri)) {
                synchronized (mPhenotypeFlagLock) {
                    updateUseFreezer();
                }
            }
        }
    }

    private final SettingsContentObserver mSettingsObserver;

    private final Object mPhenotypeFlagLock = new Object();

    // Configured by phenotype. Updates from the server take effect immediately.
@@ -259,6 +283,8 @@ public final class CachedAppOptimizer {
    @GuardedBy("mProcLock")
    private boolean mFreezerOverride = false;

    @VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;

    // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
    // when evaluating throttles that we only consider for "full" compaction, so we don't store
    // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
@@ -293,6 +319,7 @@ public final class CachedAppOptimizer {
        mProcStateThrottle = new HashSet<>();
        mProcessDependencies = processDependencies;
        mTestCallback = callback;
        mSettingsObserver = new SettingsContentObserver();
    }

    /**
@@ -303,6 +330,8 @@ public final class CachedAppOptimizer {
        // TODO: initialize flags to default and only update them if values are set in DeviceConfig
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
        mAm.mContext.getContentResolver().registerContentObserver(
                CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
        synchronized (mPhenotypeFlagLock) {
            updateUseCompaction();
            updateCompactionActions();
@@ -315,6 +344,7 @@ public final class CachedAppOptimizer {
            updateUseFreezer();
            updateMinOomAdjThrottle();
            updateMaxOomAdjThrottle();
            updateFreezerDebounceTimeout();
        }
    }

@@ -367,6 +397,7 @@ public final class CachedAppOptimizer {
                    + " processes.");
            pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
            pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
            pw.println("  " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
            if (DEBUG_COMPACTION) {
                for (Map.Entry<Integer, LastCompactionStats> entry
                        : mLastCompactionStats.entrySet()) {
@@ -627,7 +658,10 @@ public final class CachedAppOptimizer {
            mUseFreezer = isFreezerSupported();
        }

        if (mUseFreezer && mFreezeHandler == null) {
        final boolean useFreezer = mUseFreezer;
        // enableFreezer() would need the global ActivityManagerService lock, post it.
        mAm.mHandler.post(() -> {
            if (useFreezer) {
                Slog.d(TAG_AM, "Freezer enabled");
                enableFreezer(true);

@@ -635,13 +669,17 @@ public final class CachedAppOptimizer {
                    mCachedAppOptimizerThread.start();
                }

                if (mFreezeHandler == null) {
                    mFreezeHandler = new FreezeHandler();
                }

                Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                        Process.THREAD_GROUP_SYSTEM);
            } else {
                Slog.d(TAG_AM, "Freezer disabled");
                enableFreezer(false);
            }
        });
    }

    @GuardedBy("mPhenotypeFlagLock")
@@ -794,6 +832,16 @@ public final class CachedAppOptimizer {
        }
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateFreezerDebounceTimeout() {
        mFreezerDebounceTimeout = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);

        if (mFreezerDebounceTimeout < 0) {
            mFullDeltaRssThrottleKb = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
        }
    }

    private boolean parseProcStateThrottle(String procStateThrottleString) {
        String[] procStates = TextUtils.split(procStateThrottleString, ",");
        mProcStateThrottle.clear();
@@ -818,7 +866,7 @@ public final class CachedAppOptimizer {
        return COMPACT_ACTION_STRING[action];
    }

    // This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
    // This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
    @GuardedBy("mAm")
    void unfreezeTemporarily(ProcessRecord app) {
        if (mUseFreezer) {
@@ -838,7 +886,7 @@ public final class CachedAppOptimizer {
        mFreezeHandler.sendMessageDelayed(
                mFreezeHandler.obtainMessage(
                    SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
                FREEZE_TIMEOUT_MS);
                mFreezerDebounceTimeout);
    }

    @GuardedBy({"mAm", "mProcLock"})
+4 −0
Original line number Diff line number Diff line
@@ -2017,6 +2017,10 @@ public final class OomAdjuster {
                        }
                        String adjType = null;
                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                            // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
                            if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
                                app.mOptRecord.setShouldNotFreeze(true);
                            }
                            // Not doing bind OOM management, so treat
                            // this guy more like a started service.
                            if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
+5 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.am;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;

@@ -28,6 +29,9 @@ final class ProcessCachedOptimizerRecord {

    private final ActivityManagerGlobalLock mProcLock;

    @VisibleForTesting
    static final String IS_FROZEN = "isFrozen";

    /**
     * The last time that this process was compacted.
     */
@@ -169,5 +173,6 @@ final class ProcessCachedOptimizerRecord {
    void dump(PrintWriter pw, String prefix, long nowUptime) {
        pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
        pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
        pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ android_test {
        "androidx.test.ext.truth",
        "androidx.test.runner",
        "androidx.test.rules",
        "cts-wm-util",
        "platform-compat-test-rules",
        "mockito-target-minus-junit4",
        "platform-test-annotations",
+101 −0
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
@@ -61,6 +64,8 @@ import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Tests for {@link ActivityManager}.
@@ -316,6 +321,102 @@ public class ActivityManagerTest {
        }
    }

    @LargeTest
    @Test
    public void testAppFreezerWithAllowOomAdj() throws Exception {
        final long waitFor = 5000;
        boolean freezerWasEnabled = isFreezerEnabled();
        SettingsSession<String> freezerEnabled = null;
        SettingsSession<String> amConstantsSettings = null;
        DeviceConfigSession<Long> freezerDebounceTimeout = null;
        MyServiceConnection autoConnection = null;
        try {
            if (!freezerWasEnabled) {
                freezerEnabled = new SettingsSession<>(
                        Settings.Global.getUriFor(Settings.Global.CACHED_APPS_FREEZER_ENABLED),
                        Settings.Global::getString, Settings.Global::putString);
                freezerEnabled.set("enabled");
                Thread.sleep(waitFor);
                if (!isFreezerEnabled()) {
                    // Still not enabled? Probably because the device doesn't support it.
                    return;
                }
            }
            freezerDebounceTimeout = new DeviceConfigSession<>(
                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    CachedAppOptimizer.KEY_FREEZER_DEBOUNCE_TIMEOUT,
                    DeviceConfig::getLong, CachedAppOptimizer.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
            freezerDebounceTimeout.set(waitFor);

            final String activityManagerConstants = Settings.Global.ACTIVITY_MANAGER_CONSTANTS;
            amConstantsSettings = new SettingsSession<>(
                Settings.Global.getUriFor(activityManagerConstants),
                Settings.Global::getString, Settings.Global::putString);

            amConstantsSettings.set(
                    ActivityManagerConstants.KEY_MAX_SERVICE_INACTIVITY + "=" + waitFor);

            final Intent intent = new Intent();
            intent.setClassName(TEST_APP, TEST_CLASS);

            final CountDownLatch latch = new CountDownLatch(1);
            autoConnection = new MyServiceConnection(latch);
            mContext.bindService(intent, autoConnection,
                    Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT);
            try {
                assertTrue("Timeout to bind to service " + intent.getComponent(),
                        latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
            } catch (InterruptedException e) {
                fail("Unable to bind to service " + intent.getComponent());
            }
            assertFalse(TEST_APP + " shouldn't be frozen now.", isAppFrozen(TEST_APP));

            // Trigger oomAdjUpdate/
            toggleScreenOn(false);
            toggleScreenOn(true);

            // Wait for the freezer kick in if there is any.
            Thread.sleep(waitFor * 4);

            // It still shouldn't be frozen, although it's been in cached state.
            assertFalse(TEST_APP + " shouldn't be frozen now.", isAppFrozen(TEST_APP));
        } finally {
            toggleScreenOn(true);
            if (amConstantsSettings != null) {
                amConstantsSettings.close();
            }
            if (freezerEnabled != null) {
                freezerEnabled.close();
            }
            if (freezerDebounceTimeout != null) {
                freezerDebounceTimeout.close();
            }
            if (autoConnection != null) {
                mContext.unbindService(autoConnection);
            }
        }
    }

    private boolean isFreezerEnabled() throws Exception {
        final String output = runShellCommand("dumpsys activity settings");
        final Matcher matcher = Pattern.compile("\\b" + CachedAppOptimizer.KEY_USE_FREEZER
                + "\\b=\\b(true|false)\\b").matcher(output);
        if (matcher.find()) {
            return Boolean.parseBoolean(matcher.group(1));
        }
        return false;
    }

    private boolean isAppFrozen(String packageName) throws Exception {
        final String output = runShellCommand("dumpsys activity p " + packageName);
        final Matcher matcher = Pattern.compile("\\b" + ProcessCachedOptimizerRecord.IS_FROZEN
                + "\\b=\\b(true|false)\\b").matcher(output);
        if (matcher.find()) {
            return Boolean.parseBoolean(matcher.group(1));
        }
        return false;
    }

    /**
     * Make sure the screen state.
     */
Loading