cts: add test for AndroidManifestLayout
bug: 27157489
Change-Id: Ica7f6dba42cc86f7819f2d4eb13174567be4cdc5
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index ff861c0..8a2a448 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -89,6 +89,46 @@
android:taskAffinity="nobody.but.FreeformActivity"
android:exported="true"
/>
+ <activity android:name=".TopLeftLayoutActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true">
+ <layout android:defaultWidth="160dp"
+ android:defaultHeight="160dp"
+ android:gravity="top|left"
+ android:minimalSize="80dp"
+ />
+ </activity>
+ <activity android:name=".TopRightLayoutActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true">
+ <layout android:defaultWidth="25%"
+ android:defaultHeight="25%"
+ android:gravity="top|right"
+ android:minimalSize="80dp"
+ />
+ </activity>
+ <activity android:name=".BottomLeftLayoutActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true">
+ <layout android:defaultWidth="25%"
+ android:defaultHeight="25%"
+ android:gravity="bottom|left"
+ android:minimalSize="80dp"
+ />
+ </activity>
+ <activity android:name=".BottomRightLayoutActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true">
+ <layout android:defaultWidth="160dp"
+ android:defaultHeight="160dp"
+ android:gravity="bottom|right"
+ android:minimalSize="80dp"
+ />
+ </activity>
</application>
</manifest>
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
new file mode 100644
index 0000000..27a6fe2
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+
+public class BottomLeftLayoutActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java
new file mode 100644
index 0000000..7a91510
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+
+public class BottomRightLayoutActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java
new file mode 100644
index 0000000..3a0267b
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+
+public class TopLeftLayoutActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java
new file mode 100644
index 0000000..eb3b1b0
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+
+public class TopRightLayoutActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
index e933d26..bc48964 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
@@ -75,12 +75,4 @@
assertActivityLifecycle(TEST_ACTIVITY, true);
assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false);
}
-
- private void resizeActivityTask(String activityName, int left, int top, int right, int bottom)
- throws Exception {
- final int taskId = getActivityTaskId(activityName);
- final String cmd = "am task resize "
- + taskId + " " + left + " " + top + " " + right + " " + bottom;
- mDevice.executeShellCommand(cmd);
- }
}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
new file mode 100644
index 0000000..78cc4b9
--- /dev/null
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.cts;
+
+import java.lang.Exception;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import junit.framework.Assert;
+
+import java.awt.Rectangle;
+import android.server.cts.WindowManagerState.WindowState;
+import android.server.cts.WindowManagerState.Display;
+
+import static com.android.ddmlib.Log.LogLevel.INFO;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+public class ActivityManagerManifestLayoutTests extends ActivityManagerTestBase {
+
+ // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
+ // (Needed in host-side test to convert dp to px.)
+ private static final int DISPLAY_DENSITY_DEFAULT = 160;
+
+ // Test parameters
+ private static final int DEFAULT_WIDTH_DP = 160;
+ private static final int DEFAULT_HEIGHT_DP = 160;
+ private static final float DEFAULT_WIDTH_FRACTION = 0.25f;
+ private static final float DEFAULT_HEIGHT_FRACTION = 0.25f;
+ private static final int MINIMAL_SIZE_DP = 80;
+
+ private static final int GRAVITY_VER_CENTER = 0x01;
+ private static final int GRAVITY_VER_TOP = 0x02;
+ private static final int GRAVITY_VER_BOTTOM = 0x04;
+ private static final int GRAVITY_HOR_CENTER = 0x10;
+ private static final int GRAVITY_HOR_LEFT = 0x20;
+ private static final int GRAVITY_HOR_RIGHT = 0x40;
+
+ private List<WindowState> mTempWindowList = new ArrayList();
+
+ public void testGravityAndDefaultSizeTopLeft() throws Exception {
+ testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/, false /*minimize*/);
+ }
+
+ public void testGravityAndDefaultSizeTopRight() throws Exception {
+ testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/, false /*minimize*/);
+ }
+
+ public void testGravityAndDefaultSizeBottomLeft() throws Exception {
+ testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/, false /*minimize*/);
+ }
+
+ public void testGravityAndDefaultSizeBottomRight() throws Exception {
+ testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/, false /*minimize*/);
+ }
+
+ public void testMinimalSize() throws Exception {
+ testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/, true /*minimize*/);
+ }
+
+ private void testLayout(
+ int vGravity, int hGravity, boolean fraction, boolean minimize) throws Exception {
+ if (!supportsFreeform()) {
+ CLog.logAndDisplay(INFO, "Skipping test: no freeform support");
+ return;
+ }
+
+ final String activityName = (vGravity == GRAVITY_VER_TOP ? "Top" : "Bottom")
+ + (hGravity == GRAVITY_HOR_LEFT ? "Left" : "Right") + "LayoutActivity";
+
+ // Launch in freeform stack
+ launchActivityInStack(activityName, FREEFORM_WORKSPACE_STACK_ID);
+
+ int expectedWidthDp = DEFAULT_WIDTH_DP;
+ int expectedHeightDp = DEFAULT_HEIGHT_DP;
+
+ // If we're testing fraction dimensions, set the expected to -1. The expected value
+ // depends on the display size, and will be evaluated when we have display info.
+ if (fraction) {
+ expectedWidthDp = expectedHeightDp = -1;
+ }
+
+ // If we're testing minimal size, issue command to resize to <0,0,1,1>. We expect
+ // the size to be floored at MINIMAL_SIZE_DPxMINIMAL_SIZE_DP.
+ if (minimize) {
+ resizeActivityTask(activityName, 0, 0, 1, 1);
+ expectedWidthDp = expectedHeightDp = MINIMAL_SIZE_DP;
+ }
+
+ verifyWindowState(activityName, vGravity, hGravity, expectedWidthDp, expectedHeightDp);
+ }
+
+ private void verifyWindowState(String activityName, int vGravity, int hGravity,
+ int expectedWidthDp, int expectedHeightDp) throws Exception {
+ final String windowName = getWindowName(activityName);
+
+ mAmWmState.computeState(mDevice, true /* visibleOnly */, new String[] {activityName});
+
+ mAmWmState.assertSanity();
+
+ mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
+
+ mAmWmState.getWmState().getMatchingWindowState(windowName, mTempWindowList);
+
+ Assert.assertEquals("Should have exactly one window state for the activity.",
+ 1, mTempWindowList.size());
+
+ WindowState ws = mTempWindowList.get(0);
+
+ Display display = mAmWmState.getWmState().getDisplay(ws.getDisplayId());
+ Assert.assertNotNull("Should be on a display", display);
+
+ final Rectangle containingRect = ws.getContainingFrame();
+ final Rectangle appRect = display.getAppRect();
+ final int expectedWidthPx, expectedHeightPx;
+ // Evaluate the expected window size in px. If we're using fraction dimensions,
+ // calculate the size based on the app rect size. Otherwise, convert the expected
+ // size in dp to px.
+ if (expectedWidthDp < 0 || expectedHeightDp < 0) {
+ expectedWidthPx = (int) (appRect.width * DEFAULT_WIDTH_FRACTION);
+ expectedHeightPx = (int) (appRect.height * DEFAULT_HEIGHT_FRACTION);
+ } else {
+ final int densityDpi = display.getDpi();
+ expectedWidthPx = dpToPx(expectedWidthDp, densityDpi);
+ expectedHeightPx = dpToPx(expectedHeightDp, densityDpi);
+ }
+ verifyFrameSizeAndPosition(
+ vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, appRect);
+ }
+
+ private void verifyFrameSizeAndPosition(
+ int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx,
+ Rectangle containingFrame, Rectangle parentFrame) {
+ Assert.assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width);
+ Assert.assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height);
+
+ if (vGravity == GRAVITY_VER_TOP) {
+ Assert.assertEquals("Should be on the top", parentFrame.y, containingFrame.y);
+ } else {
+ Assert.assertEquals("Should be on the bottom",
+ parentFrame.y + parentFrame.height, containingFrame.y + containingFrame.height);
+ }
+
+ if (hGravity == GRAVITY_HOR_LEFT) {
+ Assert.assertEquals("Should be on the left", parentFrame.x, containingFrame.x);
+ } else {
+ Assert.assertEquals("Should be on the right",
+ parentFrame.x + parentFrame.width, containingFrame.x + containingFrame.width);
+ }
+ }
+
+ private static int dpToPx(float dp, int densityDpi){
+ return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
index 1db6829..1a6a3b8 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
@@ -60,6 +60,10 @@
boolean retry = false;
String dump = null;
+ CLog.logAndDisplay(INFO, "==============================");
+ CLog.logAndDisplay(INFO, " ActivityManagerState ");
+ CLog.logAndDisplay(INFO, "==============================");
+
do {
if (retry) {
CLog.logAndDisplay(INFO, "***Incomplete AM state. Retrying...");
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 049a04a..fcc2bd0 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -122,6 +122,14 @@
mDevice.executeShellCommand(getAmStartCmd(activityName) + " --stack " + stackId);
}
+ protected void resizeActivityTask(String activityName, int left, int top, int right, int bottom)
+ throws Exception {
+ final int taskId = getActivityTaskId(activityName);
+ final String cmd = "am task resize "
+ + taskId + " " + left + " " + top + " " + right + " " + bottom;
+ mDevice.executeShellCommand(cmd);
+ }
+
// Utility method for debugging, not used directly here, but useful, so kept around.
protected void printStacksAndTasks() throws DeviceNotAvailableException {
CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
index a30a2a7..e8b0f59 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
@@ -37,32 +37,33 @@
private static final String DUMPSYS_WINDOWS_APPS = "dumpsys window apps";
private static final String DUMPSYS_WINDOWS_VISIBLE_APPS = "dumpsys window visible-apps";
- private final Pattern mWindowPattern =
+ private static final Pattern sWindowPattern =
Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
- private final Pattern mStartingWindowPattern =
+ private static final Pattern sStartingWindowPattern =
Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
- private final Pattern mExitingWindowPattern =
+ private static final Pattern sExitingWindowPattern =
Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
- private final Pattern mFocusedWindowPattern = Pattern.compile(
+ private static final Pattern sFocusedWindowPattern = Pattern.compile(
"mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) (\\S+)\\}");
- private final Pattern mAppErrorFocusedWindowPattern = Pattern.compile(
+ private static final Pattern sAppErrorFocusedWindowPattern = Pattern.compile(
"mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Application Error\\: (\\S+)\\}");
- private final Pattern mFocusedAppPattern =
+ private static final Pattern sFocusedAppPattern =
Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
+ "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
- private final Pattern mStackIdPattern = Pattern.compile("mStackId=(\\d+)");
+ private static final Pattern sStackIdPattern = Pattern.compile("mStackId=(\\d+)");
- private final Pattern[] mExtractStackExitPatterns = { mStackIdPattern, mWindowPattern,
- mStartingWindowPattern, mExitingWindowPattern, mFocusedWindowPattern,
- mAppErrorFocusedWindowPattern, mFocusedAppPattern };
+ private static final Pattern[] sExtractStackExitPatterns = {
+ sStackIdPattern, sWindowPattern, sStartingWindowPattern, sExitingWindowPattern,
+ sFocusedWindowPattern, sAppErrorFocusedWindowPattern, sFocusedAppPattern };
// Windows in z-order with the top most at the front of the list.
private List<String> mWindows = new ArrayList();
- private List<String> mRawWindows = new ArrayList();
+ private List<WindowState> mWindowStates = new ArrayList();
private List<WindowStack> mStacks = new ArrayList();
+ private List<Display> mDisplays = new ArrayList();
private String mFocusedWindow = null;
private String mFocusedApp = null;
private final LinkedList<String> mSysDump = new LinkedList();
@@ -74,6 +75,9 @@
boolean retry = false;
String dump = null;
+ CLog.logAndDisplay(INFO, "==============================");
+ CLog.logAndDisplay(INFO, " WindowManagerState ");
+ CLog.logAndDisplay(INFO, "==============================");
do {
if (retry) {
CLog.logAndDisplay(INFO, "***Incomplete WM state. Retrying...");
@@ -117,47 +121,52 @@
Collections.addAll(mSysDump, sysDump.split("\\n"));
while (!mSysDump.isEmpty()) {
+ final Display display =
+ Display.create(mSysDump, sExtractStackExitPatterns);
+ if (display != null) {
+ CLog.logAndDisplay(INFO, display.toString());
+ mDisplays.add(display);
+ continue;
+ }
+
final WindowStack stack =
- WindowStack.create(mSysDump, mStackIdPattern, mExtractStackExitPatterns);
+ WindowStack.create(mSysDump, sStackIdPattern, sExtractStackExitPatterns);
if (stack != null) {
mStacks.add(stack);
continue;
}
- final String line = mSysDump.pop().trim();
- Matcher matcher = mWindowPattern.matcher(line);
- if (matcher.matches()) {
- CLog.logAndDisplay(INFO, line);
- final String window = matcher.group(4);
+ final WindowState ws = WindowState.create(mSysDump, sExtractStackExitPatterns);
+ if (ws != null) {
+ CLog.logAndDisplay(INFO, ws.toString());
if (visibleOnly && mWindows.isEmpty()) {
// This is the front window. Check to see if we are in the middle of
// transitioning. If we are, we want to skip dumping until window manager is
// done transitioning the top window.
- matcher = mStartingWindowPattern.matcher(line);
- if (matcher.matches()) {
+ if (ws.isStartingWindow()) {
CLog.logAndDisplay(INFO,
"Skipping dump due to starting window transition...");
return;
}
- matcher = mExitingWindowPattern.matcher(line);
- if (matcher.matches()) {
+ if (ws.isExitingWindow()) {
CLog.logAndDisplay(INFO,
"Skipping dump due to exiting window transition...");
return;
}
}
- CLog.logAndDisplay(INFO, window);
- mWindows.add(window);
- mRawWindows.add(line);
+ mWindows.add(ws.getName());
+ mWindowStates.add(ws);
continue;
}
- matcher = mFocusedWindowPattern.matcher(line);
+ final String line = mSysDump.pop().trim();
+
+ Matcher matcher = sFocusedWindowPattern.matcher(line);
if (matcher.matches()) {
CLog.logAndDisplay(INFO, line);
final String focusedWindow = matcher.group(3);
@@ -166,7 +175,7 @@
continue;
}
- matcher = mAppErrorFocusedWindowPattern.matcher(line);
+ matcher = sAppErrorFocusedWindowPattern.matcher(line);
if (matcher.matches()) {
CLog.logAndDisplay(INFO, line);
final String focusedWindow = matcher.group(3);
@@ -175,7 +184,7 @@
continue;
}
- matcher = mFocusedAppPattern.matcher(line);
+ matcher = sFocusedAppPattern.matcher(line);
if (matcher.matches()) {
CLog.logAndDisplay(INFO, line);
final String focusedApp = matcher.group(5);
@@ -189,17 +198,31 @@
void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
tokenList.clear();
- for (String line : mRawWindows) {
- if (line.contains(windowName)) {
- Matcher matcher = mWindowPattern.matcher(line);
- if (matcher.matches()) {
- CLog.logAndDisplay(INFO, "Found activity window: " + line);
- tokenList.add(matcher.group(2));
- }
+ for (WindowState ws : mWindowStates) {
+ if (windowName.equals(ws.getName())) {
+ tokenList.add(ws.getToken());
}
}
}
+ void getMatchingWindowState(final String windowName, List<WindowState> windowList) {
+ windowList.clear();
+ for (WindowState ws : mWindowStates) {
+ if (windowName.equals(ws.getName())) {
+ windowList.add(ws);
+ }
+ }
+ }
+
+ Display getDisplay(int displayId) {
+ for (Display display : mDisplays) {
+ if (displayId == display.getDisplayId()) {
+ return display;
+ }
+ }
+ return null;
+ }
+
String getFrontWindow() {
return mWindows.get(0);
}
@@ -246,15 +269,16 @@
private void reset() {
mSysDump.clear();
mStacks.clear();
+ mDisplays.clear();
mWindows.clear();
- mRawWindows.clear();
+ mWindowStates.clear();
mFocusedWindow = null;
mFocusedApp = null;
}
static class WindowStack extends WindowContainer {
- private static final Pattern TASK_ID_PATTERN = Pattern.compile("taskId=(\\d+)");
+ private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
int mStackId;
ArrayList<WindowTask> mTasks = new ArrayList();
@@ -288,13 +312,13 @@
final List<Pattern> taskExitPatterns = new ArrayList();
Collections.addAll(taskExitPatterns, exitPatterns);
- taskExitPatterns.add(TASK_ID_PATTERN);
+ taskExitPatterns.add(sTaskIdPattern);
final Pattern[] taskExitPatternsArray =
taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
while (!doneExtracting(dump, exitPatterns)) {
final WindowTask task =
- WindowTask.create(dump, TASK_ID_PATTERN, taskExitPatternsArray);
+ WindowTask.create(dump, sTaskIdPattern, taskExitPatternsArray);
if (task != null) {
mTasks.add(task);
@@ -324,10 +348,10 @@
}
static class WindowTask extends WindowContainer {
- private static final Pattern TEMP_INSET_BOUNDS_PATTERN =
+ private static final Pattern sTempInsetBoundsPattern =
Pattern.compile("mTempInsetBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
- private static final Pattern APP_TOKEN_PATTERN = Pattern.compile(
+ private static final Pattern sAppTokenPattern = Pattern.compile(
"Activity #(\\d+) AppWindowToken\\{(\\S+) token=Token\\{(\\S+) "
+ "ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}\\}\\}");
@@ -373,13 +397,13 @@
continue;
}
- Matcher matcher = TEMP_INSET_BOUNDS_PATTERN.matcher(line);
+ Matcher matcher = sTempInsetBoundsPattern.matcher(line);
if (matcher.matches()) {
CLog.logAndDisplay(INFO, line);
mTempInsetBounds = extractBounds(matcher);
}
- matcher = APP_TOKEN_PATTERN.matcher(line);
+ matcher = sAppTokenPattern.matcher(line);
if (matcher.matches()) {
CLog.logAndDisplay(INFO, line);
final String appToken = matcher.group(6);
@@ -392,8 +416,8 @@
}
static abstract class WindowContainer {
- protected static final Pattern FULLSCREEN_PATTERN = Pattern.compile("mFullscreen=(\\S+)");
- protected static final Pattern BOUNDS_PATTERN =
+ protected static final Pattern sFullscreenPattern = Pattern.compile("mFullscreen=(\\S+)");
+ protected static final Pattern sBoundsPattern =
Pattern.compile("mBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
protected boolean mFullscreen;
@@ -414,7 +438,7 @@
}
boolean extractFullscreen(String line) {
- final Matcher matcher = FULLSCREEN_PATTERN.matcher(line);
+ final Matcher matcher = sFullscreenPattern.matcher(line);
if (!matcher.matches()) {
return false;
}
@@ -426,7 +450,7 @@
}
boolean extractBounds(String line) {
- final Matcher matcher = BOUNDS_PATTERN.matcher(line);
+ final Matcher matcher = sBoundsPattern.matcher(line);
if (!matcher.matches()) {
return false;
}
@@ -446,6 +470,19 @@
return rect;
}
+ static void extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList) {
+ for (Rectangle rect : rectList) {
+ if (rect == null) {
+ return;
+ }
+ final int left = Integer.valueOf(matcher.group(groupIndex++));
+ final int top = Integer.valueOf(matcher.group(groupIndex++));
+ final int right = Integer.valueOf(matcher.group(groupIndex++));
+ final int bottom = Integer.valueOf(matcher.group(groupIndex++));
+ rect.setBounds(left, top, right - left, bottom - top);
+ }
+ }
+
Rectangle getBounds() {
return mBounds;
}
@@ -454,4 +491,198 @@
return mFullscreen;
}
}
+
+ static class Display extends WindowContainer {
+ private static final String TAG = "[Display] ";
+
+ private static final Pattern sDisplayIdPattern =
+ Pattern.compile("Display: mDisplayId=(\\d+)");
+ private static final Pattern sDisplayInfoPattern =
+ Pattern.compile("(.+) (\\d+)dpi cur=(\\d+)x(\\d+) app=(\\d+)x(\\d+) (.+)");
+
+ private final int mDisplayId;
+ private Rectangle mDisplayRect = new Rectangle();
+ private Rectangle mAppRect = new Rectangle();
+ private int mDpi;
+
+ private Display(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getDpi() {
+ return mDpi;
+ }
+
+ Rectangle getDisplayRect() {
+ return mDisplayRect;
+ }
+
+ Rectangle getAppRect() {
+ return mAppRect;
+ }
+
+ static Display create(LinkedList<String> dump, Pattern[] exitPatterns) {
+ // TODO: exit pattern for displays?
+ final String line = dump.peek().trim();
+
+ Matcher matcher = sDisplayIdPattern.matcher(line);
+ if (!matcher.matches()) {
+ return null;
+ }
+
+ CLog.logAndDisplay(INFO, TAG + "DISPLAY_ID: " + line);
+ dump.pop();
+
+ final int displayId = Integer.valueOf(matcher.group(1));
+ final Display display = new Display(displayId);
+ display.extract(dump, exitPatterns);
+ return display;
+ }
+
+ private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
+ while (!doneExtracting(dump, exitPatterns)) {
+ final String line = dump.pop().trim();
+
+ final Matcher matcher = sDisplayInfoPattern.matcher(line);
+ if (matcher.matches()) {
+ CLog.logAndDisplay(INFO, TAG + "DISPLAY_INFO: " + line);
+ mDpi = Integer.valueOf(matcher.group(2));
+
+ final int displayWidth = Integer.valueOf(matcher.group(3));
+ final int displayHeight = Integer.valueOf(matcher.group(4));
+ mDisplayRect.setBounds(0, 0, displayWidth, displayHeight);
+
+ final int appWidth = Integer.valueOf(matcher.group(5));
+ final int appHeight = Integer.valueOf(matcher.group(6));
+ mAppRect.setBounds(0, 0, appWidth, appHeight);
+
+ // break as we don't need other info for now
+ break;
+ }
+ // Extract other info here if needed
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
+ + " mAppRect=" + mAppRect;
+ }
+ }
+
+ static class WindowState extends WindowContainer {
+ private static final String TAG = "[WindowState] ";
+
+ private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
+ private static final Pattern sFramePattern =
+ Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
+ private static final Pattern sWindowAssociationPattern =
+ Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
+
+ private final String mName;
+ private final String mAppToken;
+ private final boolean mStarting;
+ private final boolean mExiting;
+ private int mDisplayId = 0;
+ private Rectangle mContainingFrame = new Rectangle();
+ private Rectangle mParentFrame = new Rectangle();
+
+ private WindowState(Matcher matcher, boolean starting, boolean exiting) {
+ mName = matcher.group(4);
+ mAppToken = matcher.group(2);
+ mStarting = starting;
+ mExiting = exiting;
+ }
+
+ String getName() {
+ return mName;
+ }
+
+ String getToken() {
+ return mAppToken;
+ }
+
+ boolean isStartingWindow() {
+ return mStarting;
+ }
+
+ boolean isExitingWindow() {
+ return mExiting;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ Rectangle getContainingFrame() {
+ return mContainingFrame;
+ }
+
+ Rectangle getParentFrame() {
+ return mParentFrame;
+ }
+
+ static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
+ final String line = dump.peek().trim();
+
+ Matcher matcher = sWindowPattern.matcher(line);
+ if (!matcher.matches()) {
+ return null;
+ }
+
+ CLog.logAndDisplay(INFO, TAG + "WINDOW: " + line);
+ dump.pop();
+
+ final WindowState window;
+ Matcher specialMatcher = sStartingWindowPattern.matcher(line);
+ if (specialMatcher.matches()) {
+ CLog.logAndDisplay(INFO, TAG + "STARTING: " + line);
+ window = new WindowState(specialMatcher, true, false);
+ } else {
+ specialMatcher = sExitingWindowPattern.matcher(line);
+ if (specialMatcher.matches()) {
+ CLog.logAndDisplay(INFO, TAG + "EXITING: " + line);
+ window = new WindowState(specialMatcher, false, true);
+ } else {
+ window = new WindowState(matcher, false, false);
+ }
+ }
+
+ window.extract(dump, exitPatterns);
+ return window;
+ }
+
+ private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
+ while (!doneExtracting(dump, exitPatterns)) {
+ final String line = dump.pop().trim();
+
+ Matcher matcher = sWindowAssociationPattern.matcher(line);
+ if (matcher.matches()) {
+ CLog.logAndDisplay(INFO, TAG + "WINDOW_ASSOCIATION: " + line);
+ mDisplayId = Integer.valueOf(matcher.group(1));
+ continue;
+ }
+
+ matcher = sFramePattern.matcher(line);
+ if (matcher.matches()) {
+ CLog.logAndDisplay(INFO, TAG + "FRAME: " + line);
+ extractMultipleBounds(matcher, 1, mContainingFrame, mParentFrame);
+ continue;
+ }
+
+ // Extract other info here if needed
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "WindowState: {" + mAppToken + " " + mName
+ + (mStarting ? " STARTING" : "") + (mExiting ? " EXITING" : "") + "}"
+ + " cf=" + mContainingFrame + " pf=" + mParentFrame;
+ }
+ }
}