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.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
20 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
21 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
22 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
23 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
24 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
25 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
26 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
27 import static android.window.WindowProviderService.isWindowProviderService;
28 
29 import android.annotation.CallbackExecutor;
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.annotation.UiContext;
33 import android.app.ResourcesManager;
34 import android.compat.annotation.UnsupportedAppUsage;
35 import android.content.Context;
36 import android.content.res.Configuration;
37 import android.graphics.Rect;
38 import android.graphics.Region;
39 import android.os.Bundle;
40 import android.os.IBinder;
41 import android.os.RemoteException;
42 import android.os.StrictMode;
43 import android.window.WindowContext;
44 import android.window.WindowProvider;
45 
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.internal.os.IResultReceiver;
48 
49 import java.util.List;
50 import java.util.concurrent.Executor;
51 import java.util.function.Consumer;
52 
53 /**
54  * Provides low-level communication with the system window manager for
55  * operations that are bound to a particular context, display or parent window.
56  * Instances of this object are sensitive to the compatibility info associated
57  * with the running application.
58  *
59  * This object implements the {@link ViewManager} interface,
60  * allowing you to add any View subclass as a top-level window on the screen.
61  * Additional window manager specific layout parameters are defined for
62  * control over how windows are displayed.  It also implements the {@link WindowManager}
63  * interface, allowing you to control the displays attached to the device.
64  *
65  * <p>Applications will not normally use WindowManager directly, instead relying
66  * on the higher-level facilities in {@link android.app.Activity} and
67  * {@link android.app.Dialog}.
68  *
69  * <p>Even for low-level window manager access, it is almost never correct to use
70  * this class.  For example, {@link android.app.Activity#getWindowManager}
71  * provides a window manager for adding windows that are associated with that
72  * activity -- the window manager will not normally allow you to add arbitrary
73  * windows that are not associated with an activity.
74  *
75  * @see WindowManager
76  * @see WindowManagerGlobal
77  * @hide
78  */
79 public final class WindowManagerImpl implements WindowManager {
80     @UnsupportedAppUsage
81     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
82     @UiContext
83     @VisibleForTesting
84     public final Context mContext;
85     private final Window mParentWindow;
86 
87     /**
88      * If {@link LayoutParams#token} is {@code null} and no parent window is specified, the value
89      * of {@link LayoutParams#token} will be overridden to {@code mDefaultToken}.
90      */
91     private IBinder mDefaultToken;
92 
93     /**
94      * This token will be set to {@link LayoutParams#mWindowContextToken} and used to receive
95      * configuration changes from the server side.
96      */
97     @Nullable
98     private final IBinder mWindowContextToken;
99 
WindowManagerImpl(Context context)100     public WindowManagerImpl(Context context) {
101         this(context, null /* parentWindow */, null /* clientToken */);
102     }
103 
WindowManagerImpl(Context context, Window parentWindow, @Nullable IBinder windowContextToken)104     private WindowManagerImpl(Context context, Window parentWindow,
105             @Nullable IBinder windowContextToken) {
106         mContext = context;
107         mParentWindow = parentWindow;
108         mWindowContextToken = windowContextToken;
109     }
110 
createLocalWindowManager(Window parentWindow)111     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
112         return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
113     }
114 
createPresentationWindowManager(Context displayContext)115     public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
116         return new WindowManagerImpl(displayContext, mParentWindow, mWindowContextToken);
117     }
118 
119     /** Creates a {@link WindowManager} for a {@link WindowContext}. */
createWindowContextWindowManager(Context context)120     public static WindowManager createWindowContextWindowManager(Context context) {
121         final IBinder clientToken = context.getWindowContextToken();
122         return new WindowManagerImpl(context, null /* parentWindow */, clientToken);
123     }
124 
125     /**
126      * Sets the window token to assign when none is specified by the client or
127      * available from the parent window.
128      *
129      * @param token The default token to assign.
130      */
setDefaultToken(IBinder token)131     public void setDefaultToken(IBinder token) {
132         mDefaultToken = token;
133     }
134 
135     @Override
addView(@onNull View view, @NonNull ViewGroup.LayoutParams params)136     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
137         applyTokens(params);
138         mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
139                 mContext.getUserId());
140     }
141 
142     @Override
updateViewLayout(@onNull View view, @NonNull ViewGroup.LayoutParams params)143     public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
144         applyTokens(params);
145         mGlobal.updateViewLayout(view, params);
146     }
147 
applyTokens(@onNull ViewGroup.LayoutParams params)148     private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
149         if (!(params instanceof WindowManager.LayoutParams)) {
150             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
151         }
152         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
153         assertWindowContextTypeMatches(wparams.type);
154         // Only use the default token if we don't have a parent window and a token.
155         if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
156             wparams.token = mDefaultToken;
157         }
158         wparams.mWindowContextToken = mWindowContextToken;
159     }
160 
assertWindowContextTypeMatches(@ayoutParams.WindowType int windowType)161     private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
162         if (!(mContext instanceof WindowProvider)) {
163             return;
164         }
165         // Don't need to check sub-window type because sub window should be allowed to be attached
166         // to the parent window.
167         if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
168             return;
169         }
170         final WindowProvider windowProvider = (WindowProvider) mContext;
171         if (windowProvider.getWindowType() == windowType) {
172             return;
173         }
174         IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
175                 + " Window Context's window type is " + windowProvider.getWindowType()
176                 + ", while LayoutParams' type is set to " + windowType + "."
177                 + " Please create another Window Context via"
178                 + " createWindowContext(getDisplay(), " + windowType + ", null)"
179                 + " to add window with type:" + windowType);
180         if (!isWindowProviderService(windowProvider.getWindowContextOptions())) {
181             throw exception;
182         }
183         // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple
184         // window types. Usually it's because the Window Context is a WindowProviderService.
185         StrictMode.onIncorrectContextUsed("WindowContext's window type must"
186                 + " match type in WindowManager.LayoutParams", exception);
187     }
188 
189     @Override
removeView(View view)190     public void removeView(View view) {
191         mGlobal.removeView(view, false);
192     }
193 
194     @Override
removeViewImmediate(View view)195     public void removeViewImmediate(View view) {
196         mGlobal.removeView(view, true);
197     }
198 
199     @Override
requestAppKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId)200     public void requestAppKeyboardShortcuts(
201             final KeyboardShortcutsReceiver receiver, int deviceId) {
202         IResultReceiver resultReceiver = new IResultReceiver.Stub() {
203             @Override
204             public void send(int resultCode, Bundle resultData) throws RemoteException {
205                 List<KeyboardShortcutGroup> result =
206                         resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
207                 receiver.onKeyboardShortcutsReceived(result);
208             }
209         };
210         try {
211             WindowManagerGlobal.getWindowManagerService()
212                 .requestAppKeyboardShortcuts(resultReceiver, deviceId);
213         } catch (RemoteException e) {
214         }
215     }
216 
217     @Override
getDefaultDisplay()218     public Display getDefaultDisplay() {
219         return mContext.getDisplayNoVerify();
220     }
221 
222     @Override
getCurrentImeTouchRegion()223     public Region getCurrentImeTouchRegion() {
224         try {
225             return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
226         } catch (RemoteException e) {
227         }
228         return null;
229     }
230 
231     @Override
setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow)232     public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
233         try {
234             WindowManagerGlobal.getWindowManagerService()
235                     .setShouldShowWithInsecureKeyguard(displayId, shouldShow);
236         } catch (RemoteException e) {
237         }
238     }
239 
240     @Override
setShouldShowSystemDecors(int displayId, boolean shouldShow)241     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
242         try {
243             WindowManagerGlobal.getWindowManagerService()
244                     .setShouldShowSystemDecors(displayId, shouldShow);
245         } catch (RemoteException e) {
246         }
247     }
248 
249     @Override
shouldShowSystemDecors(int displayId)250     public boolean shouldShowSystemDecors(int displayId) {
251         try {
252             return WindowManagerGlobal.getWindowManagerService().shouldShowSystemDecors(displayId);
253         } catch (RemoteException e) {
254         }
255         return false;
256     }
257 
258     @Override
setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy)259     public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) {
260         try {
261             WindowManagerGlobal.getWindowManagerService().setDisplayImePolicy(displayId, imePolicy);
262         } catch (RemoteException e) {
263         }
264     }
265 
266     @Override
getDisplayImePolicy(int displayId)267     public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
268         try {
269             return WindowManagerGlobal.getWindowManagerService().getDisplayImePolicy(displayId);
270         } catch (RemoteException e) {
271         }
272         return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
273     }
274 
275     @Override
getCurrentWindowMetrics()276     public WindowMetrics getCurrentWindowMetrics() {
277         final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
278         final Rect bounds = getCurrentBounds(context);
279 
280         return new WindowMetrics(bounds, computeWindowInsets(bounds));
281     }
282 
getCurrentBounds(Context context)283     private static Rect getCurrentBounds(Context context) {
284         synchronized (ResourcesManager.getInstance()) {
285             return context.getResources().getConfiguration().windowConfiguration.getBounds();
286         }
287     }
288 
289     @Override
getMaximumWindowMetrics()290     public WindowMetrics getMaximumWindowMetrics() {
291         final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
292         final Rect maxBounds = getMaximumBounds(context);
293 
294         return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
295     }
296 
getMaximumBounds(Context context)297     private static Rect getMaximumBounds(Context context) {
298         synchronized (ResourcesManager.getInstance()) {
299             return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
300         }
301     }
302 
303     // TODO(b/150095967): Set window type to LayoutParams
computeWindowInsets(Rect bounds)304     private WindowInsets computeWindowInsets(Rect bounds) {
305         // Initialize params which used for obtaining all system insets.
306         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
307         params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
308         final Context context = (mParentWindow != null) ? mParentWindow.getContext() : mContext;
309         params.token = Context.getToken(context);
310         params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
311                 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
312         params.setFitInsetsTypes(0);
313         params.setFitInsetsSides(0);
314 
315         return getWindowInsetsFromServer(params, bounds);
316     }
317 
getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds)318     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
319         try {
320             final InsetsState insetsState = new InsetsState();
321             final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
322                     .getWindowInsets(attrs, mContext.getDisplayId(), insetsState);
323             final Configuration config = mContext.getResources().getConfiguration();
324             final boolean isScreenRound = config.isScreenRound();
325             final int windowingMode = config.windowConfiguration.getWindowingMode();
326             return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
327                     isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
328                     SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, null /* typeSideMap */);
329         } catch (RemoteException e) {
330             throw e.rethrowFromSystemServer();
331         }
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
isTaskSnapshotSupported()365     public boolean isTaskSnapshotSupported() {
366         try {
367             return WindowManagerGlobal.getWindowManagerService().isTaskSnapshotSupported();
368         } catch (RemoteException e) {
369         }
370         return false;
371     }
372 }
373