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