uiautomator获取不到动态界面的缘由

这几天查看了下源码发现,uiautomatorviewer在获取界面布局信息的时候用的是启动一个脚本,该脚本在/system/bin/uiautomator。这个命令也可以在命令行下启动。



uiautomator获取不到动态界面的缘由_第1张图片


默认情况下,获取的控件信息保存在/storage/emulated/legacy/window_dump.xml文件中,你也可以改变它保存的目录,例如保存在data/local/tmp下




uiautomator获取不到动态界面的缘由_第2张图片


这是正常情况下的,但我进入秒表界面,将秒表开启。然后执行上面的命令:




报了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中。


uiautomator获取不到动态界面的缘由_第3张图片


导出该jar包,查看里面的laucher类。


uiautomator获取不到动态界面的缘由_第4张图片


不幸的是,打开以后,就一个dex文件,想办法暴力破解,更不幸的是破解后的smali文件我依然看不懂。


uiautomator获取不到动态界面的缘由_第5张图片


.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

你可能感兴趣的:(android,UiAutomator)