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

Commit 15192d37 authored by Darryl L Johnson's avatar Darryl L Johnson
Browse files

Delay sending configuraton changes to cached processes.

Configuration changes should now be delayed for cached processes until
the process is no longer in a cached state.

Test: WindowProcessControllerTests
Test: ProcessRecordTests
Test: ActivityStarterTests

Bug: 143432064

Change-Id: I0f643038df70c7b66f0e721b991a5b87f87a313c
parent 72aac108
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -237,7 +237,7 @@ class ProcessRecord implements WindowProcessListener {
    long lastTopTime;           // The last time the process was in the TOP state or greater.
    boolean reportLowMemory;    // Set to true when waiting to report low mem
    boolean empty;              // Is this an empty background process?
    private boolean mCached;    // Is this a cached process?
    private volatile boolean mCached;    // Is this a cached process?
    String adjType;             // Debugging: primary thing impacting oom_adj.
    int adjTypeCode;            // Debugging: adj code to report to app.
    Object adjSource;           // Debugging: option dependent object.
@@ -690,10 +690,14 @@ class ProcessRecord implements WindowProcessListener {
    }

    void setCached(boolean cached) {
        if (mCached != cached) {
            mCached = cached;
            mWindowProcessController.onProcCachedStateChanged(cached);
        }
    }

    boolean isCached() {
    @Override
    public boolean isCached() {
        return mCached;
    }

+45 −14
Original line number Diff line number Diff line
@@ -179,6 +179,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio

    // Last configuration that was reported to the process.
    private final Configuration mLastReportedConfiguration;
    // Configuration that is waiting to be dispatched to the process.
    private Configuration mPendingConfiguration;
    private final Configuration mNewOverrideConfig = new Configuration();
    // Registered display id as a listener to override config change
    private int mDisplayId;
@@ -1073,7 +1075,21 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
            return;
        }

        try {
        if (mListener.isCached()) {
            // This process is in a cached state. We will delay delivering the config change to the
            // process until the process is no longer cached.
            if (mPendingConfiguration == null) {
                mPendingConfiguration = new Configuration(config);
            } else {
                mPendingConfiguration.setTo(config);
            }
            return;
        }

        dispatchConfigurationChange(config);
    }

    private void dispatchConfigurationChange(Configuration config) {
        if (mThread == null) {
            if (Build.IS_DEBUGGABLE && mIsImeProcess) {
                // TODO (b/135719017): Temporary log for debugging IME service.
@@ -1083,14 +1099,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
            return;
        }
        if (DEBUG_CONFIGURATION) {
                Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName
                        + " new config " + config);
            Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config);
        }
        if (Build.IS_DEBUGGABLE && mIsImeProcess) {
            // TODO (b/135719017): Temporary log for debugging IME service.
                Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName
                        + " new config " + config);
            Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
        }

        try {
            config.seq = mAtm.increaseConfigurationSeqLocked();
            mAtm.getLifecycleManager().scheduleTransaction(mThread,
                    ConfigurationChangeItem.obtain(config));
@@ -1190,6 +1206,21 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
        return false;
    }

    /**
     * Called to notify WindowProcessController of a change in the process's cached state.
     *
     * @param isCached whether or not the process is cached.
     */
    public void onProcCachedStateChanged(boolean isCached) {
        synchronized (mAtm.mGlobalLock) {
            if (!isCached && mPendingConfiguration != null) {
                final Configuration config = mPendingConfiguration;
                mPendingConfiguration = null;
                dispatchConfigurationChange(config);
            }
        }
    }

    @HotPath(caller = HotPath.OOM_ADJUSTMENT)
    public void onTopProcChanged() {
        synchronized (mAtm.mGlobalLockWithoutBoost) {
+5 −0
Original line number Diff line number Diff line
@@ -51,6 +51,11 @@ public interface WindowProcessListener {
     */
    boolean isRemoved();

    /**
     * Returns true if the process is in a cached state.
     */
    boolean isCached();

    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
    long getCpuTime();

+4 −2
Original line number Diff line number Diff line
@@ -229,11 +229,12 @@ public class ActivityStarterTests extends ActivityTestsBase {
                service.mStackSupervisor, mock(ActivityStartInterceptor.class));
        prepareStarter(launchFlags);
        final IApplicationThread caller = mock(IApplicationThread.class);
        final WindowProcessListener listener = mock(WindowProcessListener.class);

        final WindowProcessController wpc =
                containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
                ? null : new WindowProcessController(
                        service, mock(ApplicationInfo.class), null, 0, -1, null, null);
                        service, mock(ApplicationInfo.class), null, 0, -1, null, listener);
        doReturn(wpc).when(service).getProcessController(anyObject());

        final Intent intent = new Intent();
@@ -680,10 +681,11 @@ public class ActivityStarterTests extends ActivityTestsBase {
        doReturn(realCallingUidProcState).when(mService).getUidState(realCallingUid);
        // foreground activities
        final IApplicationThread caller = mock(IApplicationThread.class);
        final WindowProcessListener listener = mock(WindowProcessListener.class);
        final ApplicationInfo ai = new ApplicationInfo();
        ai.uid = callingUid;
        final WindowProcessController callerApp =
                new WindowProcessController(mService, ai, null, callingUid, -1, null, null);
                new WindowProcessController(mService, ai, null, callingUid, -1, null, listener);
        callerApp.setHasForegroundActivities(hasForegroundActivities);
        doReturn(callerApp).when(mService).getProcessController(caller);
        // caller is recents
+39 −6
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.wm;

import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.INVALID_DISPLAY;

import static org.junit.Assert.assertEquals;
@@ -24,7 +26,9 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

import android.app.IApplicationThread;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.platform.test.annotations.Presubmit;
@@ -53,6 +57,7 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
        mMockListener = mock(WindowProcessListener.class);
        mWpc = new WindowProcessController(
                mService, mock(ApplicationInfo.class), null, 0, -1, null, mMockListener);
        mWpc.setThread(mock(IApplicationThread.class));
    }

    @Test
@@ -132,23 +137,51 @@ public class WindowProcessControllerTests extends ActivityTestsBase {

    @Test
    public void testConfigurationForSecondaryScreen() {
        final WindowProcessController wpc = new WindowProcessController(
                mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
        // By default, the process should not listen to any display.
        assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
        assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());

        // Register to a new display as a listener.
        final DisplayContent display = new TestDisplayContent.Builder(mService, 2000, 1000)
                .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
        wpc.registerDisplayConfigurationListener(display);
        mWpc.registerDisplayConfigurationListener(display);

        assertEquals(display.mDisplayId, wpc.getDisplayId());
        assertEquals(display.mDisplayId, mWpc.getDisplayId());
        final Configuration expectedConfig = mService.mRootWindowContainer.getConfiguration();
        expectedConfig.updateFrom(display.getConfiguration());
        assertEquals(expectedConfig, wpc.getConfiguration());
        assertEquals(expectedConfig, mWpc.getConfiguration());
    }

    @Test
    public void testDelayingConfigurationChange() {
        when(mMockListener.isCached()).thenReturn(false);

        Configuration tmpConfig = new Configuration(mWpc.getConfiguration());
        invertOrientation(tmpConfig);
        mWpc.onConfigurationChanged(tmpConfig);

        // The last reported config should be the current config as the process is not cached.
        Configuration originalConfig = new Configuration(mWpc.getConfiguration());
        assertEquals(mWpc.getLastReportedConfiguration(), originalConfig);

        when(mMockListener.isCached()).thenReturn(true);
        invertOrientation(tmpConfig);
        mWpc.onConfigurationChanged(tmpConfig);

        Configuration newConfig = new Configuration(mWpc.getConfiguration());

        // Last reported config hasn't changed because the process is in a cached state.
        assertEquals(mWpc.getLastReportedConfiguration(), originalConfig);

        mWpc.onProcCachedStateChanged(false);
        assertEquals(mWpc.getLastReportedConfiguration(), newConfig);
    }

    private TestDisplayContent createTestDisplayContentInContainer() {
        return new TestDisplayContent.Builder(mService, 1000, 1500).build();
    }

    private static void invertOrientation(Configuration config) {
        config.orientation = config.orientation == ORIENTATION_PORTRAIT
                ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
    }
}