diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 51416b0af8c75..c285e017571e8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3338,7 +3338,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 9db41c41c31cb..a6e77023790c6 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -294,7 +294,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 c501d23560993..498c00831fd27 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!