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

Commit c3527d7e authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Add support for SystemUI Window Management"

parents 6b861a1b 22b6bbd8
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.KeyEvent;
@@ -119,6 +120,17 @@ interface IWindowManager
     */
    void setDisplayWindowRotationController(IDisplayWindowRotationController controller);

    /**
     * Adds a root container that a client shell can populate with its own windows (usually via
     * WindowlessWindowManager).
     *
     * @param client an IWindow used for window-level communication (ime, finish draw, etc.).
     * @param windowType used by WM to determine the z-order. This is the same as the window type
     *                   used in {@link WindowManager.LayoutParams}.
     * @return a SurfaceControl to add things to.
     */
    SurfaceControl addShellRoot(int displayId, IWindow client, int windowType);

    /**
     * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is
     * used for recents, where generating the thumbnails of the specs takes a non-trivial amount of
+3 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.wm.DisplayWindowController;
import com.android.systemui.wm.SystemWindows;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -323,6 +324,7 @@ public class Dependency {
    @Inject Lazy<Recents> mRecents;
    @Inject Lazy<StatusBar> mStatusBar;
    @Inject Lazy<DisplayWindowController> mDisplayWindowController;
    @Inject Lazy<SystemWindows> mSystemWindows;

    @Inject
    public Dependency() {
@@ -513,6 +515,7 @@ public class Dependency {
        mProviders.put(Recents.class, mRecents::get);
        mProviders.put(StatusBar.class, mStatusBar::get);
        mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
        mProviders.put(SystemWindows.class, mSystemWindows::get);

        // TODO(b/118592525): to support multi-display , we start to add something which is
        //                    per-display, while others may be global. I think it's time to add
+330 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.systemui.wm;

import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessViewRoot;
import android.view.WindowlessWindowManager;

import com.android.internal.os.IResultReceiver;

import java.util.HashMap;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Represents the "windowing" layer of the System-UI. This layer allows system-ui components to
 * place and manipulate windows without talking to WindowManager.
 */
@Singleton
public class SystemWindows {
    private static final String TAG = "SystemWindows";

    private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
    final HashMap<View, WindowlessViewRoot> mViewRoots = new HashMap<>();
    Context mContext;
    IWindowSession mSession;
    DisplayWindowController mDisplayController;
    IWindowManager mWmService;

    private final DisplayWindowController.DisplayWindowListener mDisplayListener =
            new DisplayWindowController.DisplayWindowListener() {
                @Override
                public void onDisplayAdded(int displayId) { }

                @Override
                public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
                    PerDisplay pd = mPerDisplay.get(displayId);
                    if (pd == null) {
                        return;
                    }
                    pd.updateConfiguration(newConfig);
                }

