这几天查看了下源码发现,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())); } }
源码发现确实是这样的。如何解决了。。。。。。fighting