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 com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
20import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
21import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
22import static com.android.server.wm.WindowManagerService.TAG;
23
24import android.app.ActivityManager.StackBoxInfo;
25import android.graphics.Rect;
26import android.graphics.Region;
27import android.os.Debug;
28import android.util.EventLog;
29import android.util.Slog;
30import android.view.Display;
31import android.view.DisplayInfo;
32import com.android.server.EventLogTags;
33
34import java.io.PrintWriter;
35import java.util.ArrayList;
36
37class DisplayContentList extends ArrayList<DisplayContent> {
38}
39
40/**
41 * Utility class for keeping track of the WindowStates and other pertinent contents of a
42 * particular Display.
43 *
44 * IMPORTANT: No method from this class should ever be used without holding
45 * WindowManagerService.mWindowMap.
46 */
47class DisplayContent {
48
49    /** Unique identifier of this stack. */
50    private final int mDisplayId;
51
52    /** Z-ordered (bottom-most first) list of all Window objects. Assigned to an element
53     * from mDisplayWindows; */
54    private WindowList mWindows = new WindowList();
55
56    // This protects the following display size properties, so that
57    // getDisplaySize() doesn't need to acquire the global lock.  This is
58    // needed because the window manager sometimes needs to use ActivityThread
59    // while it has its global state locked (for example to load animation
60    // resources), but the ActivityThread also needs get the current display
61    // size sometimes when it has its package lock held.
62    //
63    // These will only be modified with both mWindowMap and mDisplaySizeLock
64    // held (in that order) so the window manager doesn't need to acquire this
65    // lock when needing these values in its normal operation.
66    final Object mDisplaySizeLock = new Object();
67    int mInitialDisplayWidth = 0;
68    int mInitialDisplayHeight = 0;
69    int mInitialDisplayDensity = 0;
70    int mBaseDisplayWidth = 0;
71    int mBaseDisplayHeight = 0;
72    int mBaseDisplayDensity = 0;
73    private final DisplayInfo mDisplayInfo = new DisplayInfo();
74    private final Display mDisplay;
75
76    Rect mBaseDisplayRect = new Rect();
77
78    // Accessed directly by all users.
79    boolean layoutNeeded;
80    int pendingLayoutChanges;
81    final boolean isDefaultDisplay;
82
83    /**
84     * Window tokens that are in the process of exiting, but still
85     * on screen for animations.
86     */
87    final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
88
89    /**
90     * Application tokens that are in the process of exiting, but still
91     * on screen for animations.
92     */
93    final AppTokenList mExitingAppTokens = new AppTokenList();
94
95    /** Array containing the home StackBox and possibly one more which would contain apps. Array
96     * is stored in display order with the current bottom stack at 0. */
97    private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
98
99    /** True when the home StackBox is at the top of mStackBoxes, false otherwise. */
100    private TaskStack mHomeStack = null;
101
102    /** Detect user tapping outside of current focused stack bounds .*/
103    StackTapPointerEventListener mTapDetector;
104
105    /** Detect user tapping outside of current focused stack bounds .*/
106    Region mTouchExcludeRegion = new Region();
107
108    /** Save allocating when retrieving tasks */
109    private ArrayList<Task> mTaskHistory = new ArrayList<Task>();
110
111    /** Save allocating when calculating rects */
112    Rect mTmpRect = new Rect();
113
114    final WindowManagerService mService;
115
116    /**
117     * @param display May not be null.
118     * @param service TODO(cmautner):
119     */
120    DisplayContent(Display display, WindowManagerService service) {
121        mDisplay = display;
122        mDisplayId = display.getDisplayId();
123        display.getDisplayInfo(mDisplayInfo);
124        isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
125        mService = service;
126
127        StackBox newBox = new StackBox(service, this, null);
128        mStackBoxes.add(newBox);
129        TaskStack newStack = new TaskStack(service, HOME_STACK_ID, this);
130        newStack.mStackBox = newBox;
131        newBox.mStack = newStack;
132        mHomeStack = newStack;
133    }
134
135    int getDisplayId() {
136        return mDisplayId;
137    }
138
139    WindowList getWindowList() {
140        return mWindows;
141    }
142
143    Display getDisplay() {
144        return mDisplay;
145    }
146
147    DisplayInfo getDisplayInfo() {
148        return mDisplayInfo;
149    }
150
151    /**
152     * Returns true if the specified UID has access to this display.
153     */
154    public boolean hasAccess(int uid) {
155        return mDisplay.hasAccess(uid);
156    }
157
158    boolean homeOnTop() {
159        return mStackBoxes.get(0).mStack != mHomeStack;
160    }
161
162    public boolean isPrivate() {
163        return (mDisplay.getFlags() & Display.FLAG_PRIVATE) != 0;
164    }
165
166    /**
167     * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
168     * @return All the Tasks, in order, on this display.
169     */
170    ArrayList<Task> getTasks() {
171        return mTaskHistory;
172    }
173
174    void addTask(Task task, boolean toTop) {
175        mTaskHistory.remove(task);
176
177        final int userId = task.mUserId;
178        int taskNdx;
179        final int numTasks = mTaskHistory.size();
180        if (toTop) {
181            for (taskNdx = numTasks - 1; taskNdx >= 0; --taskNdx) {
182                if (mTaskHistory.get(taskNdx).mUserId == userId) {
183                    break;
184                }
185            }
186            ++taskNdx;
187        } else {
188            for (taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
189                if (mTaskHistory.get(taskNdx).mUserId == userId) {
190                    break;
191                }
192            }
193        }
194
195        mTaskHistory.add(taskNdx, task);
196        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, taskNdx);
197    }
198
199    void removeTask(Task task) {
200        mTaskHistory.remove(task);
201    }
202
203    TaskStack getHomeStack() {
204        return mHomeStack;
205    }
206
207    void updateDisplayInfo() {
208        mDisplay.getDisplayInfo(mDisplayInfo);
209    }
210
211    void getLogicalDisplayRect(Rect out) {
212        updateDisplayInfo();
213        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
214        int width = mDisplayInfo.logicalWidth;
215        int left = (mBaseDisplayWidth - width) / 2;
216        int height = mDisplayInfo.logicalHeight;
217        int top = (mBaseDisplayHeight - height) / 2;
218        out.set(left, top, left + width, top + height);
219    }
220
221    /** @return The number of tokens in all of the Tasks on this display. */
222    int numTokens() {
223        int count = 0;
224        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
225            count += mTaskHistory.get(taskNdx).mAppTokens.size();
226        }
227        return count;
228    }
229
230    /** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */
231    TaskStack createStack(int stackId, int relativeStackBoxId, int position, float weight) {
232        TaskStack newStack = null;
233        if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackBoxId="
234                + relativeStackBoxId + " position=" + position + " weight=" + weight);
235        if (stackId == HOME_STACK_ID) {
236            if (mStackBoxes.size() != 1) {
237                throw new IllegalArgumentException("createStack: HOME_STACK_ID (0) not first.");
238            }
239            newStack = mHomeStack;
240        } else {
241            int stackBoxNdx;
242            for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
243                final StackBox box = mStackBoxes.get(stackBoxNdx);
244                if (position == StackBox.TASK_STACK_GOES_OVER
245                        || position == StackBox.TASK_STACK_GOES_UNDER) {
246                    // Position indicates a new box is added at top level only.
247                    if (box.contains(relativeStackBoxId)) {
248                        StackBox newBox = new StackBox(mService, this, null);
249                        newStack = new TaskStack(mService, stackId, this);
250                        newStack.mStackBox = newBox;
251                        newBox.mStack = newStack;
252                        final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
253                        if (DEBUG_STACK) Slog.d(TAG, "createStack: inserting stack at " +
254                                (stackBoxNdx + offset));
255                        mStackBoxes.add(stackBoxNdx + offset, newBox);
256                        break;
257                    }
258                } else {
259                    // Remaining position values indicate a box must be split.
260                    newStack = box.split(stackId, relativeStackBoxId, position, weight);
261                    if (newStack != null) {
262                        break;
263                    }
264                }
265            }
266            if (stackBoxNdx < 0) {
267                throw new IllegalArgumentException("createStack: stackBoxId " + relativeStackBoxId
268                        + " not found.");
269            }
270        }
271        if (newStack != null) {
272            layoutNeeded = true;
273        }
274        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, relativeStackBoxId, position,
275                (int)(weight * 100 + 0.5));
276        return newStack;
277    }
278
279    /** Refer to {@link WindowManagerService#resizeStackBox(int, float)} */
280    boolean resizeStack(int stackBoxId, float weight) {
281        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
282            final StackBox box = mStackBoxes.get(stackBoxNdx);
283            if (box.resize(stackBoxId, weight)) {
284                layoutNeeded = true;
285                return true;
286            }
287        }
288        return false;
289    }
290
291    void addStackBox(StackBox box, boolean toTop) {
292        if (mStackBoxes.size() >= 2) {
293            throw new RuntimeException("addStackBox: Too many toplevel StackBoxes!");
294        }
295        mStackBoxes.add(toTop ? mStackBoxes.size() : 0, box);
296    }
297
298    void removeStackBox(StackBox box) {
299        if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: box=" + box);
300        final TaskStack stack = box.mStack;
301        if (stack != null && stack.mStackId == HOME_STACK_ID) {
302            // Never delete the home stack, even if it is empty.
303            if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: Not deleting home stack.");
304            return;
305        }
306        mStackBoxes.remove(box);
307    }
308
309    StackBoxInfo getStackBoxInfo(StackBox box) {
310        StackBoxInfo info = new StackBoxInfo();
311        info.stackBoxId = box.mStackBoxId;
312        info.weight = box.mWeight;
313        info.vertical = box.mVertical;
314        info.bounds = new Rect(box.mBounds);
315        if (box.mStack != null) {
316            info.stackId = box.mStack.mStackId;
317            // ActivityManagerService will fill in the StackInfo.
318        } else {
319            info.stackId = -1;
320            info.children = new StackBoxInfo[2];
321            info.children[0] = getStackBoxInfo(box.mFirst);
322            info.children[1] = getStackBoxInfo(box.mSecond);
323        }
324        return info;
325    }
326
327    ArrayList<StackBoxInfo> getStackBoxInfos() {
328        ArrayList<StackBoxInfo> list = new ArrayList<StackBoxInfo>();
329        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
330            list.add(getStackBoxInfo(mStackBoxes.get(stackBoxNdx)));
331        }
332        return list;
333    }
334
335    /**
336     * Move the home StackBox to the top or bottom of mStackBoxes. That is the only place
337     * it is allowed to be. This is a nop if the home StackBox is already in the correct position.
338     * @param toTop Move home to the top of mStackBoxes if true, to the bottom if false.
339     * @return true if a change was made, false otherwise.
340     */
341    boolean moveHomeStackBox(boolean toTop) {
342        if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop + " Callers=" +
343                Debug.getCallers(4));
344        EventLog.writeEvent(EventLogTags.WM_HOME_STACK_MOVED, toTop ? 1 : 0);
345        switch (mStackBoxes.size()) {
346            case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!");
347            case 1: return false; // Only the home StackBox exists.
348            case 2:
349                if (homeOnTop() ^ toTop) {
350                    mStackBoxes.add(mStackBoxes.remove(0));
351                    return true;
352                }
353                return false;
354            default: throw new RuntimeException("moveHomeStackBox: Too many toplevel StackBoxes!");
355        }
356    }
357
358    /**
359     * Propagate the new bounds to all child stack boxes, applying weights as we move down.
360     * @param contentRect The bounds to apply at the top level.
361     */
362    boolean setStackBoxSize(Rect contentRect) {
363        boolean change = false;
364        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
365            change |= mStackBoxes.get(stackBoxNdx).setStackBoxSizes(contentRect, true);
366        }
367        return change;
368    }
369
370    Rect getStackBounds(int stackId) {
371        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
372            Rect bounds = mStackBoxes.get(stackBoxNdx).getStackBounds(stackId);
373            if (bounds != null) {
374                return bounds;
375            }
376        }
377        return null;
378    }
379
380    int stackIdFromPoint(int x, int y) {
381        StackBox topBox = mStackBoxes.get(mStackBoxes.size() - 1);
382        return topBox.stackIdFromPoint(x, y);
383    }
384
385    void setTouchExcludeRegion(TaskStack focusedStack) {
386        mTouchExcludeRegion.set(mBaseDisplayRect);
387        WindowList windows = getWindowList();
388        for (int i = windows.size() - 1; i >= 0; --i) {
389            final WindowState win = windows.get(i);
390            final TaskStack stack = win.getStack();
391            if (win.isVisibleLw() && stack != null && stack != focusedStack) {
392                mTmpRect.set(win.mVisibleFrame);
393                mTmpRect.intersect(win.mVisibleInsets);
394                mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
395            }
396        }
397    }
398
399    void switchUserStacks(int oldUserId, int newUserId) {
400        final WindowList windows = getWindowList();
401        for (int i = 0; i < windows.size(); i++) {
402            final WindowState win = windows.get(i);
403            if (win.isHiddenFromUserLocked()) {
404                if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing " + newUserId + " hiding "
405                        + win + ", attrs=" + win.mAttrs.type + ", belonging to "
406                        + win.mOwnerUid);
407                win.hideLw(false);
408            }
409        }
410
411        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
412            mStackBoxes.get(stackBoxNdx).switchUserStacks(newUserId);
413        }
414    }
415
416    void resetAnimationBackgroundAnimator() {
417        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
418            mStackBoxes.get(stackBoxNdx).resetAnimationBackgroundAnimator();
419        }
420    }
421
422    boolean animateDimLayers() {
423        boolean result = false;
424        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
425            result |= mStackBoxes.get(stackBoxNdx).animateDimLayers();
426        }
427        return result;
428    }
429
430    void resetDimming() {
431        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
432            mStackBoxes.get(stackBoxNdx).resetDimming();
433        }
434    }
435
436    boolean isDimming() {
437        boolean result = false;
438        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
439            result |= mStackBoxes.get(stackBoxNdx).isDimming();
440        }
441        return result;
442    }
443
444    void stopDimmingIfNeeded() {
445        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
446            mStackBoxes.get(stackBoxNdx).stopDimmingIfNeeded();
447        }
448    }
449
450    void close() {
451        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
452            mStackBoxes.get(stackBoxNdx).close();
453        }
454    }
455
456    public void dump(String prefix, PrintWriter pw) {
457        pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
458        final String subPrefix = "  " + prefix;
459        pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
460            pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
461            pw.print("dpi");
462            if (mInitialDisplayWidth != mBaseDisplayWidth
463                    || mInitialDisplayHeight != mBaseDisplayHeight
464                    || mInitialDisplayDensity != mBaseDisplayDensity) {
465                pw.print(" base=");
466                pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
467                pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
468            }
469            pw.print(" cur=");
470            pw.print(mDisplayInfo.logicalWidth);
471            pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
472            pw.print(" app=");
473            pw.print(mDisplayInfo.appWidth);
474            pw.print("x"); pw.print(mDisplayInfo.appHeight);
475            pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
476            pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
477            pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
478            pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
479            pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded);
480        for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) {
481            pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx);
482            mStackBoxes.get(boxNdx).dump(prefix + "  ", pw);
483        }
484        int ndx = numTokens();
485        if (ndx > 0) {
486            pw.println();
487            pw.println("  Application tokens in Z order:");
488            getTasks();
489            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
490                AppTokenList tokens = mTaskHistory.get(taskNdx).mAppTokens;
491                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
492                    final AppWindowToken wtoken = tokens.get(tokenNdx);
493                    pw.print("  App #"); pw.print(ndx--);
494                            pw.print(' '); pw.print(wtoken); pw.println(":");
495                    wtoken.dump(pw, "    ");
496                }
497            }
498        }
499        if (mExitingTokens.size() > 0) {
500            pw.println();
501            pw.println("  Exiting tokens:");
502            for (int i=mExitingTokens.size()-1; i>=0; i--) {
503                WindowToken token = mExitingTokens.get(i);
504                pw.print("  Exiting #"); pw.print(i);
505                pw.print(' '); pw.print(token);
506                pw.println(':');
507                token.dump(pw, "    ");
508            }
509        }
510        if (mExitingAppTokens.size() > 0) {
511            pw.println();
512            pw.println("  Exiting application tokens:");
513            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
514                WindowToken token = mExitingAppTokens.get(i);
515                pw.print("  Exiting App #"); pw.print(i);
516                  pw.print(' '); pw.print(token);
517                  pw.println(':');
518                  token.dump(pw, "    ");
519            }
520        }
521        pw.println();
522    }
523}
524