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.ViewRootImpl.NEW_INSETS_MODE_FULL;
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.SOFT_INPUT_ADJUST_NOTHING;
26 
27 import android.annotation.NonNull;
28 import android.app.ResourcesManager;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.content.Context;
31 import android.graphics.Insets;
32 import android.graphics.Point;
33 import android.graphics.Rect;
34 import android.graphics.Region;
35 import android.os.Bundle;
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.os.IResultReceiver;
41 
42 import java.util.List;
43 
44 /**
45  * Provides low-level communication with the system window manager for
46  * operations that are bound to a particular context, display or parent window.
47  * Instances of this object are sensitive to the compatibility info associated
48  * with the running application.
49  *
50  * This object implements the {@link ViewManager} interface,
51  * allowing you to add any View subclass as a top-level window on the screen.
52  * Additional window manager specific layout parameters are defined for
53  * control over how windows are displayed.  It also implements the {@link WindowManager}
54  * interface, allowing you to control the displays attached to the device.
55  *
56  * <p>Applications will not normally use WindowManager directly, instead relying
57  * on the higher-level facilities in {@link android.app.Activity} and
58  * {@link android.app.Dialog}.
59  *
60  * <p>Even for low-level window manager access, it is almost never correct to use
61  * this class.  For example, {@link android.app.Activity#getWindowManager}
62  * provides a window manager for adding windows that are associated with that
63  * activity -- the window manager will not normally allow you to add arbitrary
64  * windows that are not associated with an activity.
65  *
66  * @see WindowManager
67  * @see WindowManagerGlobal
68  * @hide
69  */
70 public final class WindowManagerImpl implements WindowManager {
71     @UnsupportedAppUsage
72     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
73     @VisibleForTesting
74     public final Context mContext;
75     private final Window mParentWindow;
76 
77     private IBinder mDefaultToken;
78 
WindowManagerImpl(Context context)79     public WindowManagerImpl(Context context) {
80         this(context, null);
81     }
82 
WindowManagerImpl(Context context, Window parentWindow)83     private WindowManagerImpl(Context context, Window parentWindow) {
84         mContext = context;
85         mParentWindow = parentWindow;
86     }
87 
createLocalWindowManager(Window parentWindow)88     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
89         return new WindowManagerImpl(mContext, parentWindow);
90     }
91 
createPresentationWindowManager(Context displayContext)92     public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
93         return new WindowManagerImpl(displayContext, mParentWindow);
94     }
95 
96     /**
97      * Sets the window token to assign when none is specified by the client or
98      * available from the parent window.
99      *
100      * @param token The default token to assign.
101      */
setDefaultToken(IBinder token)102     public void setDefaultToken(IBinder token) {
103         mDefaultToken = token;
104     }
105 
106     @Override
addView(@onNull View view, @NonNull ViewGroup.LayoutParams params)107     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
108         applyDefaultToken(params);
109         mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
110                 mContext.getUserId());
111     }
112 
113     @Override
updateViewLayout(@onNull View view, @NonNull ViewGroup.LayoutParams params)114     public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
115         applyDefaultToken(params);
116         mGlobal.updateViewLayout(view, params);
117     }
118 
applyDefaultToken(@onNull ViewGroup.LayoutParams params)119     private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
120         // Only use the default token if we don't have a parent window.
121         if (mDefaultToken != null && mParentWindow == null) {
122             if (!(params instanceof WindowManager.LayoutParams)) {
123                 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
124             }
125 
126             // Only use the default token if we don't already have a token.
127             final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
128             if (wparams.token == null) {
129                 wparams.token = mDefaultToken;
130             }
131         }
132     }
133 
134     @Override
removeView(View view)135     public void removeView(View view) {
136         mGlobal.removeView(view, false);
137     }
138 
139     @Override
removeViewImmediate(View view)140     public void removeViewImmediate(View view) {
141         mGlobal.removeView(view, true);
142     }
143 
144     @Override
requestAppKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId)145     public void requestAppKeyboardShortcuts(
146             final KeyboardShortcutsReceiver receiver, int deviceId) {
147         IResultReceiver resultReceiver = new IResultReceiver.Stub() {
148             @Override
149             public void send(int resultCode, Bundle resultData) throws RemoteException {
150                 List<KeyboardShortcutGroup> result =
151                         resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
152                 receiver.onKeyboardShortcutsReceived(result);
153             }
154         };
155         try {
156             WindowManagerGlobal.getWindowManagerService()
157                 .requestAppKeyboardShortcuts(resultReceiver, deviceId);
158         } catch (RemoteException e) {
159         }
160     }
161 
162     @Override
getDefaultDisplay()163     public Display getDefaultDisplay() {
164         return mContext.getDisplayNoVerify();
165     }
166 
167     @Override
getCurrentImeTouchRegion()168     public Region getCurrentImeTouchRegion() {
169         try {
170             return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
171         } catch (RemoteException e) {
172         }
173         return null;
174     }
175 
176     @Override
setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow)177     public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
178         try {
179             WindowManagerGlobal.getWindowManagerService()
180                     .setShouldShowWithInsecureKeyguard(displayId, shouldShow);
181         } catch (RemoteException e) {
182         }
183     }
184 
185     @Override
setShouldShowSystemDecors(int displayId, boolean shouldShow)186     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
187         try {
188             WindowManagerGlobal.getWindowManagerService()
189                     .setShouldShowSystemDecors(displayId, shouldShow);
190         } catch (RemoteException e) {
191         }
192     }
193 
194     @Override
shouldShowSystemDecors(int displayId)195     public boolean shouldShowSystemDecors(int displayId) {
196         try {
197             return WindowManagerGlobal.getWindowManagerService().shouldShowSystemDecors(displayId);
198         } catch (RemoteException e) {
199         }
200         return false;
201     }
202 
203     @Override
setShouldShowIme(int displayId, boolean shouldShow)204     public void setShouldShowIme(int displayId, boolean shouldShow) {
205         try {
206             WindowManagerGlobal.getWindowManagerService().setShouldShowIme(displayId, shouldShow);
207         } catch (RemoteException e) {
208         }
209     }
210 
211     @Override
shouldShowIme(int displayId)212     public boolean shouldShowIme(int displayId) {
213         try {
214             return WindowManagerGlobal.getWindowManagerService().shouldShowIme(displayId);
215         } catch (RemoteException e) {
216         }
217         return false;
218     }
219 
220     @Override
getCurrentWindowMetrics()221     public WindowMetrics getCurrentWindowMetrics() {
222         final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
223         final Rect bounds = getCurrentBounds(context);
224 
225         return new WindowMetrics(bounds, computeWindowInsets(bounds));
226     }
227 
getCurrentBounds(Context context)228     private static Rect getCurrentBounds(Context context) {
229         synchronized (ResourcesManager.getInstance()) {
230             return context.getResources().getConfiguration().windowConfiguration.getBounds();
231         }
232     }
233 
234     @Override
getMaximumWindowMetrics()235     public WindowMetrics getMaximumWindowMetrics() {
236         final Rect maxBounds = getMaximumBounds();
237         return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
238     }
239 
getMaximumBounds()240     private Rect getMaximumBounds() {
241         // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea
242         //  bound after displayArea feature is finished.
243         final Display display = mContext.getDisplayNoVerify();
244         final Point displaySize = new Point();
245         display.getRealSize(displaySize);
246         return new Rect(0, 0, displaySize.x, displaySize.y);
247     }
248 
249     // TODO(b/150095967): Set window type to LayoutParams
computeWindowInsets(Rect bounds)250     private WindowInsets computeWindowInsets(Rect bounds) {
251         // Initialize params which used for obtaining all system insets.
252         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
253         params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
254         params.token = (mParentWindow != null) ? mParentWindow.getContext().getActivityToken()
255                 : mContext.getActivityToken();
256         params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
257                 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
258         params.setFitInsetsTypes(0);
259         params.setFitInsetsSides(0);
260 
261         return getWindowInsetsFromServer(params, bounds);
262     }
263 
getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds)264     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
265         try {
266             final Rect systemWindowInsets = new Rect();
267             final Rect stableInsets = new Rect();
268             final DisplayCutout.ParcelableWrapper displayCutout =
269                     new DisplayCutout.ParcelableWrapper();
270             final InsetsState insetsState = new InsetsState();
271             final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
272                     .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets,
273                     stableInsets, displayCutout, insetsState);
274             final boolean isScreenRound =
275                     mContext.getResources().getConfiguration().isScreenRound();
276             if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
277                 return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
278                         isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
279                         SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
280                         SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
281             } else {
282                 return new WindowInsets.Builder()
283                         .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars)
284                         .setRound(isScreenRound)
285                         .setSystemWindowInsets(Insets.of(systemWindowInsets))
286                         .setStableInsets(Insets.of(stableInsets))
287                         .setDisplayCutout(displayCutout.get()).build();
288             }
289         } catch (RemoteException e) {
290             throw e.rethrowFromSystemServer();
291         }
292     }
293 }
294