blob: 97c89d042be0d84f7a314f0f6ea56335f709669b [file] [log] [blame]
/*
* 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.wm.shell.common;
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.Region;
import android.os.Bundle;
import android.os.IBinder;
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.DragEvent;
import android.view.IScrollCaptureResponseListener;
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.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
import java.util.HashMap;
/**
* Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and
* manipulate windows without talking to WindowManager.
*/
public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
private final DisplayController mDisplayController;
private final IWindowManager mWmService;
private IWindowSession mSession;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@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) { }
};
public SystemWindows(DisplayController displayController, IWindowManager wmService) {
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,
@WindowManager.ShellRootLayer int shellRootLayer) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
pd = new PerDisplay(displayId);
mPerDisplay.put(displayId, pd);
}
pd.addView(view, attrs, shellRootLayer);
}
/**
* Removes a view from system-ui window management.
* @param view
*/
public void removeView(View view) {
SurfaceControlViewHost root = mViewRoots.remove(view);
root.release();
}
/**
* Updates the layout params of a view.
*/
public void updateViewLayout(@NonNull View view, ViewGroup.LayoutParams params) {
SurfaceControlViewHost root = mViewRoots.get(view);
if (root == null || !(params instanceof WindowManager.LayoutParams)) {
return;
}
view.setLayoutParams(params);
root.relayout((WindowManager.LayoutParams) params);
}
/**
* Sets the accessibility window for the given {@param shellRootLayer}.
*/
public void setShellRootAccessibilityWindow(int displayId,
@WindowManager.ShellRootLayer int shellRootLayer, View view) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
return;
}
pd.setShellRootAccessibilityWindow(shellRootLayer, view);
}
/**
* Sets the touchable region of a view's window. This will be cropped to the window size.
* @param view
* @param region
*/
public void setTouchableRegion(@NonNull View view, Region region) {
SurfaceControlViewHost root = mViewRoots.get(view);
if (root == null) {
return;
}
WindowlessWindowManager wwm = root.getWindowlessWM();
if (!(wwm instanceof SysUiWindowManager)) {
return;
}
((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region);
}
/**
* Get the IWindow token for a specific root.
*
* @param windowType A window type from {@link WindowManager}.
*/
IWindow getWindow(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
return null;
}
return pd.getWindow(windowType);
}
/**
* Gets the SurfaceControl associated with a root view. This is the same surface that backs the
* ViewRootImpl.
*/
public SurfaceControl getViewSurface(View rootView) {
for (int i = 0; i < mPerDisplay.size(); ++i) {
for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) {
SurfaceControl out = mPerDisplay.valueAt(i).mWwms.valueAt(iWm)
.getSurfaceControlForWindow(rootView);
if (out != null) {
return out;
}
}
}
return null;
}
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,
@WindowManager.ShellRootLayer int shellRootLayer) {
SysUiWindowManager wwm = addRoot(shellRootLayer);
if (wwm == null) {
Slog.e(TAG, "Unable to create systemui root");
return;
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
new SurfaceControlViewHost(
view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
setShellRootAccessibilityWindow(shellRootLayer, view);
}
SysUiWindowManager addRoot(@WindowManager.ShellRootLayer int shellRootLayer) {
SysUiWindowManager wwm = mWwms.get(shellRootLayer);
if (wwm != null) {
return wwm;
}
SurfaceControl rootSurface = null;
ContainerWindow win = new ContainerWindow();
try {
rootSurface = mWmService.addShellRoot(mDisplayId, win, shellRootLayer);
} 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(shellRootLayer, wwm);
return wwm;
}
IWindow getWindow(int windowType) {
SysUiWindowManager wwm = mWwms.get(windowType);
if (wwm == null) {
return null;
}
return wwm.mContainerWindow;
}
void setShellRootAccessibilityWindow(@WindowManager.ShellRootLayer int shellRootLayer,
View view) {
SysUiWindowManager wwm = mWwms.get(shellRootLayer);
if (wwm == null) {
return;
}
try {
mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer,
view != null ? mViewRoots.get(view).getWindowToken() : null);
} catch (RemoteException e) {
Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":"
+ shellRootLayer, e);
}
}
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;
final HashMap<IBinder, SurfaceControl> mLeashForWindow =
new HashMap<IBinder, SurfaceControl>();
public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
ContainerWindow container) {
super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
mContainerWindow = container;
mDisplayId = displayId;
}
void updateConfiguration(Configuration configuration) {
setConfiguration(configuration);
}
SurfaceControl getSurfaceControlForWindow(View rootView) {
synchronized (this) {
return mLeashForWindow.get(getWindowBinder(rootView));
}
}
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("SystemWindowLeash")
.setHidden(false)
.setParent(mRootSurface)
.setCallsite("SysUiWIndowManager#attachToParentSurface").build();
synchronized (this) {
mLeashForWindow.put(window.asBinder(), leash);
}
b.setParent(leash);
}
@Override
public void remove(android.view.IWindow window) throws RemoteException {
super.remove(window);
synchronized(this) {
IBinder token = window.asBinder();
new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
.apply();
mLeashForWindow.remove(token);
}
}
void setTouchableRegionForWindow(View rootView, Region region) {
IBinder token = rootView.getWindowToken();
if (token == null) {
return;
}
setTouchRegion(token, region);
}
}
static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration newMergedConfiguration, boolean forceLayout,
boolean alwaysConsumeSystemBars, int displayId) {}
@Override
public void locationInParentDisplayChanged(Point offset) {}
@Override
public void insetsChanged(InsetsState insetsState, boolean willMove, boolean willResize) {}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls, boolean willMove, boolean willResize) {}
@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,
float zoom, 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 dispatchWindowShown() {}
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {}
@Override
public void requestScrollCapture(IScrollCaptureResponseListener listener) {
try {
listener.onScrollCaptureResponse(
new ScrollCaptureResponse.Builder()
.setDescription("Not Implemented")
.build());
} catch (RemoteException ex) {
// ignore
}
}
}
}