summaryrefslogtreecommitdiff
path: root/cmds/uiautomator
diff options
context:
space:
mode:
authorEvan Rosky <erosky@google.com>2020-04-28 14:55:46 -0700
committerEvan Rosky <erosky@google.com>2020-04-28 15:00:21 -0700
commit89f367d39fa915b90ddcd45f6294c19d580e8426 (patch)
treedeae80785b7a69ac0578b49f842a54693ae7e7d5 /cmds/uiautomator
parent046df72d9a8a7d5f6e6f255c313fc129db874f68 (diff)
Add a window dump for uiautomator
There's no good way to dump all the windows and views that uiautomator sees, so added one. Otherwise its a pain to do multi-window debugging. This could also possibly be used by various hierarchy viewers that currently are only able to look at the active window. Bug: 151632128 Test: adb shell uiautomator dump --windows Change-Id: I20b45507584e35ef5d725691b2ffb060bcfa18db
Diffstat (limited to 'cmds/uiautomator')
-rw-r--r--cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java38
-rw-r--r--cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java96
2 files changed, 122 insertions, 12 deletions
diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
index c35f7fc3fcc8..3b14be7327f7 100644
--- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
+++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
@@ -16,6 +16,7 @@
package com.android.commands.uiautomator;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.UiAutomation;
import android.graphics.Point;
import android.hardware.display.DisplayManagerGlobal;
@@ -61,11 +62,14 @@ public class DumpCommand extends Command {
public void run(String[] args) {
File dumpFile = DEFAULT_DUMP_FILE;
boolean verboseMode = true;
+ boolean allWindows = false;
for (String arg : args) {
if (arg.equals("--compressed"))
verboseMode = false;
- else if (!arg.startsWith("-")) {
+ else if (arg.equals("--windows")) {
+ allWindows = true;
+ } else if (!arg.startsWith("-")) {
dumpFile = new File(arg);
}
}
@@ -85,18 +89,28 @@ public class DumpCommand extends Command {
try {
UiAutomation uiAutomation = automationWrapper.getUiAutomation();
uiAutomation.waitForIdle(1000, 1000 * 10);
- AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
- if (info == null) {
- System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
- return;
- }
+ if (allWindows) {
+ AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ uiAutomation.setServiceInfo(info);
+ AccessibilityNodeInfoDumper.dumpWindowsToFile(
+ uiAutomation.getWindowsOnAllDisplays(), dumpFile,
+ DisplayManagerGlobal.getInstance());
+ } else {
+ AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
+ if (info == null) {
+ System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
+ return;
+ }
- Display display =
- DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- int rotation = display.getRotation();
- Point size = new Point();
- display.getSize(size);
- AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y);
+ Display display =
+ DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x,
+ size.y);
+ }
} catch (TimeoutException re) {
System.err.println("ERROR: could not get idle state.");
return;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 63c51e84d74a..ab198b319e27 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -16,11 +16,17 @@
package com.android.uiautomator.core;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
+import android.view.Display;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import org.xmlpull.v1.XmlSerializer;
@@ -28,6 +34,7 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
+import java.util.List;
/**
*
@@ -98,6 +105,95 @@ public class AccessibilityNodeInfoDumper {
Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
}
+ /**
+ * Using {@link AccessibilityWindowInfo} this method will dump some window information and
+ * then walk the layout hierarchy of it's
+ * and generates an xml dump to the location specified by <code>dumpFile</code>
+ * @param allWindows All windows indexed by display-id.
+ * @param dumpFile The file to dump to.
+ */
+ public static void dumpWindowsToFile(SparseArray<List<AccessibilityWindowInfo>> allWindows,
+ File dumpFile, DisplayManagerGlobal displayManager) {
+ if (allWindows.size() == 0) {
+ return;
+ }
+ final long startTime = SystemClock.uptimeMillis();
+ try {
+ FileWriter writer = new FileWriter(dumpFile);
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter stringWriter = new StringWriter();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", "displays");
+ for (int d = 0, nd = allWindows.size(); d < nd; ++d) {
+ int displayId = allWindows.keyAt(d);
+ Display display = displayManager.getRealDisplay(displayId);
+ if (display == null) {
+ continue;
+ }
+ final List<AccessibilityWindowInfo> windows = allWindows.valueAt(d);
+ if (windows.isEmpty()) {
+ continue;
+ }
+ serializer.startTag("", "display");
+ serializer.attribute("", "id", Integer.toString(displayId));
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ for (int i = 0, n = windows.size(); i < n; ++i) {
+ dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation);
+ }
+ serializer.endTag("", "display");
+ }
+ serializer.endTag("", "displays");
+ serializer.endDocument();
+ writer.write(stringWriter.toString());
+ writer.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "failed to dump window to file", e);
+ }
+ final long endTime = SystemClock.uptimeMillis();
+ Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
+ }
+
+ private static void dumpWindowRec(AccessibilityWindowInfo winfo, XmlSerializer serializer,
+ int index, int width, int height, int rotation) throws IOException {
+ serializer.startTag("", "window");
+ serializer.attribute("", "index", Integer.toString(index));
+ final CharSequence title = winfo.getTitle();
+ serializer.attribute("", "title", title != null ? title.toString() : "");
+ final Rect tmpBounds = new Rect();
+ winfo.getBoundsInScreen(tmpBounds);
+ serializer.attribute("", "bounds", tmpBounds.toShortString());
+ serializer.attribute("", "active", Boolean.toString(winfo.isActive()));
+ serializer.attribute("", "focused", Boolean.toString(winfo.isFocused()));
+ serializer.attribute("", "accessibility-focused",
+ Boolean.toString(winfo.isAccessibilityFocused()));
+ serializer.attribute("", "id", Integer.toString(winfo.getId()));
+ serializer.attribute("", "layer", Integer.toString(winfo.getLayer()));
+ serializer.attribute("", "type", AccessibilityWindowInfo.typeToString(winfo.getType()));
+ int count = winfo.getChildCount();
+ for (int i = 0; i < count; ++i) {
+ AccessibilityWindowInfo child = winfo.getChild(i);
+ if (child == null) {
+ Log.i(LOGTAG, String.format("Null window child %d/%d, parent: %s", i, count,
+ winfo.getTitle()));
+ continue;
+ }
+ dumpWindowRec(child, serializer, i, width, height, rotation);
+ child.recycle();
+ }
+ AccessibilityNodeInfo root = winfo.getRoot();
+ if (root != null) {
+ serializer.startTag("", "hierarchy");
+ serializer.attribute("", "rotation", Integer.toString(rotation));
+ dumpNodeRec(root, serializer, 0, width, height);
+ root.recycle();
+ serializer.endTag("", "hierarchy");
+ }
+ serializer.endTag("", "window");
+ }
+
private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index,
int width, int height) throws IOException {
serializer.startTag("", "node");