diff options
6 files changed, 474 insertions, 0 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index b829c9f36f84..9496827b1a84 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -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; @@ -120,6 +121,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 * time, so we want to move that off the critical path for starting the new activity. diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 6821265df28b..dd38a332df68 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java new file mode 100644 index 000000000000..5ec61c3313f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -0,0 +1,330 @@ +/* + * 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) {} + } +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index aa0e97318eed..9e5734b57f87 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -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(); diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java new file mode 100644 index 000000000000..9732637fdd4d --- /dev/null +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -0,0 +1,74 @@ +/* + * 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; + } +} + diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d1bd7452cfb8..be62b680b880 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3712,6 +3712,26 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public SurfaceControl addShellRoot(int displayId, IWindow client, int windowType) { + if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return null; + } + return dc.addShellRoot(client, windowType); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public int watchRotation(IRotationWatcher watcher, int displayId) { final DisplayContent displayContent; synchronized (mGlobalLock) { |