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