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