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!