这几天查看了下源码发现,uiautomatorviewer在获取界面布局信息的时候用的是启动一个脚本,该脚本在/system/bin/uiautomator。这个命令也可以在命令行下启动。
默认情况下,获取的控件信息保存在/storage/emulated/legacy/window_dump.xml文件中,你也可以改变它保存的目录,例如保存在data/local/tmp下
这是正常情况下的,但我进入秒表界面,将秒表开启。然后执行上面的命令:
报了could not get idle state的错。
说明uiautomator在获取界面状态信息时,首先要等界面处于idle空闲状态才会做dump操作。这就是uiautomator死活拿不到动态界面信息的原因。
调出uiautomator这个脚本。
- #
- # Copyright (C) 2012 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.
- #
- # Script to start "uiautomator" on the device
- #
- # The script does a couple of things:
- # * Use an alternative dalvik cache when running as non-root. Jar file needs
- # to be dexopt'd to run in Dalvik. For plain jar files, this is done at first
- # use. shell user does not have write permission to default system Dalvik
- # cache so we redirect to an alternative cache
- # * special processing for subcommand 'runtest':
- # * '--nohup' allows process continue to run even if parent process that
- # started it has already terminated. We parse for this parameter and set
- # signal trap. This is useful for testing with USB disconnected
- # * all jar files that the test classes resides in, or dependent on are
- # provided on command line and exported to CLASSPATH environment variable
- # before starting the Java code. This offloads the task of class loading
- # and resolving of cross jar class dependency to Dalvik
- # * all other subcommand or options are directly passed into Java code for
- # further parsing
- export run_base=/data/local/tmp
- export base=/system
- # if not running as root, trick dalvik into using an alternative dex cache
- if [ ${USER_ID} -ne 0 ]; then
- tmp_cache=${run_base}/dalvik-cache
- if [ ! -d ${tmp_cache} ]; then
- mkdir -p ${tmp_cache}
- fi
- export ANDROID_DATA=${run_base}
- fi
- # take first parameter as the command
- cmd=${1}
- if [ -z "${1}" ]; then
- cmd="help"
- fi
- # strip the command parameter
- if [ -n "${1}" ]; then
- shift
- fi
- CLASSPATH=/system/framework/android.test.runner.jar:${base}/framework/uiautomator.jar
- # eventually args will be what get passed down to Java code
- args=
- # we also pass the list of jar files, so we can extract class names for tests
- # if they are not explicitly specified
- jars=
- # special case pre-processing for 'runtest' command
- if [ "${cmd}" == "runtest" ]; then
- # first parse the jar paths
- while [ true ]; do
- if [ -z "${1}" ] && [ -z "${jars}" ]; then
- echo "Error: more parameters expected for runtest; please see usage for details"
- cmd="help"
- break
- fi
- if [ -z "${1}" ]; then
- break
- fi
- jar=${1}
- if [ "${1:0:1}" = "-" ]; then
- # we are done with jars, starting with parameters now
- break
- fi
- # if relative path, append the default path prefix
- if [ "${1:0:1}" != "/" ]; then
- jar=${run_base}/${1}
- fi
- # about to add the file to class path, check if it's valid
- if [ ! -f ${jar} ]; then
- echo "Error: ${jar} does not exist"
- # force to print help message
- cmd="help"
- break
- fi
- jars=${jars}:${jar}
- # done processing current arg, moving on
- shift
- done
- # look for --nohup: if found, consume it and trap SIG_HUP, otherwise just
- # append the arg to args
- while [ -n "${1}" ]; do
- if [ "${1}" = "--nohup" ]; then
- trap "" HUP
- shift
- else
- args="${args} ${1}"
- shift
- fi
- done
- else
- # if cmd is not 'runtest', just take the rest of the args
- args=${@}
- fi
- args="${cmd} ${args}"
- if [ -n "${jars}" ]; then
- args="${args} -e jars ${jars}"
- fi
- CLASSPATH=${CLASSPATH}:${jars}
- export CLASSPATH
- exec app_process ${base}/bin com.android.commands.uiautomator.Launcher ${args}
看到最后一句话是去执行了com.android.commands.uiautomator.Launcher这个类。该类位于uiautomator.jar包里。该jar包在framework中。
导出该jar包,查看里面的laucher类。
不幸的是,打开以后,就一个dex文件,想办法暴力破解,更不幸的是破解后的smali文件我依然看不懂。
- .class public Lcom/android/commands/uiautomator/Launcher;
- .super Ljava/lang/Object;
- .source "Launcher.java"
- # annotations
- .annotation system Ldalvik/annotation/MemberClasses;
- value = {
- Lcom/android/commands/uiautomator/Launcher$Command;
- }
- .end annotation
- # static fields
- .field private static COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;
- .field private static HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;
- # direct methods
- .method static constructor <clinit>()V
- .registers 3
- .prologue
- .line 99
- new-instance v0, Lcom/android/commands/uiautomator/Launcher$1;
- const-string v1, "help"
- invoke-direct {v0, v1}, Lcom/android/commands/uiautomator/Launcher$1;-><init>(Ljava/lang/String;)V
- sput-object v0, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;
- .line 129
- const/4 v0, 0x4
- new-array v0, v0, [Lcom/android/commands/uiautomator/Launcher$Command;
- const/4 v1, 0x0
- sget-object v2, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;
- aput-object v2, v0, v1
- const/4 v1, 0x1
- new-instance v2, Lcom/android/commands/uiautomator/RunTestCommand;
- invoke-direct {v2}, Lcom/android/commands/uiautomator/RunTestCommand;-><init>()V
- aput-object v2, v0, v1
- const/4 v1, 0x2
- new-instance v2, Lcom/android/commands/uiautomator/DumpCommand;
- invoke-direct {v2}, Lcom/android/commands/uiautomator/DumpCommand;-><init>()V
- aput-object v2, v0, v1
- const/4 v1, 0x3
- new-instance v2, Lcom/android/commands/uiautomator/EventsCommand;
- invoke-direct {v2}, Lcom/android/commands/uiautomator/EventsCommand;-><init>()V
- aput-object v2, v0, v1
- sput-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;
- return-void
- .end method
- .method public constructor <init>()V
- .registers 1
- .prologue
- .line 31
- invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- .line 36
- return-void
- .end method
- .method static synthetic access$000()[Lcom/android/commands/uiautomator/Launcher$Command;
- .registers 1
- .prologue
- .line 31
- sget-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;
- return-object v0
- .end method
- .method private static findCommand(Ljava/lang/String;)Lcom/android/commands/uiautomator/Launcher$Command;
- .registers 6
- .parameter "name"
- .prologue
- .line 91
- sget-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;
- .local v0, arr$:[Lcom/android/commands/uiautomator/Launcher$Command;
- array-length v3, v0
- .local v3, len$:I
- const/4 v2, 0x0
- .local v2, i$:I
- :goto_4
- if-ge v2, v3, :cond_16
- aget-object v1, v0, v2
- .line 92
- .local v1, command:Lcom/android/commands/uiautomator/Launcher$Command;
- invoke-virtual {v1}, Lcom/android/commands/uiautomator/Launcher$Command;->name()Ljava/lang/String;
- move-result-object v4
- invoke-virtual {v4, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
- move-result v4
- if-eqz v4, :cond_13
- .line 96
- .end local v1 #command:Lcom/android/commands/uiautomator/Launcher$Command;
- :goto_12
- return-object v1
- .line 91
- .restart local v1 #command:Lcom/android/commands/uiautomator/Launcher$Command;
- :cond_13
- add-int/lit8 v2, v2, 0x1
- goto :goto_4
- .line 96
- .end local v1 #command:Lcom/android/commands/uiautomator/Launcher$Command;
- :cond_16
- const/4 v1, 0x0
- goto :goto_12
- .end method
- .method public static main([Ljava/lang/String;)V
- .registers 6
- .parameter "args"
- .prologue
- const/4 v4, 0x0
- const/4 v3, 0x1
- .line 74
- const-string v2, "uiautomator"
- invoke-static {v2}, Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
- .line 75
- array-length v2, p0
- if-lt v2, v3, :cond_22
- .line 76
- aget-object v2, p0, v4
- invoke-static {v2}, Lcom/android/commands/uiautomator/Launcher;->findCommand(Ljava/lang/String;)Lcom/android/commands/uiautomator/Launcher$Command;
- move-result-object v1
- .line 77
- .local v1, command:Lcom/android/commands/uiautomator/Launcher$Command;
- if-eqz v1, :cond_22
- .line 78
- new-array v0, v4, [Ljava/lang/String;
- .line 79
- .local v0, args2:[Ljava/lang/String;
- array-length v2, p0
- if-le v2, v3, :cond_1e
- .line 81
- array-length v2, p0
- invoke-static {p0, v3, v2}, Ljava/util/Arrays;->copyOfRange([Ljava/lang/Object;II)[Ljava/lang/Object;
- move-result-object v0
- .end local v0 #args2:[Ljava/lang/String;
- check-cast v0, [Ljava/lang/String;
- .line 83
- .restart local v0 #args2:[Ljava/lang/String;
- :cond_1e
- invoke-virtual {v1, v0}, Lcom/android/commands/uiautomator/Launcher$Command;->run([Ljava/lang/String;)V
- .line 88
- .end local v0 #args2:[Ljava/lang/String;
- .end local v1 #command:Lcom/android/commands/uiautomator/Launcher$Command;
- :goto_21
- return-void
- .line 87
- :cond_22
- sget-object v2, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;
- invoke-virtual {v2, p0}, Lcom/android/commands/uiautomator/Launcher$Command;->run([Ljava/lang/String;)V
- goto :goto_21
- .end method
貌似做了代码混淆。只能去网上搜搜看啦!
还是在之前的源码网站上找到了。
- /*
- * Copyright (C) 2012 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 com.android.commands.uiautomator;
- import android.app.UiAutomation;
- import android.graphics.Point;
- import android.hardware.display.DisplayManagerGlobal;
- import android.os.Environment;
- import android.view.Display;
- import android.view.accessibility.AccessibilityNodeInfo;
- import com.android.commands.uiautomator.Launcher.Command;
- import com.android.uiautomator.core.AccessibilityNodeInfoDumper;
- import com.android.uiautomator.core.UiAutomationShellWrapper;
- import java.io.File;
- import java.util.concurrent.TimeoutException;
- /**
- * Implementation of the dump subcommand
- *
- * This creates an XML dump of current UI hierarchy
- */
- public class DumpCommand extends Command {
- private static final File DEFAULT_DUMP_FILE = new File(
- Environment.getLegacyExternalStorageDirectory(), "window_dump.xml");
- public DumpCommand() {
- super("dump");
- }
- @Override
- public String shortHelp() {
- return "creates an XML dump of current UI hierarchy";
- }
- @Override
- public String detailedOptions() {
- return " dump [--verbose][file]\n"
- + " [--compressed]: dumps compressed layout information.\n"
- + " [file]: the location where the dumped XML should be stored, default is\n "
- + DEFAULT_DUMP_FILE.getAbsolutePath() + "\n";
- }
- @Override
- public void run(String[] args) {
- File dumpFile = DEFAULT_DUMP_FILE;
- boolean verboseMode = true;
- for (String arg : args) {
- if (arg.equals("--compressed"))
- verboseMode = false;
- else if (!arg.startsWith("-")) {
- dumpFile = new File(arg);
- }
- }
- UiAutomationShellWrapper automationWrapper = new UiAutomationShellWrapper();
- automationWrapper.connect();
- if (verboseMode) {
- // default
- automationWrapper.setCompressedLayoutHierarchy(false);
- } else {
- automationWrapper.setCompressedLayoutHierarchy(true);
- }
- // It appears that the bridge needs time to be ready. Making calls to the
- // bridge immediately after connecting seems to cause exceptions. So let's also
- // do a wait for idle in case the app is busy.
- 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;
- }
- 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;
- } finally {
- automationWrapper.disconnect();
- }
- System.out.println(
- String.format("UI hierchary dumped to: %s", dumpFile.getAbsolutePath()));
- }
- }