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

Commit 5d4ef457 authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

WindowManager: Add basics of generic overlay support.

WindowContainer is extended with basic support for overlay at any
level of the hierarchy. Overlays are provided via a
SurfaceControlViewHost. Using SurfaceControlViewHost
instead of WindowState lets us flexibly attach at any level of the
hierarchy. We forward configuration changes over the new
SurfaceControlViewHost side-channel. WMS grows add and removeTaskOverlay
as specific instantiations, to be called first by game overlay
service.

Bug: 213603716
Test: Existing tests pass
Change-Id: I82c427ad9cd42cbbc0c994afdfcf32be39370164
parent 84a60628
Loading
Loading
Loading
Loading
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.content.res.Configuration;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;

import java.util.ArrayList;

/**
 * Utility class to assist WindowContainer in the hosting of
 * SurfacePackage based overlays. Manages overlays inside
 * one parent control, and manages the lifetime of that parent control
 * in order to obscure details from WindowContainer.
 *
 * Also handles multiplexing of event dispatch and tracking of overlays
 * to make things easier for WindowContainer.
 */
class OverlayHost {
    // Lazily initialized when required
    SurfaceControl mSurfaceControl;
    final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
    final WindowManagerService mWmService;

    OverlayHost(WindowManagerService wms) {
        mWmService = wms;
    }

    void requireOverlaySurfaceControl() {
        if (mSurfaceControl == null) {
            final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
                .setContainerLayer()
                .setHidden(true)
                .setName("Overlay Host Leash");

            mSurfaceControl = b.build();
        }
    }

    void setParent(SurfaceControl.Transaction t, SurfaceControl newParent) {
        if (mSurfaceControl == null) {
            return;
        }
        t.reparent(mSurfaceControl, newParent);
        if (newParent != null) {
            t.show(mSurfaceControl);
        } else {
            t.hide(mSurfaceControl);
        }
    }

    void setLayer(SurfaceControl.Transaction t, int layer) {
        if (mSurfaceControl != null) {
            t.setLayer(mSurfaceControl, layer);
        }
    }

    void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
        requireOverlaySurfaceControl();
        mOverlays.add(p);

        SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
        t.reparent(p.getSurfaceControl(), mSurfaceControl)
            .show(p.getSurfaceControl());
        setParent(t,currentParent);
        t.apply();
    }

    boolean removeOverlay(SurfaceControlViewHost.SurfacePackage p) {
        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();

        for (int i = mOverlays.size() - 1; i >= 0; i--) {
           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
           if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
               mOverlays.remove(i);
               t.reparent(l.getSurfaceControl(), null);
               l.release();
           }
        }
        t.apply();
        return mOverlays.size() > 0;
    }

    void dispatchConfigurationChanged(Configuration c) {
        for (int i = mOverlays.size() - 1; i >= 0; i--) {
           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
           try {
               l.getRemoteInterface().onConfigurationChanged(c);
           } catch (Exception e) {
               removeOverlay(l);
           }
        }
    }

    private void dispatchDetachedFromWindow() {
        for (int i = mOverlays.size() - 1; i >= 0; i--) {
            SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
            try {
                l.getRemoteInterface().onDispatchDetachedFromWindow();
            } catch (Exception e) {
                // Oh well we are tearing down anyway.
            }
            l.release();
        }
    }

    void release() {
        dispatchDetachedFromWindow();
        mOverlays.clear();
        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
        t.remove(mSurfaceControl).apply();
        mSurfaceControl = null;
    }
}
+32 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.view.TaskTransitionSpec;
@@ -312,6 +313,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<

    private final List<WindowContainerListener> mListeners = new ArrayList<>();

    private OverlayHost mOverlayHost;

    WindowContainer(WindowManagerService wms) {
        mWmService = wms;
        mTransitionController = mWmService.mAtmService.getTransitionController();
@@ -341,6 +344,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        super.onConfigurationChanged(newParentConfig);
        updateSurfacePositionNonOrganized();
        scheduleAnimation();
        if (mOverlayHost != null) {
            mOverlayHost.dispatchConfigurationChanged(getConfiguration());
        }
    }

    void reparent(WindowContainer newParent, int position) {
@@ -487,6 +493,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
                t.reparent(sc, mSurfaceControl);
            }
        }

        if (mOverlayHost != null) {
            mOverlayHost.setParent(t, mSurfaceControl);
        }

        scheduleAnimation();
    }

@@ -632,6 +643,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
            mLastSurfacePosition.set(0, 0);
            scheduleAnimation();
        }
        if (mOverlayHost != null) {
            mOverlayHost.release();
            mOverlayHost = null;
        }

        // This must happen after updating the surface so that sync transactions can be handled
        // properly.
@@ -2308,6 +2323,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
                wc.assignLayer(t, layer++);
            }
        }
        if (mOverlayHost != null) {
            mOverlayHost.setLayer(t, layer++);
        }
    }

    void assignChildLayers() {
@@ -3570,4 +3588,18 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
    }

    void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
        if (mOverlayHost == null) {
            mOverlayHost = new OverlayHost(mWmService);
        }
        mOverlayHost.addOverlay(overlay, mSurfaceControl);
    }

    void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
        if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
            mOverlayHost.release();
            mOverlayHost = null;
        }
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.view.IWindow;
import android.view.InputChannel;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControlViewHost;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;

@@ -767,4 +768,16 @@ public abstract class WindowManagerInternal {
     *         {@code false} otherwise.
     */
    public abstract boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken);

    /**
     * Internal methods for other parts of SystemServer to manage
     * SurfacePackage based overlays on tasks.
     *
     * Callers prepare a view hierarchy with SurfaceControlViewHost
     * and send the package to WM here. The remote view hierarchy will receive
     * configuration change, lifecycle events, etc, forwarded over the
     * ISurfaceControlViewHost interface inside the SurfacePackage.
     */
    public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
    public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
}
+23 −0
Original line number Diff line number Diff line
@@ -267,6 +267,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.ScrollCaptureResponse;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.TaskTransitionSpec;
import android.view.View;
@@ -7891,6 +7892,28 @@ public class WindowManagerService extends IWindowManager.Stub
        public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
            return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
        }

        @Override
        public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
            synchronized (mGlobalLock) {
                final Task task = mRoot.getRootTask(taskId);
                if (task == null) {
                    throw new IllegalArgumentException("no task with taskId" + taskId);
                }
                task.addOverlay(overlay);
            }
        }

        @Override
        public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
            synchronized (mGlobalLock) {
                final Task task = mRoot.getRootTask(taskId);
                if (task == null) {
                    throw new IllegalArgumentException("no task with taskId" + taskId);
                }
                task.removeOverlay(overlay);
            }
        }
    }

    void registerAppFreezeListener(AppFreezeListener listener) {