• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 android.view.WindowManager.LayoutParams.WindowType;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.ActivityThread;
24 import android.app.IApplicationThread;
25 import android.app.servertransaction.WindowContextInfoChangeItem;
26 import android.app.servertransaction.WindowContextWindowRemovalItem;
27 import android.content.Context;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.util.ArrayMap;
32 import android.util.Log;
33 import android.view.IWindowManager;
34 import android.view.WindowManagerGlobal;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.annotations.VisibleForTesting;
38 
39 /**
40  * Singleton controller to manage the attached {@link WindowTokenClient}s, and to dispatch
41  * corresponding window configuration change from server side.
42  * @hide
43  */
44 public class WindowTokenClientController {
45 
46     private static final String TAG = WindowTokenClientController.class.getSimpleName();
47     private static WindowTokenClientController sController;
48 
49     private final Object mLock = new Object();
50     private final IApplicationThread mAppThread = ActivityThread.currentActivityThread()
51             .getApplicationThread();
52 
53     /** Mapping from a client defined token to the {@link WindowTokenClient} it represents. */
54     @GuardedBy("mLock")
55     private final ArrayMap<IBinder, WindowTokenClient> mWindowTokenClientMap = new ArrayMap<>();
56 
57     /** Gets the singleton controller. */
58     @NonNull
getInstance()59     public static WindowTokenClientController getInstance() {
60         synchronized (WindowTokenClientController.class) {
61             if (sController == null) {
62                 sController = new WindowTokenClientController();
63             }
64             return sController;
65         }
66     }
67 
68     /** Overrides the {@link #getInstance()} for test only. */
69     @VisibleForTesting
overrideForTesting(@onNull WindowTokenClientController controller)70     public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
71         synchronized (WindowTokenClientController.class) {
72             sController = controller;
73         }
74     }
75 
76     /** Creates a new instance for test only. */
77     @VisibleForTesting
78     @NonNull
createInstanceForTesting()79     public static WindowTokenClientController createInstanceForTesting() {
80         return new WindowTokenClientController();
81     }
82 
WindowTokenClientController()83     private WindowTokenClientController() {}
84 
85     /** Gets the {@link WindowContext} instance for the token. */
86     @Nullable
getWindowContext(@onNull IBinder clientToken)87     public Context getWindowContext(@NonNull IBinder clientToken) {
88         final WindowTokenClient windowTokenClient;
89         synchronized (mLock) {
90             windowTokenClient = mWindowTokenClientMap.get(clientToken);
91         }
92         return windowTokenClient != null ? windowTokenClient.getContext() : null;
93     }
94 
95     /**
96      * Attaches a {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
97      *
98      * @param client The {@link WindowTokenClient} to attach.
99      * @param type The window type of the {@link WindowContext}
100      * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
101      * @param options The window context launched option
102      * @return {@code true} if attaching successfully.
103      */
attachToDisplayArea(@onNull WindowTokenClient client, @WindowType int type, int displayId, @Nullable Bundle options)104     public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
105             @WindowType int type, int displayId, @Nullable Bundle options) {
106         final WindowContextInfo info;
107         try {
108             info = getWindowManagerService().attachWindowContextToDisplayArea(
109                     mAppThread, client, type, displayId, options);
110         } catch (RemoteException e) {
111             throw e.rethrowFromSystemServer();
112         }
113         if (info == null) {
114             return false;
115         }
116         onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
117         return true;
118     }
119 
120     /**
121      * Attaches a {@link WindowTokenClient} to a {@code DisplayContent}.
122      *
123      * @param client The {@link WindowTokenClient} to attach.
124      * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
125      * @return {@code true} if attaching successfully.
126      */
attachToDisplayContent(@onNull WindowTokenClient client, int displayId)127     public boolean attachToDisplayContent(@NonNull WindowTokenClient client, int displayId) {
128         final IWindowManager wms = getWindowManagerService();
129         // #createSystemUiContext may call this method before WindowManagerService is initialized.
130         if (wms == null) {
131             return false;
132         }
133         final WindowContextInfo info;
134         try {
135             info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
136         } catch (RemoteException e) {
137             throw e.rethrowFromSystemServer();
138         }
139         if (info == null) {
140             return false;
141         }
142         onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
143         return true;
144     }
145 
146     /**
147      * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
148      *
149      * @param client The {@link WindowTokenClient} to attach.
150      * @param windowToken the window token to associated with
151      * @return {@code true} if attaching successfully.
152      */
attachToWindowToken(@onNull WindowTokenClient client, @NonNull IBinder windowToken)153     public boolean attachToWindowToken(@NonNull WindowTokenClient client,
154             @NonNull IBinder windowToken) {
155         final WindowContextInfo info;
156         try {
157             info = getWindowManagerService().attachWindowContextToWindowToken(
158                     mAppThread, client, windowToken);
159         } catch (RemoteException e) {
160             throw e.rethrowFromSystemServer();
161         }
162         if (info == null) {
163             return false;
164         }
165         // We currently report configuration for WindowToken after attached.
166         onWindowContextTokenAttached(client, info, true /* shouldReportConfigChange */);
167         return true;
168     }
169 
170     /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
detachIfNeeded(@onNull WindowTokenClient client)171     public void detachIfNeeded(@NonNull WindowTokenClient client) {
172         synchronized (mLock) {
173             if (mWindowTokenClientMap.remove(client) == null) {
174                 return;
175             }
176         }
177         try {
178             getWindowManagerService().detachWindowContext(client);
179         } catch (RemoteException e) {
180             throw e.rethrowFromSystemServer();
181         }
182     }
183 
onWindowContextTokenAttached(@onNull WindowTokenClient client, @NonNull WindowContextInfo info, boolean shouldReportConfigChange)184     private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
185             @NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
186         synchronized (mLock) {
187             mWindowTokenClientMap.put(client, client);
188         }
189         if (shouldReportConfigChange) {
190             // Should trigger an #onConfigurationChanged callback to the WindowContext. Post the
191             // dispatch in the next loop to prevent the callback from being dispatched before
192             // #onCreate or WindowContext creation..
193             client.postOnConfigurationChanged(info.getConfiguration(), info.getDisplayId());
194         } else {
195             // Apply the config change directly in case users get stale values after WindowContext
196             // creation.
197             client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId(),
198                     false /* shouldReportConfigChange */);
199         }
200     }
201 
202     /** Called when receives {@link WindowContextInfoChangeItem}. */
onWindowContextInfoChanged(@onNull IBinder clientToken, @NonNull WindowContextInfo info)203     public void onWindowContextInfoChanged(@NonNull IBinder clientToken,
204             @NonNull WindowContextInfo info) {
205         final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
206         if (windowTokenClient != null) {
207             windowTokenClient.onConfigurationChanged(info.getConfiguration(), info.getDisplayId());
208         }
209     }
210 
211     /** Called when receives {@link WindowContextWindowRemovalItem}. */
onWindowContextWindowRemoved(@onNull IBinder clientToken)212     public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
213         final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
214         if (windowTokenClient != null) {
215             windowTokenClient.onWindowTokenRemoved();
216         }
217     }
218 
219     @Nullable
getWindowTokenClient(@onNull IBinder clientToken)220     private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
221         final WindowTokenClient windowTokenClient;
222         synchronized (mLock) {
223             windowTokenClient = mWindowTokenClientMap.get(clientToken);
224         }
225         if (windowTokenClient == null) {
226             Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
227         }
228         return windowTokenClient;
229     }
230 
231     /** Gets the {@link IWindowManager}. */
232     @VisibleForTesting
233     @Nullable
getWindowManagerService()234     public IWindowManager getWindowManagerService() {
235         return WindowManagerGlobal.getWindowManagerService();
236     }
237 }
238