Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.server.am;
     18 
     19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
     20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
     21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
     22 import static android.server.am.ProtoExtractors.extract;
     23 import static android.server.am.StateLogger.log;
     24 import static android.server.am.StateLogger.logE;
     25 import static android.view.Display.DEFAULT_DISPLAY;
     26 
     27 import static org.junit.Assert.fail;
     28 
     29 import android.content.res.Configuration;
     30 import android.graphics.Rect;
     31 import android.os.ParcelFileDescriptor;
     32 import android.os.SystemClock;
     33 import android.support.test.InstrumentationRegistry;
     34 import android.view.nano.DisplayInfoProto;
     35 
     36 import com.android.server.wm.nano.AppTransitionProto;
     37 import com.android.server.wm.nano.AppWindowTokenProto;
     38 import com.android.server.wm.nano.ConfigurationContainerProto;
     39 import com.android.server.wm.nano.DisplayFramesProto;
     40 import com.android.server.wm.nano.DisplayProto;
     41 import com.android.server.wm.nano.IdentifierProto;
     42 import com.android.server.wm.nano.PinnedStackControllerProto;
     43 import com.android.server.wm.nano.StackProto;
     44 import com.android.server.wm.nano.TaskProto;
     45 import com.android.server.wm.nano.WindowContainerProto;
     46 import com.android.server.wm.nano.WindowManagerServiceDumpProto;
     47 import com.android.server.wm.nano.WindowStateAnimatorProto;
     48 import com.android.server.wm.nano.WindowStateProto;
     49 import com.android.server.wm.nano.WindowSurfaceControllerProto;
     50 import com.android.server.wm.nano.WindowTokenProto;
     51 
     52 import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
     53 
     54 import java.io.ByteArrayOutputStream;
     55 import java.io.FileInputStream;
     56 import java.io.IOException;
     57 import java.nio.charset.StandardCharsets;
     58 import java.util.ArrayList;
     59 import java.util.HashMap;
     60 import java.util.LinkedList;
     61 import java.util.List;
     62 import java.util.Map;
     63 
     64 public class WindowManagerState {
     65     private static final String DUMPSYS_WINDOW = "dumpsys window -a --proto";
     66 
     67     private static final String STARTING_WINDOW_PREFIX = "Starting ";
     68     private static final String DEBUGGER_WINDOW_PREFIX = "Waiting For Debugger: ";
     69 
     70     // Windows in z-order with the top most at the front of the list.
     71     private List<WindowState> mWindowStates = new ArrayList();
     72     // Stacks in z-order with the top most at the front of the list, starting with primary display.
     73     private final List<WindowStack> mStacks = new ArrayList();
     74     // Stacks on all attached displays, in z-order with the top most at the front of the list.
     75     private final Map<Integer, List<WindowStack>> mDisplayStacks
     76             = new HashMap<>();
     77     private List<Display> mDisplays = new ArrayList();
     78     private String mFocusedWindow = null;
     79     private String mFocusedApp = null;
     80     private Rect mDefaultPinnedStackBounds = new Rect();
     81     private Rect mPinnedStackMovementBounds = new Rect();
     82     private final LinkedList<String> mSysDump = new LinkedList();
     83     private int mRotation;
     84     private boolean mIsDockedStackMinimized;
     85 
     86     public void computeState() {
     87         // It is possible the system is in the middle of transition to the right state when we get
     88         // the dump. We try a few times to get the information we need before giving up.
     89         int retriesLeft = 3;
     90         boolean retry = false;
     91         byte[] dump = null;
     92 
     93         log("==============================");
     94         log("      WindowManagerState      ");
     95         log("==============================");
     96         do {
     97             if (retry) {
     98                 log("***Incomplete WM state. Retrying...");
     99                 // Wait half a second between retries for window manager to finish transitioning...
    100                 SystemClock.sleep(500);
    101             }
    102 
    103             dump = executeShellCommand(DUMPSYS_WINDOW);
    104             try {
    105                 parseSysDumpProto(dump);
    106             } catch (InvalidProtocolBufferNanoException ex) {
    107                 throw new RuntimeException("Failed to parse dumpsys:\n"
    108                         + new String(dump, StandardCharsets.UTF_8), ex);
    109             }
    110 
    111             retry = mWindowStates.isEmpty() || mFocusedApp == null;
    112         } while (retry && retriesLeft-- > 0);
    113 
    114         if (mWindowStates.isEmpty()) {
    115             logE("No Windows found...");
    116         }
    117         if (mFocusedWindow == null) {
    118             logE("No Focused Window...");
    119         }
    120         if (mFocusedApp == null) {
    121             logE("No Focused App...");
    122         }
    123     }
    124 
    125     private byte[] executeShellCommand(String cmd) {
    126         try {
    127             ParcelFileDescriptor pfd =
    128                     InstrumentationRegistry.getInstrumentation().getUiAutomation()
    129                             .executeShellCommand(cmd);
    130             byte[] buf = new byte[512];
    131             int bytesRead;
    132             FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
    133             ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    134             while ((bytesRead = fis.read(buf)) != -1) {
    135                 stdout.write(buf, 0, bytesRead);
    136             }
    137             fis.close();
    138             return stdout.toByteArray();
    139         } catch (IOException e) {
    140             throw new RuntimeException(e);
    141         }
    142     }
    143 
    144 
    145     private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
    146         reset();
    147         WindowManagerServiceDumpProto state = WindowManagerServiceDumpProto.parseFrom(sysDump);
    148         List<WindowState> allWindows = new ArrayList<>();
    149         Map<String, WindowState> windowMap = new HashMap<>();
    150         if (state.focusedWindow != null) {
    151             mFocusedWindow = state.focusedWindow.title;
    152         }
    153         mFocusedApp = state.focusedApp;
    154         for (int i = 0; i < state.rootWindowContainer.displays.length; i++) {
    155             DisplayProto displayProto = state.rootWindowContainer.displays[i];
    156             final Display display = new Display(displayProto);
    157             mDisplays.add(display);
    158             allWindows.addAll(display.getWindows());
    159             List<WindowStack> stacks = new ArrayList<>();
    160             for (int j = 0; j < displayProto.stacks.length; j++) {
    161                 StackProto stackProto = displayProto.stacks[j];
    162                 final WindowStack stack = new WindowStack(stackProto);
    163                 mStacks.add(stack);
    164                 stacks.add(stack);
    165                 allWindows.addAll(stack.getWindows());
    166             }
    167             mDisplayStacks.put(display.mDisplayId, stacks);
    168 
    169             // use properties from the default display only
    170             if (display.getDisplayId() == DEFAULT_DISPLAY) {
    171                 if (displayProto.dockedStackDividerController != null) {
    172                     mIsDockedStackMinimized =
    173                             displayProto.dockedStackDividerController.minimizedDock;
    174                 }
    175                 PinnedStackControllerProto pinnedStackProto = displayProto.pinnedStackController;
    176                 if (pinnedStackProto != null) {
    177                     mDefaultPinnedStackBounds = extract(pinnedStackProto.defaultBounds);
    178                     mPinnedStackMovementBounds = extract(pinnedStackProto.movementBounds);
    179                 }
    180             }
    181         }
    182         for (WindowState w : allWindows) {
    183             windowMap.put(w.getToken(), w);
    184         }
    185         for (int i = 0; i < state.rootWindowContainer.windows.length; i++) {
    186             IdentifierProto identifierProto = state.rootWindowContainer.windows[i];
    187             String hash_code = Integer.toHexString(identifierProto.hashCode);
    188             mWindowStates.add(windowMap.get(hash_code));
    189         }
    190         mRotation = state.rotation;
    191         AppTransitionProto appTransitionProto = state.appTransition;
    192     }
    193 
    194     public void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
    195         windowList.clear();
    196         for (WindowState ws : mWindowStates) {
    197             if (ws.isShown() && windowName.equals(ws.getName())) {
    198                 windowList.add(ws);
    199             }
    200         }
    201     }
    202 
    203     public WindowState getWindowByPackageName(String packageName, int windowType) {
    204         for (WindowState ws : mWindowStates) {
    205             final String name = ws.getName();
    206             if (name == null || !name.contains(packageName)) {
    207                 continue;
    208             }
    209             if (windowType != ws.getType()) {
    210                 continue;
    211             }
    212             return ws;
    213         }
    214 
    215         return null;
    216     }
    217 
    218     public void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
    219             List<WindowState> outWindowList) {
    220         outWindowList.clear();
    221         for (WindowState ws : mWindowStates) {
    222             final String name = ws.getName();
    223             if (name == null || !name.contains(packageName)) {
    224                 continue;
    225             }
    226             if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
    227                 continue;
    228             }
    229             outWindowList.add(ws);
    230         }
    231     }
    232 
    233     Display getDisplay(int displayId) {
    234         for (Display display : mDisplays) {
    235             if (displayId == display.getDisplayId()) {
    236                 return display;
    237             }
    238         }
    239         return null;
    240     }
    241 
    242     List<Display> getDisplays() {
    243         return mDisplays;
    244     }
    245 
    246     String getFrontWindow() {
    247         if (mWindowStates == null || mWindowStates.isEmpty()) {
    248             return null;
    249         }
    250         return mWindowStates.get(0).getName();
    251     }
    252 
    253     public String getFocusedWindow() {
    254         return mFocusedWindow;
    255     }
    256 
    257     public String getFocusedApp() {
    258         return mFocusedApp;
    259     }
    260 
    261     public int getRotation() {
    262         return mRotation;
    263     }
    264 
    265     /**
    266      * Check if at least one window which matches provided window name is visible.
    267      */
    268     boolean isWindowVisible(String windowName) {
    269         for (WindowState window : mWindowStates) {
    270             if (window.getName().equals(windowName)) {
    271                 if (window.isShown()) {
    272                     return true;
    273                 }
    274             }
    275         }
    276         return false;
    277     }
    278 
    279     WindowStack getStack(int stackId) {
    280         for (WindowStack stack : mStacks) {
    281             if (stackId == stack.mStackId) {
    282                 return stack;
    283             }
    284         }
    285         return null;
    286     }
    287 
    288     public boolean isDockedStackMinimized() {
    289         return mIsDockedStackMinimized;
    290     }
    291 
    292     public int getZOrder(WindowState w) {
    293         return mWindowStates.size() - mWindowStates.indexOf(w);
    294     }
    295 
    296     private void reset() {
    297         mSysDump.clear();
    298         mStacks.clear();
    299         mDisplays.clear();
    300         mWindowStates.clear();
    301         mDisplayStacks.clear();
    302         mFocusedWindow = null;
    303         mFocusedApp = null;
    304         mIsDockedStackMinimized = false;
    305         mDefaultPinnedStackBounds.setEmpty();
    306         mPinnedStackMovementBounds.setEmpty();
    307         mRotation = 0;
    308     }
    309 
    310     static class WindowStack extends WindowContainer {
    311 
    312         int mStackId;
    313         ArrayList<WindowTask> mTasks = new ArrayList<>();
    314         boolean mWindowAnimationBackgroundSurfaceShowing;
    315 
    316         WindowStack(StackProto proto) {
    317             super(proto.windowContainer);
    318             mStackId = proto.id;
    319             mFullscreen = proto.fillsParent;
    320             mBounds = extract(proto.bounds);
    321             for (int i = 0; i < proto.tasks.length; i++) {
    322                 TaskProto taskProto = proto.tasks[i];
    323                 WindowTask task = new WindowTask(taskProto);
    324                 mTasks.add(task);
    325                 mSubWindows.addAll(task.getWindows());
    326             }
    327             mWindowAnimationBackgroundSurfaceShowing = proto.animationBackgroundSurfaceIsDimming;
    328         }
    329 
    330         WindowTask getTask(int taskId) {
    331             for (WindowTask task : mTasks) {
    332                 if (taskId == task.mTaskId) {
    333                     return task;
    334                 }
    335             }
    336             return null;
    337         }
    338     }
    339 
    340     static class WindowTask extends WindowContainer {
    341 
    342         int mTaskId;
    343         Rect mTempInsetBounds;
    344         List<String> mAppTokens = new ArrayList<>();
    345 
    346         WindowTask(TaskProto proto) {
    347             super(proto.windowContainer);
    348             mTaskId = proto.id;
    349             mFullscreen = proto.fillsParent;
    350             mBounds = extract(proto.bounds);
    351             for (int i = 0; i < proto.appWindowTokens.length; i++) {
    352                 AppWindowTokenProto appWindowTokenProto = proto.appWindowTokens[i];
    353                 mAppTokens.add(appWindowTokenProto.name);
    354                 WindowTokenProto windowTokenProto = appWindowTokenProto.windowToken;
    355                 for (int j = 0; j < windowTokenProto.windows.length; j++) {
    356                     WindowStateProto windowProto = windowTokenProto.windows[j];
    357                     WindowState window = new WindowState(windowProto);
    358                     mSubWindows.add(window);
    359                     mSubWindows.addAll(window.getWindows());
    360                 }
    361             }
    362             mTempInsetBounds = extract(proto.tempInsetBounds);
    363         }
    364     }
    365 
    366     static class ConfigurationContainer {
    367         final Configuration mOverrideConfiguration = new Configuration();
    368         final Configuration mFullConfiguration = new Configuration();
    369         final Configuration mMergedOverrideConfiguration = new Configuration();
    370 
    371         ConfigurationContainer(ConfigurationContainerProto proto) {
    372             if (proto == null) {
    373                 return;
    374             }
    375             mOverrideConfiguration.setTo(extract(proto.overrideConfiguration));
    376             mFullConfiguration.setTo(extract(proto.fullConfiguration));
    377             mMergedOverrideConfiguration.setTo(extract(proto.mergedOverrideConfiguration));
    378         }
    379 
    380         int getWindowingMode() {
    381             if (mFullConfiguration == null) {
    382                 return WINDOWING_MODE_UNDEFINED;
    383             }
    384             return mFullConfiguration.windowConfiguration.getWindowingMode();
    385         }
    386 
    387         int getActivityType() {
    388             if (mFullConfiguration == null) {
    389                 return ACTIVITY_TYPE_UNDEFINED;
    390             }
    391             return mFullConfiguration.windowConfiguration.getActivityType();
    392         }
    393     }
    394 
    395     static abstract class WindowContainer extends ConfigurationContainer {
    396 
    397         protected boolean mFullscreen;
    398         protected Rect mBounds;
    399         protected int mOrientation;
    400         protected List<WindowState> mSubWindows = new ArrayList<>();
    401 
    402         WindowContainer(WindowContainerProto proto) {
    403             super(proto.configurationContainer);
    404             mOrientation = proto.orientation;
    405         }
    406 
    407         Rect getBounds() {
    408             return mBounds;
    409         }
    410 
    411         boolean isFullscreen() {
    412             return mFullscreen;
    413         }
    414 
    415         List<WindowState> getWindows() {
    416             return mSubWindows;
    417         }
    418     }
    419 
    420     static class Display extends WindowContainer {
    421 
    422         private final int mDisplayId;
    423         private Rect mDisplayRect = new Rect();
    424         private Rect mAppRect = new Rect();
    425         private int mDpi;
    426         private String mName;
    427 
    428         public Display(DisplayProto proto) {
    429             super(proto.windowContainer);
    430             mDisplayId = proto.id;
    431             for (int i = 0; i < proto.aboveAppWindows.length; i++) {
    432                 addWindowsFromTokenProto(proto.aboveAppWindows[i]);
    433             }
    434             for (int i = 0; i < proto.belowAppWindows.length; i++) {
    435                 addWindowsFromTokenProto(proto.belowAppWindows[i]);
    436             }
    437             for (int i = 0; i < proto.imeWindows.length; i++) {
    438                 addWindowsFromTokenProto(proto.imeWindows[i]);
    439             }
    440             mDpi = proto.dpi;
    441             DisplayInfoProto infoProto = proto.displayInfo;
    442             if (infoProto != null) {
    443                 mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
    444                 mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
    445                 mName = infoProto.name;
    446             }
    447         }
    448 
    449         private void addWindowsFromTokenProto(WindowTokenProto proto) {
    450             for (int j = 0; j < proto.windows.length; j++) {
    451                 WindowStateProto windowProto = proto.windows[j];
    452                 WindowState childWindow = new WindowState(windowProto);
    453                 mSubWindows.add(childWindow);
    454                 mSubWindows.addAll(childWindow.getWindows());
    455             }
    456         }
    457 
    458         int getDisplayId() {
    459             return mDisplayId;
    460         }
    461 
    462         int getDpi() {
    463             return mDpi;
    464         }
    465 
    466         Rect getDisplayRect() {
    467             return mDisplayRect;
    468         }
    469 
    470         String getName() {
    471             return mName;
    472         }
    473 
    474         @Override
    475         public String toString() {
    476             return "Display #" + mDisplayId + ": name=" + mName + " mDisplayRect=" + mDisplayRect
    477                     + " mAppRect=" + mAppRect;
    478         }
    479     }
    480 
    481     public static class WindowState extends WindowContainer {
    482 
    483         private static final int WINDOW_TYPE_STARTING = 1;
    484         private static final int WINDOW_TYPE_EXITING = 2;
    485         private static final int WINDOW_TYPE_DEBUGGER = 3;
    486 
    487         private String mName;
    488         private final String mAppToken;
    489         private final int mWindowType;
    490         private int mType = 0;
    491         private int mDisplayId;
    492         private int mStackId;
    493         private boolean mShown;
    494         private Rect mContainingFrame = new Rect();
    495         private Rect mParentFrame = new Rect();
    496         private Rect mContentFrame = new Rect();
    497         private Rect mFrame = new Rect();
    498         private Rect mCrop = new Rect();
    499 
    500         WindowState(WindowStateProto proto) {
    501             super(proto.windowContainer);
    502             IdentifierProto identifierProto = proto.identifier;
    503             mName = identifierProto.title;
    504             mAppToken = Integer.toHexString(identifierProto.hashCode);
    505             mDisplayId = proto.displayId;
    506             mStackId = proto.stackId;
    507             if (proto.attributes != null) {
    508                 mType = proto.attributes.type;
    509             }
    510             WindowStateAnimatorProto animatorProto = proto.animator;
    511             if (animatorProto != null) {
    512                 if (animatorProto.surface != null) {
    513                     WindowSurfaceControllerProto surfaceProto = animatorProto.surface;
    514                     mShown = surfaceProto.shown;
    515                 }
    516                 mCrop = extract(animatorProto.lastClipRect);
    517             }
    518             mFrame = extract(proto.frame);
    519             mContainingFrame = extract(proto.containingFrame);
    520             mParentFrame = extract(proto.parentFrame);
    521             mContentFrame = extract(proto.contentFrame);
    522             if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
    523                 mWindowType = WINDOW_TYPE_STARTING;
    524                 // Existing code depends on the prefix being removed
    525                 mName = mName.substring(STARTING_WINDOW_PREFIX.length());
    526             } else if (proto.animatingExit) {
    527                 mWindowType = WINDOW_TYPE_EXITING;
    528             } else if (mName.startsWith(DEBUGGER_WINDOW_PREFIX)) {
    529                 mWindowType = WINDOW_TYPE_STARTING;
    530                 mName = mName.substring(DEBUGGER_WINDOW_PREFIX.length());
    531             } else {
    532                 mWindowType = 0;
    533             }
    534             for (int i = 0; i < proto.childWindows.length; i++) {
    535                 WindowStateProto childProto = proto.childWindows[i];
    536                 WindowState childWindow = new WindowState(childProto);
    537                 mSubWindows.add(childWindow);
    538                 mSubWindows.addAll(childWindow.getWindows());
    539             }
    540         }
    541 
    542         public String getName() {
    543             return mName;
    544         }
    545 
    546         String getToken() {
    547             return mAppToken;
    548         }
    549 
    550         int getDisplayId() {
    551             return mDisplayId;
    552         }
    553 
    554         int getStackId() {
    555             return mStackId;
    556         }
    557 
    558         public Rect getFrame() {
    559             return mFrame;
    560         }
    561 
    562         public Rect getContentFrame() {
    563             return mContentFrame;
    564         }
    565 
    566         Rect getCrop() {
    567             return mCrop;
    568         }
    569 
    570         public boolean isShown() {
    571             return mShown;
    572         }
    573 
    574         public int getType() {
    575             return mType;
    576         }
    577 
    578         private String getWindowTypeSuffix(int windowType) {
    579             switch (windowType) {
    580                 case WINDOW_TYPE_STARTING:
    581                     return " STARTING";
    582                 case WINDOW_TYPE_EXITING:
    583                     return " EXITING";
    584                 case WINDOW_TYPE_DEBUGGER:
    585                     return " DEBUGGER";
    586                 default:
    587                     break;
    588             }
    589             return "";
    590         }
    591 
    592         @Override
    593         public String toString() {
    594             return "WindowState: {" + mAppToken + " " + mName
    595                     + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
    596                     + " cf=" + mContainingFrame + " pf=" + mParentFrame;
    597         }
    598     }
    599 }
    600