DisplayContent.java revision 3ddc5d60967a5daa2ebf0ede98b71093bb8cc4ae
1/*
2 * Copyright (C) 2012 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.app.ActivityManager.DOCKED_STACK_ID;
20import static android.app.ActivityManager.HOME_STACK_ID;
21
22import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
23import static com.android.server.wm.WindowManagerService.TAG;
24import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
25import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
26
27import android.graphics.Rect;
28import android.graphics.Region;
29import android.util.DisplayMetrics;
30import android.util.Slog;
31import android.view.Display;
32import android.view.DisplayInfo;
33import android.view.Surface;
34
35import java.io.PrintWriter;
36import java.util.ArrayList;
37
38class DisplayContentList extends ArrayList<DisplayContent> {
39}
40
41/**
42 * Utility class for keeping track of the WindowStates and other pertinent contents of a
43 * particular Display.
44 *
45 * IMPORTANT: No method from this class should ever be used without holding
46 * WindowManagerService.mWindowMap.
47 */
48class DisplayContent {
49
50    /** Unique identifier of this stack. */
51    private final int mDisplayId;
52
53    /** Z-ordered (bottom-most first) list of all Window objects. Assigned to an element
54     * from mDisplayWindows; */
55    private final WindowList mWindows = new WindowList();
56
57    // This protects the following display size properties, so that
58    // getDisplaySize() doesn't need to acquire the global lock.  This is
59    // needed because the window manager sometimes needs to use ActivityThread
60    // while it has its global state locked (for example to load animation
61    // resources), but the ActivityThread also needs get the current display
62    // size sometimes when it has its package lock held.
63    //
64    // These will only be modified with both mWindowMap and mDisplaySizeLock
65    // held (in that order) so the window manager doesn't need to acquire this
66    // lock when needing these values in its normal operation.
67    final Object mDisplaySizeLock = new Object();
68    int mInitialDisplayWidth = 0;
69    int mInitialDisplayHeight = 0;
70    int mInitialDisplayDensity = 0;
71    int mBaseDisplayWidth = 0;
72    int mBaseDisplayHeight = 0;
73    int mBaseDisplayDensity = 0;
74    boolean mDisplayScalingDisabled;
75    private final DisplayInfo mDisplayInfo = new DisplayInfo();
76    private final Display mDisplay;
77    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
78
79    Rect mBaseDisplayRect = new Rect();
80    Rect mContentRect = new Rect();
81
82    // Accessed directly by all users.
83    boolean layoutNeeded;
84    int pendingLayoutChanges;
85    final boolean isDefaultDisplay;
86
87    /** Window tokens that are in the process of exiting, but still on screen for animations. */
88    final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
89
90    /** Array containing all TaskStacks on this display.  Array
91     * is stored in display order with the current bottom stack at 0. */
92    private final ArrayList<TaskStack> mStacks = new ArrayList<>();
93
94    /** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
95     * (except a future lockscreen TaskStack) moves to the top. */
96    private TaskStack mHomeStack = null;
97
98    /** Detect user tapping outside of current focused task bounds .*/
99    TaskTapPointerEventListener mTapDetector;
100
101    /** Detect user tapping outside of current focused stack bounds .*/
102    Region mTouchExcludeRegion = new Region();
103
104    /** Save allocating when calculating rects */
105    private Rect mTmpRect = new Rect();
106    private Rect mTmpRect2 = new Rect();
107
108    /** For gathering Task objects in order. */
109    final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
110
111    final WindowManagerService mService;
112
113    /** Remove this display when animation on it has completed. */
114    boolean mDeferredRemoval;
115
116    final DockedStackDividerController mDividerControllerLocked;
117
118    /**
119     * @param display May not be null.
120     * @param service You know.
121     */
122    DisplayContent(Display display, WindowManagerService service) {
123        mDisplay = display;
124        mDisplayId = display.getDisplayId();
125        display.getDisplayInfo(mDisplayInfo);
126        display.getMetrics(mDisplayMetrics);
127        isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
128        mService = service;
129        mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
130    }
131
132    int getDisplayId() {
133        return mDisplayId;
134    }
135
136    WindowList getWindowList() {
137        return mWindows;
138    }
139
140    Display getDisplay() {
141        return mDisplay;
142    }
143
144    DisplayInfo getDisplayInfo() {
145        return mDisplayInfo;
146    }
147
148    DisplayMetrics getDisplayMetrics() {
149        return mDisplayMetrics;
150    }
151
152    /**
153     * Returns true if the specified UID has access to this display.
154     */
155    public boolean hasAccess(int uid) {
156        return mDisplay.hasAccess(uid);
157    }
158
159    public boolean isPrivate() {
160        return (mDisplay.getFlags() & Display.FLAG_PRIVATE) != 0;
161    }
162
163    ArrayList<TaskStack> getStacks() {
164        return mStacks;
165    }
166
167    /**
168     * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
169     * @return All the Tasks, in order, on this display.
170     */
171    ArrayList<Task> getTasks() {
172        mTmpTaskHistory.clear();
173        final int numStacks = mStacks.size();
174        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
175            mTmpTaskHistory.addAll(mStacks.get(stackNdx).getTasks());
176        }
177        return mTmpTaskHistory;
178    }
179
180    TaskStack getHomeStack() {
181        if (mHomeStack == null && mDisplayId == Display.DEFAULT_DISPLAY) {
182            Slog.e(TAG, "getHomeStack: Returning null from this=" + this);
183        }
184        return mHomeStack;
185    }
186
187    void updateDisplayInfo() {
188        mDisplay.getDisplayInfo(mDisplayInfo);
189        mDisplay.getMetrics(mDisplayMetrics);
190        for (int i = mStacks.size() - 1; i >= 0; --i) {
191            mStacks.get(i).updateDisplayInfo(null);
192        }
193    }
194
195    void getLogicalDisplayRect(Rect out) {
196        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
197        final int orientation = mDisplayInfo.rotation;
198        boolean rotated = (orientation == Surface.ROTATION_90
199                || orientation == Surface.ROTATION_270);
200        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
201        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
202        int width = mDisplayInfo.logicalWidth;
203        int left = (physWidth - width) / 2;
204        int height = mDisplayInfo.logicalHeight;
205        int top = (physHeight - height) / 2;
206        out.set(left, top, left + width, top + height);
207    }
208
209    /** Refer to {@link WindowManagerService#attachStack(int, int, boolean)} */
210    void attachStack(TaskStack stack, boolean onTop) {
211        if (stack.mStackId == HOME_STACK_ID) {
212            if (mHomeStack != null) {
213                throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first.");
214            }
215            mHomeStack = stack;
216        }
217        if (onTop) {
218            mStacks.add(stack);
219        } else {
220            mStacks.add(0, stack);
221        }
222        layoutNeeded = true;
223    }
224
225    void moveStack(TaskStack stack, boolean toTop) {
226        if (!mStacks.remove(stack)) {
227            Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable());
228        }
229        mStacks.add(toTop ? mStacks.size() : 0, stack);
230    }
231
232    void detachStack(TaskStack stack) {
233        mStacks.remove(stack);
234    }
235
236    /**
237     * Propagate the new bounds to all child stacks.
238     * @param contentRect The bounds to apply at the top level.
239     */
240    void resize(Rect contentRect) {
241        mContentRect.set(contentRect);
242    }
243
244    int taskIdFromPoint(int x, int y) {
245        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
246            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
247            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
248                final Task task = tasks.get(taskNdx);
249                task.getBounds(mTmpRect);
250                if (task.inFreeformWorkspace() && mTmpRect.contains(x, y)) {
251                    return task.mTaskId;
252                }
253            }
254        }
255        return -1;
256    }
257
258    /**
259     * Find the window whose outside touch area (for resizing) (x, y) falls within.
260     * Returns null if the touch doesn't fall into a resizing area.
261     */
262    WindowState findWindowForControlPoint(int x, int y) {
263        final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
264        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
265            TaskStack stack = mStacks.get(stackNdx);
266            if (!stack.allowTaskResize()) {
267                break;
268            }
269            final ArrayList<Task> tasks = stack.getTasks();
270            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
271                final Task task = tasks.get(taskNdx);
272                if (task.isFullscreen()) {
273                    return null;
274                }
275
276                // We need to use the visible frame on the window for any touch-related
277                // tests. Can't use the task's bounds because the original task bounds
278                // might be adjusted to fit the content frame. (One example is when the
279                // task is put to top-left quadrant, the actual visible frame would not
280                // start at (0,0) after it's adjusted for the status bar.)
281                WindowState win = task.getTopAppMainWindow();
282                if (win != null) {
283                    win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
284                    mTmpRect.inset(-delta, -delta);
285                    if (mTmpRect.contains(x, y)) {
286                        mTmpRect.inset(delta, delta);
287                        if (!mTmpRect.contains(x, y)) {
288                            return win;
289                        }
290                        // User touched inside the task. No need to look further,
291                        // focus transfer will be handled in ACTION_UP.
292                        return null;
293                    }
294                }
295            }
296        }
297        return null;
298    }
299
300    void setTouchExcludeRegion(Task focusedTask) {
301        mTouchExcludeRegion.set(mBaseDisplayRect);
302        WindowList windows = getWindowList();
303        final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
304        for (int i = windows.size() - 1; i >= 0; --i) {
305            final WindowState win = windows.get(i);
306            final Task task = win.getTask();
307            if (win.isVisibleLw() && task != null) {
308                /**
309                 * Exclusion region is the region that TapDetector doesn't care about.
310                 * Here we want to remove all non-focused tasks from the exclusion region.
311                 * We also remove the outside touch area for resizing for all freeform
312                 * tasks (including the focused).
313                 *
314                 * (For freeform focused task, the below logic will first remove the enlarged
315                 * area, then add back the inner area.)
316                 */
317                final boolean isFreeformed = task.inFreeformWorkspace();
318                if (task != focusedTask || isFreeformed) {
319                    mTmpRect.set(win.mVisibleFrame);
320                    mTmpRect.intersect(win.mVisibleInsets);
321                    /**
322                     * If the task is freeformed, enlarge the area to account for outside
323                     * touch area for resize.
324                     */
325                    if (isFreeformed) {
326                        mTmpRect.inset(-delta, -delta);
327                    }
328                    mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
329                }
330                /**
331                 * If we removed the focused task above, add it back and only leave its
332                 * outside touch area in the exclusion. TapDectector is not interested in
333                 * any touch inside the focused task itself.
334                 */
335                if (task == focusedTask && isFreeformed) {
336                    mTmpRect.inset(delta, delta);
337                    mTouchExcludeRegion.op(mTmpRect, Region.Op.UNION);
338                }
339            }
340        }
341        if (mTapDetector != null) {
342            mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
343        }
344    }
345
346    void switchUserStacks() {
347        final WindowList windows = getWindowList();
348        for (int i = 0; i < windows.size(); i++) {
349            final WindowState win = windows.get(i);
350            if (win.isHiddenFromUserLocked()) {
351                if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing, hiding " + win
352                        + ", attrs=" + win.mAttrs.type + ", belonging to " + win.mOwnerUid);
353                win.hideLw(false);
354            }
355        }
356
357        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
358            mStacks.get(stackNdx).switchUser();
359        }
360    }
361
362    void resetAnimationBackgroundAnimator() {
363        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
364            mStacks.get(stackNdx).resetAnimationBackgroundAnimator();
365        }
366    }
367
368    boolean animateDimLayers() {
369        boolean result = false;
370        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
371            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
372            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
373                final Task task = tasks.get(taskNdx);
374                result |= task.animateDimLayers();
375                if (task.isFullscreen()) {
376                    // No point in continuing as this task covers the entire screen.
377                    // Also, fullscreen tasks all share the same dim layer, so we don't want
378                    // processing of fullscreen task below this one affecting the dim layer state.
379                    return result;
380                }
381            }
382        }
383        return result;
384    }
385
386    void resetDimming() {
387        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
388            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
389            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
390                tasks.get(taskNdx).clearContinueDimming();
391            }
392        }
393    }
394
395    boolean isDimming() {
396        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
397            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
398            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
399                if (tasks.get(taskNdx).isDimming()) {
400                    return true;
401                }
402            }
403        }
404        return false;
405    }
406
407    void stopDimmingIfNeeded() {
408        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
409            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
410            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
411                tasks.get(taskNdx).stopDimmingIfNeeded();
412            }
413        }
414    }
415
416    void close() {
417        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
418            mStacks.get(stackNdx).close();
419        }
420    }
421
422    boolean isAnimating() {
423        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
424            final TaskStack stack = mStacks.get(stackNdx);
425            if (stack.isAnimating()) {
426                return true;
427            }
428        }
429        return false;
430    }
431
432    void checkForDeferredActions() {
433        boolean animating = false;
434        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
435            final TaskStack stack = mStacks.get(stackNdx);
436            if (stack.isAnimating()) {
437                animating = true;
438            } else {
439                if (stack.mDeferDetach) {
440                    mService.detachStackLocked(this, stack);
441                }
442                final ArrayList<Task> tasks = stack.getTasks();
443                for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
444                    final Task task = tasks.get(taskNdx);
445                    AppTokenList tokens = task.mAppTokens;
446                    for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
447                        AppWindowToken wtoken = tokens.get(tokenNdx);
448                        if (wtoken.mIsExiting) {
449                            wtoken.removeAppFromTaskLocked();
450                        }
451                    }
452                }
453            }
454        }
455        if (!animating && mDeferredRemoval) {
456            mService.onDisplayRemoved(mDisplayId);
457        }
458    }
459
460    void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
461        final int rotationDelta = DisplayContent.deltaRotation(oldRotation, newRotation);
462        getLogicalDisplayRect(mTmpRect);
463        switch (rotationDelta) {
464            case Surface.ROTATION_0:
465                mTmpRect2.set(bounds);
466                break;
467            case Surface.ROTATION_90:
468                mTmpRect2.top = mTmpRect.bottom - bounds.right;
469                mTmpRect2.left = bounds.top;
470                mTmpRect2.right = mTmpRect2.left + bounds.height();
471                mTmpRect2.bottom = mTmpRect2.top + bounds.width();
472                break;
473            case Surface.ROTATION_180:
474                mTmpRect2.top = mTmpRect.bottom - bounds.bottom;
475                mTmpRect2.left = mTmpRect.right - bounds.right;
476                mTmpRect2.right = mTmpRect2.left + bounds.width();
477                mTmpRect2.bottom = mTmpRect2.top + bounds.height();
478                break;
479            case Surface.ROTATION_270:
480                mTmpRect2.top = bounds.left;
481                mTmpRect2.left = mTmpRect.right - bounds.bottom;
482                mTmpRect2.right = mTmpRect2.left + bounds.height();
483                mTmpRect2.bottom = mTmpRect2.top + bounds.width();
484                break;
485        }
486        bounds.set(mTmpRect2);
487    }
488
489    static int deltaRotation(int oldRotation, int newRotation) {
490        int delta = newRotation - oldRotation;
491        if (delta < 0) delta += 4;
492        return delta;
493    }
494
495    public void dump(String prefix, PrintWriter pw) {
496        pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
497        final String subPrefix = "  " + prefix;
498        pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
499            pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
500            pw.print("dpi");
501            if (mInitialDisplayWidth != mBaseDisplayWidth
502                    || mInitialDisplayHeight != mBaseDisplayHeight
503                    || mInitialDisplayDensity != mBaseDisplayDensity) {
504                pw.print(" base=");
505                pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
506                pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
507            }
508            if (mDisplayScalingDisabled) {
509                pw.println(" noscale");
510            }
511            pw.print(" cur=");
512            pw.print(mDisplayInfo.logicalWidth);
513            pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
514            pw.print(" app=");
515            pw.print(mDisplayInfo.appWidth);
516            pw.print("x"); pw.print(mDisplayInfo.appHeight);
517            pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
518            pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
519            pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
520            pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
521            pw.print(subPrefix); pw.print("deferred="); pw.print(mDeferredRemoval);
522                pw.print(" layoutNeeded="); pw.println(layoutNeeded);
523        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
524            final TaskStack stack = mStacks.get(stackNdx);
525            pw.print(prefix); pw.print("mStacks[" + stackNdx + "]"); pw.println(stack.mStackId);
526            stack.dump(prefix + "  ", pw);
527        }
528        pw.println();
529        pw.println("  Application tokens in top down Z order:");
530        int ndx = 0;
531        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
532            final TaskStack stack = mStacks.get(stackNdx);
533            pw.print("  mStackId="); pw.println(stack.mStackId);
534            ArrayList<Task> tasks = stack.getTasks();
535            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
536                final Task task = tasks.get(taskNdx);
537                pw.print("    mTaskId="); pw.println(task.mTaskId);
538                AppTokenList tokens = task.mAppTokens;
539                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) {
540                    final AppWindowToken wtoken = tokens.get(tokenNdx);
541                    pw.print("    Activity #"); pw.print(tokenNdx);
542                            pw.print(' '); pw.print(wtoken); pw.println(":");
543                    wtoken.dump(pw, "      ");
544                }
545            }
546        }
547        if (ndx == 0) {
548            pw.println("    None");
549        }
550        pw.println();
551        if (!mExitingTokens.isEmpty()) {
552            pw.println();
553            pw.println("  Exiting tokens:");
554            for (int i=mExitingTokens.size()-1; i>=0; i--) {
555                WindowToken token = mExitingTokens.get(i);
556                pw.print("  Exiting #"); pw.print(i);
557                pw.print(' '); pw.print(token);
558                pw.println(':');
559                token.dump(pw, "    ");
560            }
561        }
562        pw.println();
563    }
564
565    @Override
566    public String toString() {
567        return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
568    }
569
570    TaskStack getDockedStackLocked() {
571        for (int i = mStacks.size() - 1; i >= 0; i--) {
572            TaskStack stack = mStacks.get(i);
573            if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) {
574                return stack;
575            }
576        }
577        return null;
578    }
579}
580