/* * Copyright (C) 2021 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 static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManager.LayoutParams.WindowType; import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; /** * The controller to manage {@link WindowContext}, such as attaching to a window manager node or * detaching from the current attached node. The user must call * {@link #attachToDisplayArea(int, int, Bundle)}, call {@link #attachToWindowToken(IBinder)} * after that if necessary, and then call {@link #detachIfNeeded()} for release. * * @hide */ public class WindowContextController { private static final boolean DEBUG_ATTACH = false; private static final String TAG = "WindowContextController"; /** * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}. */ @VisibleForTesting public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED; /** * Status to indicate that the Window Context attach to a * {@link com.android.server.wm.DisplayArea}. */ @Retention(SOURCE) @IntDef({AttachStatus.STATUS_INITIALIZED, AttachStatus.STATUS_ATTACHED, AttachStatus.STATUS_DETACHED, AttachStatus.STATUS_FAILED}) public @interface AttachStatus{ /** * The Window Context haven't attached to a {@link com.android.server.wm.DisplayArea}. */ int STATUS_INITIALIZED = 0; /** * The Window Context has already attached to a {@link com.android.server.wm.DisplayArea}. */ int STATUS_ATTACHED = 1; /** * The Window Context has detached from a {@link com.android.server.wm.DisplayArea}. */ int STATUS_DETACHED = 2; /** * The Window Context fails to attach to a {@link com.android.server.wm.DisplayArea}. */ int STATUS_FAILED = 3; } @NonNull private final WindowTokenClient mToken; /** * Window Context Controller constructor * * @param token The token used to attach to a window manager node. It is usually from * {@link Context#getWindowContextToken()}. */ @VisibleForTesting public WindowContextController(@NonNull WindowTokenClient token) { mToken = token; } /** * Attaches the {@code mToken} to a {@link com.android.server.wm.DisplayArea}. * * @param type The window type of the {@link WindowContext} * @param displayId The {@link Context#getDisplayId() ID of display} to associate with * @param options The window context launched option * @throws IllegalStateException if the {@code mToken} has already been attached to a * DisplayArea. */ public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) { if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) { throw new IllegalStateException("A Window Context can be only attached to " + "a DisplayArea once."); } mAttachedToDisplayArea = getWindowTokenClientController().attachToDisplayArea( mToken, type, displayId, options) ? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED; if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) { Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:" + displayId); } else if (DEBUG_ATTACH) { Log.d(TAG, "attachToDisplayArea success, type:" + type + ", displayId:" + displayId); } } /** * Switches to attach the window context to a window token. *
* Note that the context should have been attached to a * {@link com.android.server.wm.DisplayArea} by {@link #attachToDisplayArea(int, int, Bundle)} * before attaching to a window token, and the window token's type must match the window * context's type. *
* A {@link WindowContext} can only attach to a specific window manager node, which is either a * {@link com.android.server.wm.DisplayArea} by calling * {@link #attachToDisplayArea(int, int, Bundle)} or the latest attached {@code windowToken} * although this API is allowed to be called multiple times. *
* @throws IllegalStateException if the {@code mClientToken} has not yet attached to * a {@link com.android.server.wm.DisplayArea} by * {@link #attachToDisplayArea(int, int, Bundle)}. * * @see WindowProviderService#attachToWindowToken(IBinder)) * @see IWindowManager#attachWindowContextToWindowToken */ public void attachToWindowToken(@NonNull IBinder windowToken) { if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) { throw new IllegalStateException("The Window Context should have been attached" + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea); } if (!getWindowTokenClientController().attachToWindowToken(mToken, windowToken)) { Log.e(TAG, "attachToWindowToken fail"); } } /** Detaches the window context from the node it's currently associated with. */ public void detachIfNeeded() { if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) { getWindowTokenClientController().detachIfNeeded(mToken); mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED; if (DEBUG_ATTACH) { Log.d(TAG, "Detach Window Context."); } } } /** Gets the {@link WindowTokenClientController}. */ @VisibleForTesting @NonNull public WindowTokenClientController getWindowTokenClientController() { return WindowTokenClientController.getInstance(); } }