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

Commit 00106b93 authored by Kazuki Takise's avatar Kazuki Takise Committed by Android (Google) Code Review
Browse files

Merge "Reland: Implement auto-restart on display move" into main

parents ac534e40 1b3ce29b
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -1714,6 +1714,22 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
    @Disabled
    public static final long OVERRIDE_MOUSE_TO_TOUCH = 413207127L;

    /**
     * This change id automatically restarts apps when they move between displays.
     *
     * <p>Some apps don't work well with density change. The override enabled by this change id
     * allows them to automatically restart their process to ensure that UI is rendered based on the
     * correct density. This is disabled by default, and can be enabled by device manufacturers on a
     * per-application basis, controlled via
     * <a href="https://developer.android.com/guide/practices/device-compatibility-mode#device_manufacturer_per-app_overrides">Device manufacturer per-app overrides</a>.
     *
     * @hide
     */
    @ChangeId
    @Overridable
    @Disabled
    public static final long OVERRIDE_AUTO_RESTART_ON_DISPLAY_MOVE = 427878712L;

    /**
     * Optional set of a certificates identifying apps that are allowed to embed this activity. From
     * the "knownActivityEmbeddingCerts" attribute.
+51 −26
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ import static android.view.WindowManager.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENAB
import static android.view.WindowManager.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.hasWindowExtensionsEnabled;
import static android.window.DesktopExperienceFlags.ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE;
import static android.window.DesktopExperienceFlags.ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS;
import static android.window.DesktopExperienceFlags.ENABLE_PIP_PARAMS_UPDATE_NOTIFICATION_BUGFIX;
import static android.window.DesktopExperienceFlags.ENABLE_RESTART_MENU_FOR_CONNECTED_DISPLAYS;
@@ -8778,8 +8779,6 @@ final class ActivityRecord extends WindowToken {
        // configuration.
        mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
        mAppCompatController.getDisplayCompatModePolicy().onProcessRestarted();
        final boolean fullscreenOverrideChanged =
                mAppCompatController.getAspectRatioOverrides().resetSystemFullscreenOverrideCache();

        if (!attachedToProcess()) {
            return;
@@ -8788,7 +8787,52 @@ final class ActivityRecord extends WindowToken {
        // The restarting state avoids removing this record when process is died.
        setState(RESTARTING_PROCESS, "restartActivityProcess");

        if (!mVisibleRequested || mHaveState) {
        if (mTransitionController.isShellTransitionsEnabled()) {
            if (!ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE.isTrue()
                    && killInvisibleProcessOrPrepareForRestart()) {
                return;
            }
            final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
                    mTransitionController, mWmService.mSyncEngine);
            mTransitionController.startCollectOrQueue(transition, (deferred) -> {
                if (ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE.isTrue()
                        && killInvisibleProcessOrPrepareForRestart()) {
                    transition.abort();
                    return;
                }
                if (!ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE.isTrue()
                        && mState != RESTARTING_PROCESS) {
                    transition.abort();
                    return;
                }
                if (!attachedToProcess()) {
                    transition.abort();
                    return;
                }
                final ActionChain chain = mAtmService.mChainTracker.start(
                        "restartProc", transition);
                chain.collect(this);
                // Make sure this will be a change in the transition.
                transition.setKnownConfigChanges(this, CONFIG_WINDOW_CONFIGURATION);
                mTransitionController.requestStartTransition(transition, task,
                        null /* remoteTransition */, null /* displayChange */);
                scheduleStopForRestartProcess();
                mAtmService.mChainTracker.end();
            });
        } else {
            if (killInvisibleProcessOrPrepareForRestart()) {
                return;
            }
            scheduleStopForRestartProcess();
        }
    }

    /**
     * Returns {@code true} if the process is killed as the app is invisible. Otherwise, do some
     * preparation to restart the process.
     */
    private boolean killInvisibleProcessOrPrepareForRestart() {
        if (!mVisibleRequested || (!ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE.isTrue() && mHaveState)) {
            // Kill its process immediately because the activity should be in background.
            // The activity state will be update to {@link #DESTROYED} in
            // {@link ActivityStack#cleanUp} when handling process died.
@@ -8803,9 +8847,11 @@ final class ActivityRecord extends WindowToken {
                }
                mAtmService.mAmInternal.killProcess(wpc.mName, wpc.mUid, "resetConfig");
            });
            return;
            return true;
        }

        final boolean fullscreenOverrideChanged =
                mAppCompatController.getAspectRatioOverrides().resetSystemFullscreenOverrideCache();
        if (fullscreenOverrideChanged) {
            task.updateForceResizeOverridesIfNeeded(this);
        }
