1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.window;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.os.Bundle;
26 import android.os.IBinder;
27 import android.util.Log;
28 import android.view.IWindowManager;
29 import android.view.WindowManager.LayoutParams.WindowType;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.lang.annotation.Retention;
34 
35 /**
36  * The controller to manage {@link WindowContext}, such as attaching to a window manager node or
37  * detaching from the current attached node. The user must call
38  * {@link #attachToDisplayArea(int, int, Bundle)}, call {@link #attachToWindowToken(IBinder)}
39  * after that if necessary, and then call {@link #detachIfNeeded()} for release.
40  *
41  * @hide
42  */
43 public class WindowContextController {
44     private static final boolean DEBUG_ATTACH = false;
45     private static final String TAG = "WindowContextController";
46 
47     /**
48      * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
49      * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
50      * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}.
51      */
52     @VisibleForTesting
53     public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
54 
55     /**
56      * Status to indicate that the Window Context attach to a
57      * {@link com.android.server.wm.DisplayArea}.
58      */
59     @Retention(SOURCE)
60     @IntDef({AttachStatus.STATUS_INITIALIZED, AttachStatus.STATUS_ATTACHED,
61             AttachStatus.STATUS_DETACHED, AttachStatus.STATUS_FAILED})
62     public @interface AttachStatus{
63         /**
64          * The Window Context haven't attached to a {@link com.android.server.wm.DisplayArea}.
65          */
66         int STATUS_INITIALIZED = 0;
67         /**
68          * The Window Context has already attached to a {@link com.android.server.wm.DisplayArea}.
69          */
70         int STATUS_ATTACHED = 1;
71         /**
72          * The Window Context has detached from a {@link com.android.server.wm.DisplayArea}.
73          */
74         int STATUS_DETACHED = 2;
75         /**
76          * The Window Context fails to attach to a {@link com.android.server.wm.DisplayArea}.
77          */
78         int STATUS_FAILED = 3;
79     }
80     @NonNull
81     private final WindowTokenClient mToken;
82 
83     /**
84      * Window Context Controller constructor
85      *
86      * @param token The token used to attach to a window manager node. It is usually from
87      *              {@link Context#getWindowContextToken()}.
88      */
89     @VisibleForTesting
WindowContextController(@onNull WindowTokenClient token)90     public WindowContextController(@NonNull WindowTokenClient token) {
91         mToken = token;
92     }
93 
94     /**
95      * Attaches the {@code mToken} to a {@link com.android.server.wm.DisplayArea}.
96      *
97      * @param type The window type of the {@link WindowContext}
98      * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
99      * @param options The window context launched option
100      * @throws IllegalStateException if the {@code mToken} has already been attached to a
101      * DisplayArea.
102      */
attachToDisplayArea(@indowType int type, int displayId, @Nullable Bundle options)103     public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) {
104         if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
105             throw new IllegalStateException("A Window Context can be only attached to "
106                     + "a DisplayArea once.");
107         }
108         mAttachedToDisplayArea = getWindowTokenClientController().attachToDisplayArea(
109                 mToken, type, displayId, options)
110                 ? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
111         if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
112             Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
113                     + displayId);
114         } else if (DEBUG_ATTACH) {
115             Log.d(TAG, "attachToDisplayArea success, type:" + type + ", displayId:"
116                     + displayId);
117         }
118     }
119 
120     /**
121      * Switches to attach the window context to a window token.
122      * <p>
123      * Note that the context should have been attached to a
124      * {@link com.android.server.wm.DisplayArea} by {@link #attachToDisplayArea(int, int, Bundle)}
125      * before attaching to a window token, and the window token's type must match the window
126      * context's type.
127      * </p><p>
128      * A {@link WindowContext} can only attach to a specific window manager node, which is either a
129      * {@link com.android.server.wm.DisplayArea} by calling
130      * {@link #attachToDisplayArea(int, int, Bundle)} or the latest attached {@code windowToken}
131      * although this API is allowed to be called multiple times.
132      * </p>
133      * @throws IllegalStateException if the {@code mClientToken} has not yet attached to
134      * a {@link com.android.server.wm.DisplayArea} by
135      * {@link #attachToDisplayArea(int, int, Bundle)}.
136      *
137      * @see WindowProviderService#attachToWindowToken(IBinder))
138      * @see IWindowManager#attachWindowContextToWindowToken
139      */
attachToWindowToken(@onNull IBinder windowToken)140     public void attachToWindowToken(@NonNull IBinder windowToken) {
141         if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
142             throw new IllegalStateException("The Window Context should have been attached"
143                     + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
144         }
145         if (!getWindowTokenClientController().attachToWindowToken(mToken, windowToken)) {
146             Log.e(TAG, "attachToWindowToken fail");
147         }
148     }
149 
150     /** Detaches the window context from the node it's currently associated with. */
detachIfNeeded()151     public void detachIfNeeded() {
152         if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
153             getWindowTokenClientController().detachIfNeeded(mToken);
154             mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
155             if (DEBUG_ATTACH) {
156                 Log.d(TAG, "Detach Window Context.");
157             }
158         }
159     }
160 
161     /** Gets the {@link WindowTokenClientController}. */
162     @VisibleForTesting
163     @NonNull
getWindowTokenClientController()164     public WindowTokenClientController getWindowTokenClientController() {
165         return WindowTokenClientController.getInstance();
166     }
167 }
168