1 /*
2  * Copyright (C) 2006 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.view;
18 
19 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
20 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
21 import static android.window.WindowProviderService.isWindowProviderService;
22 
23 import android.annotation.CallbackExecutor;
24 import android.annotation.IntRange;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.UiContext;
28 import android.compat.annotation.UnsupportedAppUsage;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.graphics.Bitmap;
32 import android.graphics.Region;
33 import android.os.Bundle;
34 import android.os.IBinder;
35 import android.os.RemoteException;
36 import android.os.StrictMode;
37 import android.window.ITaskFpsCallback;
38 import android.window.TaskFpsCallback;
39 import android.window.WindowContext;
40 import android.window.WindowMetricsController;
41 import android.window.WindowProvider;
42 
43 import com.android.internal.annotations.GuardedBy;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.os.IResultReceiver;
46 
47 import java.util.ArrayList;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Objects;
51 import java.util.Set;
52 import java.util.concurrent.Executor;
53 import java.util.function.Consumer;
54 import java.util.function.IntConsumer;
55 
56 /**
57  * Provides low-level communication with the system window manager for
58  * operations that are bound to a particular context, display or parent window.
59  * Instances of this object are sensitive to the compatibility info associated
60  * with the running application.
61  *
62  * This object implements the {@link ViewManager} interface,
63  * allowing you to add any View subclass as a top-level window on the screen.
64  * Additional window manager specific layout parameters are defined for
65  * control over how windows are displayed.  It also implements the {@link WindowManager}
66  * interface, allowing you to control the displays attached to the device.
67  *
68  * <p>Applications will not normally use WindowManager directly, instead relying
69  * on the higher-level facilities in {@link android.app.Activity} and
70  * {@link android.app.Dialog}.
71  *
72  * <p>Even for low-level window manager access, it is almost never correct to use
73  * this class.  For example, {@link android.app.Activity#getWindowManager}
74  * provides a window manager for adding windows that are associated with that
75  * activity -- the window manager will not normally allow you to add arbitrary
76  * windows that are not associated with an activity.
77  *
78  * @see WindowManager
79  * @see WindowManagerGlobal
80  * @hide
81  */
82 public final class WindowManagerImpl implements WindowManager {
83     @UnsupportedAppUsage
84     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
85     @UiContext
86     @VisibleForTesting
87     public final Context mContext;
88     private final Window mParentWindow;
89 
90     /**
91      * If {@link LayoutParams#token} is {@code null} and no parent window is specified, the value
92      * of {@link LayoutParams#token} will be overridden to {@code mDefaultToken}.
93      */
94     private IBinder mDefaultToken;
95 
96     /**
97      * This token will be set to {@link LayoutParams#mWindowContextToken} and used to receive
98      * configuration changes from the server side.
99      */
100     @Nullable
101     private final IBinder mWindowContextToken;
102 
103     @GuardedBy("mOnFpsCallbackListenerProxies")
104     private final ArrayList<OnFpsCallbackListenerProxy> mOnFpsCallbackListenerProxies =
105             new ArrayList<>();
106 
107     /** A controller to handle {@link WindowMetrics} related APIs */
108     @NonNull
109     private final WindowMetricsController mWindowMetricsController;
110 
WindowManagerImpl(Context context)111     public WindowManagerImpl(Context context) {
112         this(context, null /* parentWindow */, null /* clientToken */);
113     }
114 
WindowManagerImpl(Context context, Window parentWindow, @Nullable IBinder windowContextToken)115     private WindowManagerImpl(Context context, Window parentWindow,
116             @Nullable IBinder windowContextToken) {
117         mContext = context;
118         mParentWindow = parentWindow;
119         mWindowContextToken = windowContextToken;
120         mWindowMetricsController = new WindowMetricsController(mContext);
121     }
122 
createLocalWindowManager(Window parentWindow)123     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
124         return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
125     }
126 
createPresentationWindowManager(Context displayContext)127     public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
128         return new WindowManagerImpl(displayContext, mParentWindow, mWindowContextToken);
129     }
130 
131     /** Creates a {@link WindowManager} for a {@link WindowContext}. */
createWindowContextWindowManager(Context context)132     public static WindowManager createWindowContextWindowManager(Context context) {
133         final IBinder clientToken = context.getWindowContextToken();
134         return new WindowManagerImpl(context, null /* parentWindow */, clientToken);
135     }
136 
137     /**
138      * Sets the window token to assign when none is specified by the client or
139      * available from the parent window.
140      *
141      * @param token The default token to assign.
142      */
setDefaultToken(IBinder token)143     public void setDefaultToken(IBinder token) {
144         mDefaultToken = token;
145     }
146 
147     @Override
addView(@onNull View view, @NonNull ViewGroup.LayoutParams params)148     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
149         applyTokens(params);
150         mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
151                 mContext.getUserId());
152     }
153 
154     @Override
updateViewLayout(@onNull View view, @NonNull ViewGroup.LayoutParams params)155     public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
156         applyTokens(params);
157         mGlobal.updateViewLayout(view, params);
158     }
159 
applyTokens(@onNull ViewGroup.LayoutParams params)160     private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
161         if (!(params instanceof WindowManager.LayoutParams)) {
162             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
163         }
164         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
165         assertWindowContextTypeMatches(wparams.type);
166         // Only use the default token if we don't have a parent window and a token.
167         if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
168             wparams.token = mDefaultToken;
169         }
170         wparams.mWindowContextToken = mWindowContextToken;
171     }
172 
assertWindowContextTypeMatches(@ayoutParams.WindowType int windowType)173     private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
174         if (!(mContext instanceof WindowProvider)) {
175             return;
176         }
177         // Don't need to check sub-window type because sub window should be allowed to be attached
178         // to the parent window.
179         if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
180             return;
181         }
182         final WindowProvider windowProvider = (WindowProvider) mContext;
183         if (windowProvider.getWindowType() == windowType) {
184             return;
185         }
186         IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
187                 + " Window Context's window type is " + windowProvider.getWindowType()
188                 + ", while LayoutParams' type is set to " + windowType + "."
189                 + " Please create another Window Context via"
190                 + " createWindowContext(getDisplay(), " + windowType + ", null)"
191                 + " to add window with type:" + windowType);
192         if (!isWindowProviderService(windowProvider.getWindowContextOptions())) {
193             throw exception;
194         }
195         // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple
196         // window types. Usually it's because the Window Context is a WindowProviderService.
197         StrictMode.onIncorrectContextUsed("WindowContext's window type must"
198                 + " match type in WindowManager.LayoutParams", exception);
199     }
200 
201     @Override
removeView(View view)202     public void removeView(View view) {
203         mGlobal.removeView(view, false);
204     }
205 
206     @Override
removeViewImmediate(View view)207     public void removeViewImmediate(View view) {
208         mGlobal.removeView(view, true);
209     }
210 
211     @Override
requestAppKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId)212     public void requestAppKeyboardShortcuts(
213             final KeyboardShortcutsReceiver receiver, int deviceId) {
214         IResultReceiver resultReceiver = new IResultReceiver.Stub() {
215             @Override
216             public void send(int resultCode, Bundle resultData) throws RemoteException {
217                 List<KeyboardShortcutGroup> result =
218                         resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY,
219                                 android.view.KeyboardShortcutGroup.class);
220                 receiver.onKeyboardShortcutsReceived(result);
221             }
222         };
223         try {
224             WindowManagerGlobal.getWindowManagerService()
225                     .requestAppKeyboardShortcuts(resultReceiver, deviceId);
226         } catch (RemoteException e) {
227             throw e.rethrowFromSystemServer();
228         }
229     }
230 
231     @Override
requestImeKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId)232     public void requestImeKeyboardShortcuts(
233             final KeyboardShortcutsReceiver receiver, int deviceId) {
234         IResultReceiver resultReceiver = new IResultReceiver.Stub() {
235             @Override
236             public void send(int resultCode, Bundle resultData) throws RemoteException {
237                 List<KeyboardShortcutGroup> result =
238                         resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY,
239                                 android.view.KeyboardShortcutGroup.class);
240                 receiver.onKeyboardShortcutsReceived(result);
241             }
242         };
243         try {
244             WindowManagerGlobal.getWindowManagerService()
245                     .requestImeKeyboardShortcuts(resultReceiver, deviceId);
246         } catch (RemoteException e) {
247             throw e.rethrowFromSystemServer();
248         }
249     }
250 
251     @Override
getDefaultDisplay()252     public Display getDefaultDisplay() {
253         return mContext.getDisplayNoVerify();
254     }
255 
256     @Override
getCurrentImeTouchRegion()257     public Region getCurrentImeTouchRegion() {
258         try {
259             return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
260         } catch (RemoteException e) {
261         }
262         return null;
263     }
264 
265     @Override
setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow)266     public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
267         try {
268             WindowManagerGlobal.getWindowManagerService()
269                     .setShouldShowWithInsecureKeyguard(displayId, shouldShow);
270         } catch (RemoteException e) {
271         }
272     }
273 
274     @Override
setShouldShowSystemDecors(int displayId, boolean shouldShow)275     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
276         try {
277             WindowManagerGlobal.getWindowManagerService()
278                     .setShouldShowSystemDecors(displayId, shouldShow);
279         } catch (RemoteException e) {
280         }
281     }
282 
283     @Override
shouldShowSystemDecors(int displayId)284     public boolean shouldShowSystemDecors(int displayId) {
285         try {
286             return WindowManagerGlobal.getWindowManagerService().shouldShowSystemDecors(displayId);
287         } catch (RemoteException e) {
288         }
289         return false;
290     }
291 
292     @Override
setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy)293     public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) {
294         try {
295             WindowManagerGlobal.getWindowManagerService().setDisplayImePolicy(displayId, imePolicy);
296         } catch (RemoteException e) {
297         }
298     }
299 
300     @Override
getDisplayImePolicy(int displayId)301     public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
302         try {
303             return WindowManagerGlobal.getWindowManagerService().getDisplayImePolicy(displayId);
304         } catch (RemoteException e) {
305         }
306         return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
307     }
308 
309     @Override
isGlobalKey(int keyCode)310     public boolean isGlobalKey(int keyCode) {
311         try {
312             return WindowManagerGlobal.getWindowManagerService().isGlobalKey(keyCode);
313         } catch (RemoteException e) {
314         }
315         return false;
316     }
317 
318     @Override
getCurrentWindowMetrics()319     public WindowMetrics getCurrentWindowMetrics() {
320         return mWindowMetricsController.getCurrentWindowMetrics();
321     }
322 
323     @Override
getMaximumWindowMetrics()324     public WindowMetrics getMaximumWindowMetrics() {
325         return mWindowMetricsController.getMaximumWindowMetrics();
326     }
327 
328     @Override
329     @NonNull
getPossibleMaximumWindowMetrics(int displayId)330     public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
331         return mWindowMetricsController.getPossibleMaximumWindowMetrics(displayId);
332     }
333 
334     @Override
holdLock(IBinder token, int durationMs)335     public void holdLock(IBinder token, int durationMs) {
336         try {
337             WindowManagerGlobal.getWindowManagerService().holdLock(token, durationMs);
338         } catch (RemoteException e) {
339             throw e.rethrowFromSystemServer();
340         }
341     }
342 
343     @Override
isCrossWindowBlurEnabled()344     public boolean isCrossWindowBlurEnabled() {
345         return CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled();
346     }
347 
348     @Override
addCrossWindowBlurEnabledListener(@onNull Consumer<Boolean> listener)349     public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
350         addCrossWindowBlurEnabledListener(mContext.getMainExecutor(), listener);
351     }
352 
353     @Override
addCrossWindowBlurEnabledListener(@onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> listener)354     public void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
355             @NonNull Consumer<Boolean> listener) {
356         CrossWindowBlurListeners.getInstance().addListener(executor, listener);
357     }
358 
359     @Override
removeCrossWindowBlurEnabledListener(@onNull Consumer<Boolean> listener)360     public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
361         CrossWindowBlurListeners.getInstance().removeListener(listener);
362     }
363 
364     @Override
addProposedRotationListener(@onNull @allbackExecutor Executor executor, @NonNull IntConsumer listener)365     public void addProposedRotationListener(@NonNull @CallbackExecutor Executor executor,
366             @NonNull IntConsumer listener) {
367         Objects.requireNonNull(executor, "executor must not be null");
368         Objects.requireNonNull(listener, "listener must not be null");
369         final IBinder contextToken = Context.getToken(mContext);
370         if (contextToken == null) {
371             throw new UnsupportedOperationException("The context of this window manager instance "
372                     + "must be a UI context, e.g. an Activity or a Context created by "
373                     + "Context#createWindowContext()");
374         }
375         mGlobal.registerProposedRotationListener(contextToken, executor, listener);
376     }
377 
378     @Override
removeProposedRotationListener(@onNull IntConsumer listener)379     public void removeProposedRotationListener(@NonNull IntConsumer listener) {
380         mGlobal.unregisterProposedRotationListener(Context.getToken(mContext), listener);
381     }
382 
383     @Override
isTaskSnapshotSupported()384     public boolean isTaskSnapshotSupported() {
385         try {
386             return WindowManagerGlobal.getWindowManagerService().isTaskSnapshotSupported();
387         } catch (RemoteException e) {
388         }
389         return false;
390     }
391 
392     @Override
registerTaskFpsCallback(@ntRangefrom = 0) int taskId, @NonNull Executor executor, TaskFpsCallback callback)393     public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, @NonNull Executor executor,
394             TaskFpsCallback callback) {
395         final OnFpsCallbackListenerProxy onFpsCallbackListenerProxy =
396                 new OnFpsCallbackListenerProxy(executor, callback);
397         try {
398             WindowManagerGlobal.getWindowManagerService().registerTaskFpsCallback(
399                     taskId, onFpsCallbackListenerProxy);
400         } catch (RemoteException e) {
401             throw e.rethrowFromSystemServer();
402         }
403         synchronized (mOnFpsCallbackListenerProxies) {
404             mOnFpsCallbackListenerProxies.add(onFpsCallbackListenerProxy);
405         }
406     }
407 
408     @Override
unregisterTaskFpsCallback(TaskFpsCallback callback)409     public void unregisterTaskFpsCallback(TaskFpsCallback callback) {
410         synchronized (mOnFpsCallbackListenerProxies) {
411             final Iterator<OnFpsCallbackListenerProxy> iterator =
412                     mOnFpsCallbackListenerProxies.iterator();
413             while (iterator.hasNext()) {
414                 final OnFpsCallbackListenerProxy proxy = iterator.next();
415                 if (proxy.mCallback == callback) {
416                     try {
417                         WindowManagerGlobal.getWindowManagerService()
418                                 .unregisterTaskFpsCallback(proxy);
419                     } catch (RemoteException e) {
420                         throw e.rethrowFromSystemServer();
421                     }
422                     iterator.remove();
423                 }
424             }
425         }
426     }
427 
428     private static class OnFpsCallbackListenerProxy
429             extends ITaskFpsCallback.Stub {
430         private final Executor mExecutor;
431         private final TaskFpsCallback mCallback;
432 
OnFpsCallbackListenerProxy(Executor executor, TaskFpsCallback callback)433         private OnFpsCallbackListenerProxy(Executor executor, TaskFpsCallback callback) {
434             mExecutor = executor;
435             mCallback = callback;
436         }
437 
438         @Override
onFpsReported(float fps)439         public void onFpsReported(float fps) {
440             mExecutor.execute(() -> {
441                 mCallback.onFpsReported(fps);
442             });
443         }
444     }
445 
446     @Override
snapshotTaskForRecents(int taskId)447     public Bitmap snapshotTaskForRecents(int taskId) {
448         try {
449             return WindowManagerGlobal.getWindowManagerService().snapshotTaskForRecents(taskId);
450         } catch (RemoteException e) {
451             e.rethrowAsRuntimeException();
452         }
453         return null;
454     }
455 
getDefaultToken()456     IBinder getDefaultToken() {
457         return mDefaultToken;
458     }
459 
460     @Override
461     @NonNull
notifyScreenshotListeners(int displayId)462     public List<ComponentName> notifyScreenshotListeners(int displayId) {
463         try {
464             return List.copyOf(WindowManagerGlobal.getWindowManagerService()
465                     .notifyScreenshotListeners(displayId));
466         } catch (RemoteException e) {
467             throw e.rethrowFromSystemServer();
468         }
469     }
470 }
471