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