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