@@ -8817,28 +8863,7 @@ final class ActivityRecord extends WindowToken {
        if (ENABLE_RESTART_MENU_FOR_CONNECTED_DISPLAYS.isTrue()) {
            mAtmService.resumeAppSwitches();
        }

        if (mTransitionController.isShellTransitionsEnabled()) {
            final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
                    mTransitionController, mWmService.mSyncEngine);
            mTransitionController.startCollectOrQueue(transition, (deferred) -> {
                if (mState != RESTARTING_PROCESS || !attachedToProcess()) {
                    transition.abort();
                    return;
                }
                final ActionChain chain = mAtmService.mChainTracker.start(
                        "restartProc", transition);
                chain.collect(this);
                // Make sure this will be a change in the transition.
                transition.setKnownConfigChanges(this, CONFIG_WINDOW_CONFIGURATION);
                mTransitionController.requestStartTransition(transition, task,
                        null /* remoteTransition */, null /* displayChange */);
                scheduleStopForRestartProcess();
                mAtmService.mChainTracker.end();
            });
        } else {
            scheduleStopForRestartProcess();
        }
        return false;
    }

    private void scheduleStopForRestartProcess() {
+5 −0
Original line number Diff line number Diff line
@@ -168,6 +168,11 @@ class AppCompatController {
        return mDisplayCompatModePolicy;
    }

    @NonNull
    AppCompatDisplayOverrides getDisplayOverrides() {
        return mAppCompatOverrides.getDisplayOverrides();
    }

    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
        getTransparentPolicy().dump(pw, prefix);
        getLetterboxPolicy().dump(pw, prefix);
+18 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.content.pm.ActivityInfo.CONFIG_COLOR_MODE;
import static android.content.pm.ActivityInfo.CONFIG_DENSITY;
import static android.content.pm.ActivityInfo.CONFIG_TOUCHSCREEN;
import static android.view.Display.TYPE_INTERNAL;
import static android.window.DesktopExperienceFlags.ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE;
import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_COMPAT_MODE;
import static android.window.DesktopExperienceFlags.ENABLE_RESTART_MENU_FOR_CONNECTED_DISPLAYS;

@@ -71,6 +72,15 @@ class AppCompatDisplayCompatModePolicy {
                || (mActivityRecord.inSizeCompatMode() && mDisplayChangedWithoutRestart));
    }

    /**
     * Returns whether the app should be restarted when moved to a different display for app-compat.
     */
    boolean shouldRestartOnDisplayMove() {
        // TODO(b/427878712): Discuss opt-in/out policies.
        return mActivityRecord.mAppCompatController.getDisplayOverrides()
                .shouldRestartOnDisplayMove();
    }

    /**
     * Called when the activity is moved to a different display.
     *
@@ -87,6 +97,14 @@ class AppCompatDisplayCompatModePolicy {
            return;
        }
        mDisplayChangedWithoutRestart = true;

        if (ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE.isTrue() && shouldRestartOnDisplayMove()) {
            // At this point, a transition for moving the app between displays should be running, so
            // the restarting logic below will be queued as a new transition, which means the
            // configuration change for the display move has been processed when the process is
            // restarted. This allows the app to be launched in the latest configuration.
            mActivityRecord.restartProcessIfVisible();
        }
    }

    /**
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.content.pm.ActivityInfo.OVERRIDE_AUTO_RESTART_ON_DISPLAY_MOVE;

import static com.android.server.wm.AppCompatUtils.isChangeEnabled;

import android.annotation.NonNull;

/**
 * Encapsulates app compat configurations and overrides related to display.
 */
class AppCompatDisplayOverrides {

    @NonNull
    private final ActivityRecord mActivityRecord;

    AppCompatDisplayOverrides(@NonNull ActivityRecord activityRecord) {
        mActivityRecord = activityRecord;
    }

    /**
     * Whether the activity should be restarted when moved to a different display.
     */
    boolean shouldRestartOnDisplayMove() {
        return isChangeEnabled(mActivityRecord, OVERRIDE_AUTO_RESTART_ON_DISPLAY_MOVE);
    }
}
Loading