From 8bae39cab2a9c1ad7827e5d67dae1023f9493a06 Mon Sep 17 00:00:00 2001
From: Charles Chen
Date: Fri, 25 Jun 2021 14:38:55 +0800
Subject: [PATCH] Allow WPS to create windows with multiple type
Before WindowProviderService, Service can add windows with several
window types. This is previously not allowed for WindowProviderService
because a context can only associate with a window container.
However, it may cause regressions because Service is used to add
windows with multiple types.
This CL allows WindowProviderService to do so, but WindowProviderService
can only associate with the window type returned by #getWindowType.
This CL also extracts some methods to WindowContext interface so that
WindowContext and WindowProviderService can reuse the same interface.
Test: atest WindowContextPolicyTests StrictModeTest
Test: atest ContextIsUiContextTest ContextGetDisplayTest
Test: atest WindowContextTest WindowContextTests
fixes: 191959013
Change-Id: Ie16916b370a4cbb8a17ccaec9870d47b4b089390
---
core/api/test-current.txt | 2 +-
.../AbstractInputMethodService.java | 2 +-
core/java/android/view/WindowManagerImpl.java | 35 ++++++++++++
core/java/android/window/WindowContext.java | 57 ++++++++++++-------
core/java/android/window/WindowProvider.java | 39 +++++++++++++
.../android/window/WindowProviderService.java | 36 ++++++++----
.../android/window/WindowTokenClient.java | 2 +
.../server/wm/WindowManagerService.java | 17 ++++--
8 files changed, 154 insertions(+), 36 deletions(-)
create mode 100644 core/java/android/window/WindowProvider.java
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 85c4f31727752..f6b437405049c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3323,7 +3323,7 @@ package android.window {
ctor public WindowProviderService();
method public final void attachToWindowToken(@NonNull android.os.IBinder);
method @NonNull public int getInitialDisplayId();
- method @Nullable public android.os.Bundle getWindowContextOptions();
+ method @CallSuper @Nullable public android.os.Bundle getWindowContextOptions();
method public abstract int getWindowType();
}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 17d4ae6205caa..f8f0970ecfe4e 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -309,7 +309,7 @@ public abstract class AbstractInputMethodService extends WindowProviderService
@Override
@Nullable
public final Bundle getWindowContextOptions() {
- return null;
+ return super.getWindowContextOptions();
}
/** @hide */
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f800991944ac2..20ecaf599921a 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -19,9 +19,12 @@ package android.view;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -36,7 +39,9 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.window.WindowContext;
+import android.window.WindowProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
@@ -145,6 +150,7 @@ public final class WindowManagerImpl implements WindowManager {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
+ assertWindowContextTypeMatches(wparams.type);
// Only use the default token if we don't have a parent window and a token.
if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
wparams.token = mDefaultToken;
@@ -152,6 +158,35 @@ public final class WindowManagerImpl implements WindowManager {
wparams.mWindowContextToken = mWindowContextToken;
}
+ private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
+ if (!(mContext instanceof WindowProvider)) {
+ return;
+ }
+ // Don't need to check sub-window type because sub window should be allowed to be attached
+ // to the parent window.
+ if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
+ return;
+ }
+ final WindowProvider windowProvider = (WindowProvider) mContext;
+ if (windowProvider.getWindowType() == windowType) {
+ return;
+ }
+ IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
+ + " Window Context's window type is " + windowProvider.getWindowType()
+ + ", while LayoutParams' type is set to " + windowType + "."
+ + " Please create another Window Context via"
+ + " createWindowContext(getDisplay(), " + windowType + ", null)"
+ + " to add window with type:" + windowType);
+ if (!windowProvider.getWindowContextOptions().getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE,
+ false)) {
+ throw exception;
+ }
+ // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple
+ // window types. Usually it's because the Window Context is a WindowProviderService.
+ StrictMode.onIncorrectContextUsed("WindowContext's window type must"
+ + " match type in WindowManager.LayoutParams", exception);
+ }
+
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 5d400853540f2..cfccb712127e9 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.view.Display;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -42,29 +43,33 @@ import java.lang.ref.Reference;
* @hide
*/
@UiContext
-public class WindowContext extends ContextWrapper {
+public class WindowContext extends ContextWrapper implements WindowProvider {
private final WindowManager mWindowManager;
- private final @WindowManager.LayoutParams.WindowType int mType;
- private final @Nullable Bundle mOptions;
+ @WindowManager.LayoutParams.WindowType
+ private final int mType;
+ @Nullable
+ private final Bundle mOptions;
private final ComponentCallbacksController mCallbacksController =
new ComponentCallbacksController();
private final WindowContextController mController;
/**
- * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
- * the token.
+ * Default implementation of {@link WindowContext}
+ *
+ * Note that the users should call {@link Context#createWindowContext(Display, int, Bundle)}
+ * to create a {@link WindowContext} instead of using this constructor
+ *
+ * Example usage:
+ *
+ * Bundle options = new Bundle();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * Context windowContext = context.createWindowContext(display, windowType, options);
+ *
*
- * @param base Base {@link Context} for this new instance.
- * @param type Window type to be used with this context.
- * @param options A bundle used to pass window-related options. For example, on device with
- * multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window
- * using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options.
- * Example usage:
- * Bundle options = new Bundle();
- * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
- * Context windowContext = context.createWindowContext(display, type, options);
+ * @param base Base {@link Context} for this new instance.
+ * @param type Window type to be used with this context.
+ * @param options A bundle used to pass window-related options.
* @see DisplayAreaInfo#rootDisplayAreaId
- * @hide
*/
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
super(base);
@@ -110,10 +115,13 @@ public class WindowContext extends ContextWrapper {
@Override
public void destroy() {
- mCallbacksController.clearCallbacks();
- // Called to the base ContextImpl to do final clean-up.
- getBaseContext().destroy();
- Reference.reachabilityFence(this);
+ try {
+ mCallbacksController.clearCallbacks();
+ // Called to the base ContextImpl to do final clean-up.
+ getBaseContext().destroy();
+ } finally {
+ Reference.reachabilityFence(this);
+ }
}
@Override
@@ -130,4 +138,15 @@ public class WindowContext extends ContextWrapper {
void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
mCallbacksController.dispatchConfigurationChanged(newConfig);
}
+
+ @Override
+ public int getWindowType() {
+ return mType;
+ }
+
+ @Nullable
+ @Override
+ public Bundle getWindowContextOptions() {
+ return mOptions;
+ }
}
diff --git a/core/java/android/window/WindowProvider.java b/core/java/android/window/WindowProvider.java
new file mode 100644
index 0000000000000..b078b9362b907
--- /dev/null
+++ b/core/java/android/window/WindowProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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 android.window;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.view.WindowManager.LayoutParams.WindowType;
+
+/**
+ * An interface to provide a non-activity window.
+ * Examples are {@link WindowContext} and {@link WindowProviderService}.
+ *
+ * @hide
+ */
+public interface WindowProvider {
+ /** @hide */
+ String KEY_IS_WINDOW_PROVIDER_SERVICE = "android.windowContext.isWindowProviderService";
+
+ /** Gets the window type of this provider */
+ @WindowType
+ int getWindowType();
+
+ /** Gets the launch options of this provider */
+ @Nullable
+ Bundle getWindowContextOptions();
+}
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index 5171adf168cec..033b9eda9da4b 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -42,21 +42,26 @@ import android.view.WindowManagerImpl;
* {@link WindowContext}, but is represented as {@link Service}.
*
* @see android.inputmethodservice.InputMethodService
- * @see android.accessibilityservice.AccessibilityService
*
* @hide
*/
@TestApi
@UiContext
-public abstract class WindowProviderService extends Service {
+public abstract class WindowProviderService extends Service implements WindowProvider {
+ private final Bundle mOptions;
private final WindowTokenClient mWindowToken = new WindowTokenClient();
private final WindowContextController mController = new WindowContextController(mWindowToken);
private WindowManager mWindowManager;
private boolean mInitialized;
+ public WindowProviderService() {
+ mOptions = new Bundle();
+ mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+ }
+
/**
- * Returns the type of this {@link WindowProviderService}.
+ * Returns the window type of this {@link WindowProviderService}.
* Each inheriting class must implement this method to provide the type of the window. It is
* used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)}
*
@@ -68,15 +73,24 @@ public abstract class WindowProviderService extends Service {
@SuppressLint("OnNameExpected")
// Suppress the lint because it is not a callback and users should provide window type
// so we cannot make it final.
- public abstract @WindowType int getWindowType();
+ @WindowType
+ @Override
+ public abstract int getWindowType();
/**
* Returns the option of this {@link WindowProviderService}.
- * Default is {@code null}. The inheriting class can implement this method to provide the
- * customization {@code option} of the window. It is used similar to {@code options} of
- * {@link Context#createWindowContext(int, Bundle)}
- *
- * @see Context#createWindowContext(int, Bundle)
+ *
+ * The inheriting class can implement this method to provide the customization {@code option} of
+ * the window, but must be based on this method's returned value.
+ * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)}
+ *
+ *
+ * public Bundle getWindowContextOptions() {
+ * final Bundle options = super.getWindowContextOptions();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * return options;
+ * }
+ *
*
* @hide
*/
@@ -85,8 +99,10 @@ public abstract class WindowProviderService extends Service {
// Suppress the lint because it is not a callback and users may override this API to provide
// launch option. Also, the return value of this API is null by default.
@Nullable
+ @CallSuper
+ @Override
public Bundle getWindowContextOptions() {
- return null;
+ return mOptions;
}
/**
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 284b4b98b4c9d..f3e3859b4256e 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -94,6 +94,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
}
+ // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
+ // are inherited from WindowProvider.
/**
* Called when {@link Configuration} updates from the server side receive.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 36809b374525e..2338257c4bb99 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -87,6 +87,7 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
+import static android.window.WindowContext.KEY_IS_WINDOW_PROVIDER_SERVICE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -1730,16 +1731,22 @@ public class WindowManagerService extends IWindowManager.Stub
&& mWindowContextListenerController.hasListener(windowContextToken)) {
final int windowContextType = mWindowContextListenerController
.getWindowType(windowContextToken);
+ final Bundle options = mWindowContextListenerController
+ .getOptions(windowContextToken);
if (type != windowContextType) {
ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
+ " LayoutParams.type should match! Type from LayoutParams is %d,"
+ " but type from WindowContext is %d", type, windowContextType);
- return WindowManagerGlobal.ADD_INVALID_TYPE;
+ // We allow WindowProviderService to add window other than windowContextType,
+ // but the WindowProviderService won't be associated with the window's
+ // WindowToken.
+ if (!options.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false)) {
+ return WindowManagerGlobal.ADD_INVALID_TYPE;
+ }
+ } else {
+ mWindowContextListenerController.registerWindowContainerListener(
+ windowContextToken, token, callingUid, type, options);
}
- final Bundle options = mWindowContextListenerController
- .getOptions(windowContextToken);
- mWindowContextListenerController.registerWindowContainerListener(
- windowContextToken, token, callingUid, type, options);
}
// From now on, no exceptions or errors allowed!