                @Override
                public void onDisplayRemoved(int displayId) { }
            };

    @Inject
    public SystemWindows(Context context, DisplayWindowController displayController,
            IWindowManager wmService) {
        mContext = context;
        mWmService = wmService;
        mDisplayController = displayController;
        mDisplayController.addDisplayWindowListener(mDisplayListener);
        try {
            mSession = wmService.openSession(
                    new IWindowSessionCallback.Stub() {
                        @Override
                        public void onAnimatorScaleChanged(float scale) {}
                    });
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to create layer", e);
        }
    }

    /**
     * Adds a view to system-ui window management.
     */
    public void addView(View view, WindowManager.LayoutParams attrs, int displayId,
            int windowType) {
        PerDisplay pd = mPerDisplay.get(displayId);
        if (pd == null) {
            pd = new PerDisplay(displayId);
            mPerDisplay.put(displayId, pd);
        }
        pd.addView(view, attrs, windowType);
    }

    /**
     * Removes a view from system-ui window management.
     * @param view
     */
    public void removeView(View view) {
        WindowlessViewRoot root = mViewRoots.remove(view);
        root.die();
    }

    /**
     * Updates the layout params of a view.
     */
    public void updateViewLayout(@NonNull View view, ViewGroup.LayoutParams params) {
        WindowlessViewRoot root = mViewRoots.get(view);
        if (root == null || !(params instanceof WindowManager.LayoutParams)) {
            return;
        }
        view.setLayoutParams(params);
        root.relayout((WindowManager.LayoutParams) params);
    }

    /**
     * Adds a root for system-ui window management with no views. Only useful for IME.
     */
    public void addRoot(int displayId, int windowType) {
        PerDisplay pd = mPerDisplay.get(displayId);
        if (pd == null) {
            pd = new PerDisplay(displayId);
            mPerDisplay.put(displayId, pd);
        }
        pd.addRoot(windowType);
    }

    /**
     * Get the IWindow token for a specific root.
     *
     * @param windowType A window type from {@link android.view.WindowManager}.
     */
    IWindow getWindow(int displayId, int windowType) {
        PerDisplay pd = mPerDisplay.get(displayId);
        if (pd == null) {
            return null;
        }
        return pd.getWindow(windowType);
    }

    private class PerDisplay {
        final int mDisplayId;
        private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>();

        PerDisplay(int displayId) {
            mDisplayId = displayId;
        }

        public void addView(View view, WindowManager.LayoutParams attrs, int windowType) {
            SysUiWindowManager wwm = addRoot(windowType);
            if (wwm == null) {
                Slog.e(TAG, "Unable to create systemui root");
                return;
            }
            final Display display = mDisplayController.getDisplay(mDisplayId);
            WindowlessViewRoot viewRoot = new WindowlessViewRoot(mContext, display, wwm);
            attrs.flags |= FLAG_HARDWARE_ACCELERATED;
            viewRoot.addView(view, attrs);
            mViewRoots.put(view, viewRoot);
        }

        SysUiWindowManager addRoot(int windowType) {
            SysUiWindowManager wwm = mWwms.get(windowType);
            if (wwm != null) {
                return wwm;
            }
            SurfaceControl rootSurface = null;
            ContainerWindow win = new ContainerWindow();
            try {
                rootSurface = mWmService.addShellRoot(mDisplayId, win, windowType);
            } catch (RemoteException e) {
            }
            if (rootSurface == null) {
                Slog.e(TAG, "Unable to get root surfacecontrol for systemui");
                return null;
            }
            Context displayContext = mDisplayController.getDisplayContext(mDisplayId);
            wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win);
            mWwms.put(windowType, wwm);
            return wwm;
        }

        IWindow getWindow(int windowType) {
            SysUiWindowManager wwm = mWwms.get(windowType);
            if (wwm == null) {
                return null;
            }
            return wwm.mContainerWindow;
        }

        void updateConfiguration(Configuration configuration) {
            for (int i = 0; i < mWwms.size(); ++i) {
                mWwms.valueAt(i).updateConfiguration(configuration);
            }
        }
    }

    /**
     * A subclass of WindowlessWindowManager that provides insets to its viewroots.
     */
    public class SysUiWindowManager extends WindowlessWindowManager {
        final int mDisplayId;
        ContainerWindow mContainerWindow;
        public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
                ContainerWindow container) {
            super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
            mContainerWindow = container;
            mDisplayId = displayId;
        }

        @Override
        public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewVisibility, int flags,
                long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
                Rect outVisibleInsets, Rect outStableInsets,
                DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
                SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
            int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
                    viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
                    outContentInsets, outVisibleInsets, outStableInsets,
                    cutout, mergedConfiguration, outSurfaceControl, outInsetsState);
            if (res != 0) {
                return res;
            }
            DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
            outStableInsets.set(dl.stableInsets());
            return 0;
        }

        void updateConfiguration(Configuration configuration) {
            setConfiguration(configuration);
        }
    }

    class ContainerWindow extends IWindow.Stub {
        ContainerWindow() {}

        @Override
        public void resized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets,
                boolean reportDraw, MergedConfiguration newMergedConfiguration, Rect backDropFrame,
                boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
                DisplayCutout.ParcelableWrapper displayCutout) {}

        @Override
        public void locationInParentDisplayChanged(Point offset) {}

        @Override
        public void insetsChanged(InsetsState insetsState) {}

        @Override
        public void insetsControlChanged(InsetsState insetsState,
                InsetsSourceControl[] activeControls) {}

        @Override
        public void showInsets(int types, boolean fromIme) {}

        @Override
        public void hideInsets(int types, boolean fromIme) {}

        @Override
        public void moved(int newX, int newY) {}

        @Override
        public void dispatchAppVisibility(boolean visible) {}

        @Override
        public void dispatchGetNewSurface() {}

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {}

        @Override
        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {}

        @Override
        public void closeSystemDialogs(String reason) {}

        @Override
        public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
                boolean sync) {}

        @Override
        public void dispatchWallpaperCommand(String action, int x, int y,
                int z, Bundle extras, boolean sync) {}

        /* Drag/drop */
        @Override
        public void dispatchDragEvent(DragEvent event) {}

        @Override
        public void updatePointerIcon(float x, float y) {}

        @Override
        public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
                int localValue, int localChanges) {}

        @Override
        public void dispatchWindowShown() {}

        @Override
        public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {}

        @Override
        public void dispatchPointerCaptureChanged(boolean hasCapture) {}
    }
}
+35 −0
Original line number Diff line number Diff line
@@ -167,6 +167,7 @@ import android.os.UserHandle;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -174,6 +175,7 @@ import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
@@ -563,6 +565,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    /** Corner radius that windows should have in order to match the display. */
    private final float mWindowCornerRadius;

    private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>();

    private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
        WindowStateAnimator winAnimator = w.mWinAnimator;
        final ActivityRecord activity = w.mActivityRecord;
