blob: cab39b56c3e941cb559bf8d78ae9563ebbc6b576 [file] [log] [blame]
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -08001/*
2 * Copyright (C) 2016 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
17package com.android.server.wm;
18
19import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
20import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
21import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
22import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
23import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
24import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
25import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
26import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
27import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
28import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080030
31import android.content.res.CompatibilityInfo;
32import android.content.res.Configuration;
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080033import android.graphics.Bitmap;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080034import android.os.Binder;
35import android.os.Debug;
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080036import android.os.Handler;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080037import android.os.IBinder;
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080038import android.os.Looper;
39import android.os.Trace;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080040import android.util.Slog;
41import android.view.IApplicationToken;
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080042import android.view.WindowManagerPolicy.StartingSurface;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080043
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080044import com.android.server.AttributeCache;
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080045/**
46 * Controller for the app window token container. This is created by activity manager to link
47 * activity records to the app window token container they use in window manager.
48 *
49 * Test class: {@link AppWindowContainerControllerTests}
50 */
51public class AppWindowContainerController
52 extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
53
54 private final IApplicationToken mToken;
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080055 private final Handler mHandler = new Handler(Looper.getMainLooper());
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -080056
57 private final Runnable mOnWindowsDrawn = () -> {
58 if (mListener == null) {
59 return;
60 }
61 if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
62 + AppWindowContainerController.this.mToken);
63 mListener.onWindowsDrawn();
64 };
65
66 private final Runnable mOnWindowsVisible = () -> {
67 if (mListener == null) {
68 return;
69 }
70 if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
71 + AppWindowContainerController.this.mToken);
72 mListener.onWindowsVisible();
73 };
74
75 private final Runnable mOnWindowsGone = () -> {
76 if (mListener == null) {
77 return;
78 }
79 if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
80 + AppWindowContainerController.this.mToken);
81 mListener.onWindowsGone();
82 };
83
Jorim Jaggiba41f4b2016-12-14 17:43:07 -080084 private final Runnable mAddStartingWindow = () -> {
85 final StartingData startingData;
86 final Configuration mergedOverrideConfiguration;
87
88 synchronized (mWindowMap) {
89 startingData = mContainer.startingData;
90 mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
91 }
92
93 if (startingData == null) {
94 // Animation has been canceled... do nothing.
95 return;
96 }
97
98 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
99 + this + ": pkg=" + mContainer.startingData.pkg);
100
101 StartingSurface contents = null;
102 try {
103 contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
104 startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
105 startingData.labelRes, startingData.icon, startingData.logo,
106 startingData.windowFlags, mergedOverrideConfiguration);
107 } catch (Exception e) {
108 Slog.w(TAG_WM, "Exception when adding starting window", e);
109 }
110 if (contents != null) {
111 boolean abort = false;
112
113 synchronized(mWindowMap) {
114 if (mContainer.removed || mContainer.startingData == null) {
115 // If the window was successfully added, then
116 // we need to remove it.
117 if (mContainer.startingWindow != null) {
118 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
119 "Aborted starting " + mContainer
120 + ": removed=" + mContainer.removed
121 + " startingData=" + mContainer.startingData);
122 mContainer.startingWindow = null;
123 mContainer.startingData = null;
124 abort = true;
125 }
126 } else {
127 mContainer.startingSurface = contents;
128 }
129 if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
130 "Added starting " + mContainer
131 + ": startingWindow="
132 + mContainer.startingWindow + " startingView="
133 + mContainer.startingSurface);
134 }
135
136 if (abort) {
137 try {
138 mService.mPolicy.removeSplashScreen(mContainer.token, contents);
139 } catch (Exception e) {
140 Slog.w(TAG_WM, "Exception when removing starting window", e);
141 }
142 }
143 }
144 };
145
146 private final Runnable mRemoveStartingWindow = () -> {
147 IBinder token = null;
148 StartingSurface contents = null;
149 synchronized (mWindowMap) {
150 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
151 + mContainer + ": startingWindow="
152 + mContainer.startingWindow + " startingView="
153 + mContainer.startingSurface);
154 if (mContainer.startingWindow != null) {
155 contents = mContainer.startingSurface;
156 token = mContainer.token;
157 mContainer.startingData = null;
158 mContainer.startingSurface = null;
159 mContainer.startingWindow = null;
160 mContainer.startingDisplayed = false;
161 }
162 }
163 if (contents != null) {
164 try {
165 mService.mPolicy.removeSplashScreen(token, contents);
166 } catch (Exception e) {
167 Slog.w(TAG_WM, "Exception when removing starting window", e);
168 }
169 }
170 };
171
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800172 public AppWindowContainerController(IApplicationToken token,
173 AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
174 boolean fullscreen, boolean showForAllUsers, int configChanges,
175 boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
176 int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
177 this(token, listener, taskId, index, requestedOrientation, fullscreen, showForAllUsers,
178 configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
179 targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
180 WindowManagerService.getInstance());
181 }
182
183 public AppWindowContainerController(IApplicationToken token,
184 AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
185 boolean fullscreen, boolean showForAllUsers, int configChanges,
186 boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
187 int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
188 WindowManagerService service) {
189 super(listener, service);
190 mToken = token;
191 synchronized(mWindowMap) {
192 AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
193 if (atoken != null) {
194 // TODO: Should this throw an exception instead?
195 Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
196 return;
197 }
198
199 // TODO: Have the controller for the task passed in when task are changed to use
200 // controller.
201 final Task task = mService.mTaskIdToTask.get(taskId);
202 if (task == null) {
203 throw new IllegalArgumentException("addAppToken: invalid taskId=" + taskId);
204 }
205
206 atoken = new AppWindowToken(mService, token, voiceInteraction, task.getDisplayContent(),
207 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
208 requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
209 alwaysFocusable, this);
210 if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
211 + " task=" + taskId + " at " + index);
212 task.addChild(atoken, index);
213 }
214 }
215
216 public void removeContainer(int displayId) {
217 final long origId = Binder.clearCallingIdentity();
218 try {
219 synchronized(mWindowMap) {
220 final DisplayContent dc = mRoot.getDisplayContent(displayId);
221 if (dc == null) {
222 Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
223 + mToken + " from non-existing displayId=" + displayId);
224 return;
225 }
226 dc.removeAppToken(mToken.asBinder());
227 super.removeContainer();
228 }
229 } finally {
230 Binder.restoreCallingIdentity(origId);
231 }
232 }
233
234 // TODO: Move to task window controller when that is created and rename to positionChildAt()
235 public void positionAt(int taskId, int index) {
236 synchronized(mService.mWindowMap) {
237 if (mContainer == null) {
238 Slog.w(TAG_WM,
239 "Attempted to position of non-existing app token: " + mToken);
240 return;
241 }
242
243 // TODO: Should get the window container from this owner when the task owner stuff is
244 // hooked-up.
245 final Task task = mService.mTaskIdToTask.get(taskId);
246 if (task == null) {
247 throw new IllegalArgumentException("positionChildAt: invalid taskId=" + taskId);
248 }
249 task.addChild(mContainer, index);
250 }
251
252 }
253
254 public Configuration setOrientation(int requestedOrientation, int displayId,
255 Configuration displayConfig, boolean freezeScreenIfNeeded) {
256 synchronized(mWindowMap) {
257 if (mContainer == null) {
258 Slog.w(TAG_WM,
259 "Attempted to set orientation of non-existing app token: " + mToken);
260 return null;
261 }
262
263 mContainer.setOrientation(requestedOrientation);
264
265 final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
266 return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
267
268 }
269 }
270
271 public int getOrientation() {
272 synchronized(mWindowMap) {
273 if (mContainer == null) {
274 return SCREEN_ORIENTATION_UNSPECIFIED;
275 }
276
277 return mContainer.getOrientationIgnoreVisibility();
278 }
279 }
280
281 public void setVisibility(boolean visible) {
282 synchronized(mWindowMap) {
283 if (mContainer == null) {
284 Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
285 + mToken);
286 return;
287 }
288
289 final AppWindowToken wtoken = mContainer;
290
291 if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
292 + mToken + ", visible=" + visible + "): " + mService.mAppTransition
293 + " hidden=" + wtoken.hidden + " hiddenRequested="
294 + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
295
296 mService.mOpeningApps.remove(wtoken);
297 mService.mClosingApps.remove(wtoken);
298 wtoken.waitingToShow = false;
299 wtoken.hiddenRequested = !visible;
300
301 if (!visible) {
302 // If the app is dead while it was visible, we kept its dead window on screen.
303 // Now that the app is going invisible, we can remove it. It will be restarted
304 // if made visible again.
305 wtoken.removeDeadWindows();
306 wtoken.setVisibleBeforeClientHidden();
307 } else {
308 if (!mService.mAppTransition.isTransitionSet()
309 && mService.mAppTransition.isReady()) {
310 // Add the app mOpeningApps if transition is unset but ready. This means
311 // we're doing a screen freeze, and the unfreeze will wait for all opening
312 // apps to be ready.
313 mService.mOpeningApps.add(wtoken);
314 }
315 wtoken.startingMoved = false;
316 // If the token is currently hidden (should be the common case), or has been
317 // stopped, then we need to set up to wait for its windows to be ready.
318 if (wtoken.hidden || wtoken.mAppStopped) {
319 wtoken.clearAllDrawn();
320
321 // If the app was already visible, don't reset the waitingToShow state.
322 if (wtoken.hidden) {
323 wtoken.waitingToShow = true;
324 }
325
326 if (wtoken.clientHidden) {
327 // In the case where we are making an app visible
328 // but holding off for a transition, we still need
329 // to tell the client to make its windows visible so
330 // they get drawn. Otherwise, we will wait on
331 // performing the transition until all windows have
332 // been drawn, they never will be, and we are sad.
333 wtoken.clientHidden = false;
334 wtoken.sendAppVisibilityToClients();
335 }
336 }
337 wtoken.requestUpdateWallpaperIfNeeded();
338
339 if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
340 wtoken.mAppStopped = false;
341 }
342
343 // If we are preparing an app transition, then delay changing
344 // the visibility of this token until we execute that transition.
345 if (mService.okToDisplay() && mService.mAppTransition.isTransitionSet()) {
346 // A dummy animation is a placeholder animation which informs others that an
347 // animation is going on (in this case an application transition). If the animation
348 // was transferred from another application/animator, no dummy animator should be
349 // created since an animation is already in progress.
350 if (wtoken.mAppAnimator.usingTransferredAnimation
351 && wtoken.mAppAnimator.animation == null) {
352 Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
353 + ", using null transferred animation!");
354 }
355 if (!wtoken.mAppAnimator.usingTransferredAnimation &&
356 (!wtoken.startingDisplayed || mService.mSkipAppTransitionAnimation)) {
357 if (DEBUG_APP_TRANSITIONS) Slog.v(
358 TAG_WM, "Setting dummy animation on: " + wtoken);
359 wtoken.mAppAnimator.setDummyAnimation();
360 }
361 wtoken.inPendingTransaction = true;
362 if (visible) {
363 mService.mOpeningApps.add(wtoken);
364 wtoken.mEnteringAnimation = true;
365 } else {
366 mService.mClosingApps.add(wtoken);
367 wtoken.mEnteringAnimation = false;
368 }
369 if (mService.mAppTransition.getAppTransition()
370 == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
371 // We're launchingBehind, add the launching activity to mOpeningApps.
372 final WindowState win =
373 mService.getDefaultDisplayContentLocked().findFocusedWindow();
374 if (win != null) {
375 final AppWindowToken focusedToken = win.mAppToken;
376 if (focusedToken != null) {
377 if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
378 + " adding " + focusedToken + " to mOpeningApps");
379 // Force animation to be loaded.
380 focusedToken.hidden = true;
381 mService.mOpeningApps.add(focusedToken);
382 }
383 }
384 }
385 return;
386 }
387
388 wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
389 wtoken.updateReportedVisibilityLocked();
390 }
391 }
392
393 /**
394 * Notifies that we launched an app that might be visible or not visible depending on what kind
395 * of Keyguard flags it's going to set on its windows.
396 */
397 public void notifyUnknownVisibilityLaunched() {
398 synchronized(mWindowMap) {
399 if (mContainer != null) {
400 mService.mUnknownAppVisibilityController.notifyLaunched(mContainer);
401 }
402 }
403 }
404
405 public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
406 CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
407 IBinder transferFrom, boolean createIfNeeded) {
408 synchronized(mWindowMap) {
409 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
410 + " pkg=" + pkg + " transferFrom=" + transferFrom);
411
412 if (mContainer == null) {
413 Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
414 return false;
415 }
416
417 // If the display is frozen, we won't do anything until the actual window is
418 // displayed so there is no reason to put in the starting window.
419 if (!mService.okToDisplay()) {
420 return false;
421 }
422
423 if (mContainer.startingData != null) {
424 return false;
425 }
426
427 // If this is a translucent window, then don't show a starting window -- the current
428 // effect (a full-screen opaque starting window that fades away to the real contents
429 // when it is ready) does not work for this.
430 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
431 + Integer.toHexString(theme));
432 if (theme != 0) {
433 AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
434 com.android.internal.R.styleable.Window, mService.mCurrentUserId);
435 if (ent == null) {
436 // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
437 // see that.
438 return false;
439 }
440 final boolean windowIsTranslucent = ent.array.getBoolean(
441 com.android.internal.R.styleable.Window_windowIsTranslucent, false);
442 final boolean windowIsFloating = ent.array.getBoolean(
443 com.android.internal.R.styleable.Window_windowIsFloating, false);
444 final boolean windowShowWallpaper = ent.array.getBoolean(
445 com.android.internal.R.styleable.Window_windowShowWallpaper, false);
446 final boolean windowDisableStarting = ent.array.getBoolean(
447 com.android.internal.R.styleable.Window_windowDisablePreview, false);
448 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
449 + " Floating=" + windowIsFloating
450 + " ShowWallpaper=" + windowShowWallpaper);
451 if (windowIsTranslucent) {
452 return false;
453 }
454 if (windowIsFloating || windowDisableStarting) {
455 return false;
456 }
457 if (windowShowWallpaper) {
458 if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
459 == null) {
460 // If this theme is requesting a wallpaper, and the wallpaper
461 // is not currently visible, then this effectively serves as
462 // an opaque window and our starting window transition animation
463 // can still work. We just need to make sure the starting window
464 // is also showing the wallpaper.
465 windowFlags |= FLAG_SHOW_WALLPAPER;
466 } else {
467 return false;
468 }
469 }
470 }
471
472 if (mContainer.transferStartingWindow(transferFrom)) {
473 return true;
474 }
475
476 // There is no existing starting window, and the caller doesn't
477 // want us to create one, so that's it!
478 if (!createIfNeeded) {
479 return false;
480 }
481
482 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
483 mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
484 labelRes, icon, logo, windowFlags);
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800485 scheduleAddStartingWindow();
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800486 }
487 return true;
488 }
489
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800490 void scheduleAddStartingWindow() {
491
492 // Note: we really want to do sendMessageAtFrontOfQueue() because we
493 // want to process the message ASAP, before any other queued
494 // messages.
495 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
496 mHandler.postAtFrontOfQueue(mAddStartingWindow);
497 }
498
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800499 public void removeStartingWindow() {
500 synchronized (mWindowMap) {
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800501 if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
502 // Already scheduled.
503 return;
504 }
505
506 if (mContainer.startingWindow == null) {
507 if (mContainer.startingData != null) {
508 // Starting window has not been added yet, but it is scheduled to be added.
509 // Go ahead and cancel the request.
510 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
511 "Clearing startingData for token=" + mContainer);
512 mContainer.startingData = null;
513 }
514 return;
515 }
516
517 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1)
518 + ": Schedule remove starting " + mContainer
519 + " startingWindow=" + mContainer.startingWindow);
520 mHandler.post(mRemoveStartingWindow);
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800521 }
522 }
523
524 public void pauseKeyDispatching() {
525 synchronized (mWindowMap) {
526 if (mContainer != null) {
527 mService.mInputMonitor.pauseDispatchingLw(mContainer);
528 }
529 }
530 }
531
532 public void resumeKeyDispatching() {
533 synchronized (mWindowMap) {
534 if (mContainer != null) {
535 mService.mInputMonitor.resumeDispatchingLw(mContainer);
536 }
537 }
538 }
539
540 public void notifyAppResumed(boolean wasStopped, boolean allowSavedSurface) {
541 synchronized(mWindowMap) {
542 if (mContainer == null) {
543 Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
544 return;
545 }
546 mContainer.notifyAppResumed(wasStopped, allowSavedSurface);
547 }
548 }
549
550 public void notifyAppStopped() {
551 synchronized(mWindowMap) {
552 if (mContainer == null) {
553 Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
554 + mToken);
555 return;
556 }
557 mContainer.notifyAppStopped();
558 }
559 }
560
561 public void startFreezingScreen(int configChanges) {
562 synchronized(mWindowMap) {
563 if (configChanges == 0 && mService.okToDisplay()) {
564 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
565 return;
566 }
567
568 if (mContainer == null) {
569 Slog.w(TAG_WM,
570 "Attempted to freeze screen with non-existing app token: " + mContainer);
571 return;
572 }
573 final long origId = Binder.clearCallingIdentity();
574 mContainer.startFreezingScreen();
575 Binder.restoreCallingIdentity(origId);
576 }
577 }
578
579 public void stopFreezingScreen(boolean force) {
580 synchronized(mWindowMap) {
581 if (mContainer == null) {
582 return;
583 }
584 final long origId = Binder.clearCallingIdentity();
585 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
586 + mContainer.hidden + " freezing=" + mContainer.mAppAnimator.freezingScreen);
587 mContainer.stopFreezingScreen(true, force);
588 Binder.restoreCallingIdentity(origId);
589 }
590 }
591
592 /**
593 * Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
594 * In portrait mode, it grabs the full screenshot.
595 *
596 * @param displayId the Display to take a screenshot of.
597 * @param width the width of the target bitmap
598 * @param height the height of the target bitmap
599 * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
600 */
601 public Bitmap screenshotApplications(int displayId, int width, int height, float frameScale) {
602 try {
603 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
604 final DisplayContent dc;
605 synchronized(mWindowMap) {
606 dc = mRoot.getDisplayContentOrCreate(displayId);
607 if (dc == null) {
608 if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + mToken
609 + ": returning null. No Display for displayId=" + displayId);
610 return null;
611 }
612 }
613 return dc.screenshotApplications(mToken.asBinder(), width, height,
614 false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
615 false /* wallpaperOnly */);
616 } finally {
617 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
618 }
619 }
620
621
622 void reportWindowsDrawn() {
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800623 mHandler.post(mOnWindowsDrawn);
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800624 }
625
626 void reportWindowsVisible() {
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800627 mHandler.post(mOnWindowsVisible);
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800628 }
629
630 void reportWindowsGone() {
Jorim Jaggiba41f4b2016-12-14 17:43:07 -0800631 mHandler.post(mOnWindowsGone);
Wale Ogunwale26c0dfe2016-12-14 14:42:30 -0800632 }
633
634 /** Calls directly into activity manager so window manager lock shouldn't held. */
635 boolean keyDispatchingTimedOut(String reason) {
636 return mListener != null && mListener.keyDispatchingTimedOut(reason);
637 }
638}