@@ -1017,6 +1021,37 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        return token;
    }

    SurfaceControl addShellRoot(@NonNull IWindow client, int windowType) {
        ShellRoot root = mShellRoots.get(windowType);
        if (root != null) {
            if (root.getClient() == client) {
                return root.getSurfaceControl();
            }
            root.clear();
            mShellRoots.remove(windowType);
        }
        root = new ShellRoot(client, this, windowType);
        SurfaceControl rootLeash = root.getSurfaceControl();
        if (rootLeash == null) {
            // Root didn't finish initializing, so don't add it.
            root.clear();
            return null;
        }
        mShellRoots.put(windowType, root);
        SurfaceControl out = new SurfaceControl();
        out.copyFrom(rootLeash);
        return out;
    }

    void removeShellRoot(int windowType) {
        ShellRoot root = mShellRoots.get(windowType);
        if (root == null) {
            return;
        }
        root.clear();
        mShellRoots.remove(windowType);
    }

    /** Changes the display the input window token is housed on to this one. */
    void reParentWindowToken(WindowToken token) {
        final DisplayContent prevDc = token.getDisplayContent();
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.annotation.NonNull;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IWindow;
import android.view.SurfaceControl;

/**
 * Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
 */
public class ShellRoot {
    private static final String TAG = "ShellRoot";
    private final DisplayContent mDisplayContent;
    private IWindow mClient;
    private WindowToken mToken;
    private final IBinder.DeathRecipient mDeathRecipient;
    private SurfaceControl mSurfaceControl = null;

    ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, final int windowType) {
        mDisplayContent = dc;
        mDeathRecipient = () -> mDisplayContent.removeShellRoot(windowType);
        try {
            client.asBinder().linkToDeath(mDeathRecipient, 0);
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to add shell root for layer " + windowType + " on display "
                    + dc.getDisplayId(), e);
            return;
        }
        mClient = client;
        mToken = new WindowToken(
                dc.mWmService, client.asBinder(), windowType, true, dc, true, false);
        mSurfaceControl = mToken.makeChildSurface(null)
                .setContainerLayer().setName("Shell Root Leash " + dc.getDisplayId()).build();
        mToken.getPendingTransaction().show(mSurfaceControl);
    }

    void clear() {
        if (mClient != null) {
            mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mClient = null;
        }
        if (mToken != null) {
            mToken.removeImmediately();
            mToken = null;
        }
    }

    SurfaceControl getSurfaceControl() {
        return mSurfaceControl;
    }

    IWindow getClient() {
        return mClient;
    }
}
Loading