uiautomator獲取不到動態界面的緣由

這幾天查看了下源碼發現,uiautomatorviewer在獲取界面佈局信息的時候用的是啓動一個腳本,該腳本在/system/bin/uiautomator。這個命令也可以在命令行下啓動。





默認情況下,獲取的控件信息保存在/storage/emulated/legacy/window_dump.xml文件中,你也可以改變它保存的目錄,例如保存在data/local/tmp下






這是正常情況下的,但我進入秒錶界面,將秒錶開啓。然後執行上面的命令:




報了could not get idle state的錯。


說明uiautomator在獲取界面狀態信息時,首先要等界面處於idle空閒狀態纔會做dump操作。這就是uiautomator死活拿不到動態界面信息的原因。


調出uiautomator這個腳本。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. #  
  2. # Copyright (C) 2012 The Android Open Source Project  
  3. #  
  4. # Licensed under the Apache License, Version 2.0 (the "License");  
  5. # you may not use this file except in compliance with the License.  
  6. # You may obtain a copy of the License at  
  7. #  
  8. #      http://www.apache.org/licenses/LICENSE-2.0  
  9. #  
  10. # Unless required by applicable law or agreed to in writing, software  
  11. # distributed under the License is distributed on an "AS IS" BASIS,  
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13. # See the License for the specific language governing permissions and  
  14. # limitations under the License.  
  15. #  
  16. # Script to start "uiautomator" on the device  
  17. #  
  18. # The script does a couple of things:  
  19. # * Use an alternative dalvik cache when running as non-root. Jar file needs  
  20. #   to be dexopt'd to run in Dalvik. For plain jar files, this is done at first  
  21. #   use. shell user does not have write permission to default system Dalvik  
  22. #   cache so we redirect to an alternative cache  
  23. # * special processing for subcommand 'runtest':  
  24. #    * '--nohup' allows process continue to run even if parent process that  
  25. #      started it has already terminated. We parse for this parameter and set  
  26. #      signal trap. This is useful for testing with USB disconnected  
  27. #    * all jar files that the test classes resides in, or dependent on are  
  28. #      provided on command line and exported to CLASSPATH environment variable  
  29. #      before starting the Java code. This offloads the task of class loading  
  30. #      and resolving of cross jar class dependency to Dalvik  
  31. #    * all other subcommand or options are directly passed into Java code for  
  32. #      further parsing  
  33.   
  34. export run_base=/data/local/tmp  
  35. export base=/system  
  36.   
  37. if not running as root, trick dalvik into using an alternative dex cache  
  38. if [ ${USER_ID} -ne 0 ]; then  
  39.   tmp_cache=${run_base}/dalvik-cache  
  40.   
  41.   if [ ! -d ${tmp_cache} ]; then  
  42.     mkdir -p ${tmp_cache}  
  43.   fi  
  44.   
  45.   export ANDROID_DATA=${run_base}  
  46. fi  
  47.   
  48. # take first parameter as the command  
  49. cmd=${1}  
  50.   
  51. if [ -z "${1}" ]; then  
  52.   cmd="help"  
  53. fi  
  54.   
  55. # strip the command parameter  
  56. if [ -n "${1}" ]; then  
  57.   shift  
  58. fi  
  59.   
  60. CLASSPATH=/system/framework/android.test.runner.jar:${base}/framework/uiautomator.jar  
  61.   
  62. # eventually args will be what get passed down to Java code  
  63. args=  
  64. # we also pass the list of jar files, so we can extract class names for tests  
  65. if they are not explicitly specified  
  66. jars=  
  67.   
  68. # special case pre-processing for 'runtest' command  
  69. if [ "${cmd}" == "runtest" ]; then  
  70.   # first parse the jar paths  
  71.   while [ true ]; do  
  72.     if [ -z "${1}" ] && [ -z "${jars}" ]; then  
  73.       echo "Error: more parameters expected for runtest; please see usage for details"  
  74.       cmd="help"  
  75.       break  
  76.     fi  
  77.     if [ -z "${1}" ]; then  
  78.       break  
  79.     fi  
  80.     jar=${1}  
  81.     if [ "${1:0:1}" = "-" ]; then  
  82.       # we are done with jars, starting with parameters now  
  83.       break  
  84.     fi  
  85.     # if relative path, append the default path prefix  
  86.     if [ "${1:0:1}" != "/" ]; then  
  87.       jar=${run_base}/${1}  
  88.     fi  
  89.     # about to add the file to class path, check if it's valid  
  90.     if [ ! -f ${jar} ]; then  
  91.       echo "Error: ${jar} does not exist"  
  92.       # force to print help message  
  93.       cmd="help"  
  94.       break  
  95.     fi  
  96.     jars=${jars}:${jar}  
  97.     # done processing current arg, moving on  
  98.     shift  
  99.   done  
  100.   # look for --nohup: if found, consume it and trap SIG_HUP, otherwise just  
  101.   # append the arg to args  
  102.   while [ -n "${1}" ]; do  
  103.     if [ "${1}" = "--nohup" ]; then  
  104.       trap "" HUP  
  105.       shift  
  106.     else  
  107.       args="${args} ${1}"  
  108.       shift  
  109.     fi  
  110.   done  
  111. else  
  112.   # if cmd is not 'runtest', just take the rest of the args  
  113.   args=${@}  
  114. fi  
  115.   
  116. args="${cmd} ${args}"  
  117. if [ -n "${jars}" ]; then  
  118.    args="${args} -e jars ${jars}"  
  119. fi  
  120.   
  121. CLASSPATH=${CLASSPATH}:${jars}  
  122. export CLASSPATH  
  123. exec app_process ${base}/bin com.android.commands.uiautomator.Launcher ${args}  

看到最後一句話是去執行了com.android.commands.uiautomator.Launcher這個類。該類位於uiautomator.jar包裏。該jar包在framework中。



導出該jar包,查看裏面的laucher類。



不幸的是,打開以後,就一個dex文件,想辦法暴力破解,更不幸的是破解後的smali文件我依然看不懂。




[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. .class public Lcom/android/commands/uiautomator/Launcher;  
  2. .super Ljava/lang/Object;  
  3. .source "Launcher.java"  
  4.   
  5.   
  6. # annotations  
  7. .annotation system Ldalvik/annotation/MemberClasses;  
  8.     value = {  
  9.         Lcom/android/commands/uiautomator/Launcher$Command;  
  10.     }  
  11. .end annotation  
  12.   
  13.   
  14. static fields  
  15. .field private static COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;  
  16.   
  17. .field private static HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;  
  18.   
  19.   
  20. # direct methods  
  21. .method static constructor <clinit>()V  
  22.     .registers 3  
  23.   
  24.     .prologue  
  25.     .line 99  
  26.     new-instance v0, Lcom/android/commands/uiautomator/Launcher$1;  
  27.   
  28.     const-string v1, "help"  
  29.   
  30.     invoke-direct {v0, v1}, Lcom/android/commands/uiautomator/Launcher$1;-><init>(Ljava/lang/String;)V  
  31.   
  32.     sput-object v0, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;  
  33.   
  34.     .line 129  
  35.     const/4 v0, 0x4  
  36.   
  37.     new-array v0, v0, [Lcom/android/commands/uiautomator/Launcher$Command;  
  38.   
  39.     const/4 v1, 0x0  
  40.   
  41.     sget-object v2, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;  
  42.   
  43.     aput-object v2, v0, v1  
  44.   
  45.     const/4 v1, 0x1  
  46.   
  47.     new-instance v2, Lcom/android/commands/uiautomator/RunTestCommand;  
  48.   
  49.     invoke-direct {v2}, Lcom/android/commands/uiautomator/RunTestCommand;-><init>()V  
  50.   
  51.     aput-object v2, v0, v1  
  52.   
  53.     const/4 v1, 0x2  
  54.   
  55.     new-instance v2, Lcom/android/commands/uiautomator/DumpCommand;  
  56.   
  57.     invoke-direct {v2}, Lcom/android/commands/uiautomator/DumpCommand;-><init>()V  
  58.   
  59.     aput-object v2, v0, v1  
  60.   
  61.     const/4 v1, 0x3  
  62.   
  63.     new-instance v2, Lcom/android/commands/uiautomator/EventsCommand;  
  64.   
  65.     invoke-direct {v2}, Lcom/android/commands/uiautomator/EventsCommand;-><init>()V  
  66.   
  67.     aput-object v2, v0, v1  
  68.   
  69.     sput-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;  
  70.   
  71.     return-void  
  72. .end method  
  73.   
  74. .method public constructor <init>()V  
  75.     .registers 1  
  76.   
  77.     .prologue  
  78.     .line 31  
  79.     invoke-direct {p0}, Ljava/lang/Object;-><init>()V  
  80.   
  81.     .line 36  
  82.     return-void  
  83. .end method  
  84.   
  85. .method static synthetic access$000()[Lcom/android/commands/uiautomator/Launcher$Command;  
  86.     .registers 1  
  87.   
  88.     .prologue  
  89.     .line 31  
  90.     sget-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;  
  91.   
  92.     return-object v0  
  93. .end method  
  94.   
  95. .method private static findCommand(Ljava/lang/String;)Lcom/android/commands/uiautomator/Launcher$Command;  
  96.     .registers 6  
  97.     .parameter "name"  
  98.   
  99.     .prologue  
  100.     .line 91  
  101.     sget-object v0, Lcom/android/commands/uiautomator/Launcher;->COMMANDS:[Lcom/android/commands/uiautomator/Launcher$Command;  
  102.   
  103.     .local v0, arr$:[Lcom/android/commands/uiautomator/Launcher$Command;  
  104.     array-length v3, v0  
  105.   
  106.     .local v3, len$:I  
  107.     const/4 v2, 0x0  
  108.   
  109.     .local v2, i$:I  
  110.     :goto_4  
  111.     if-ge v2, v3, :cond_16  
  112.   
  113.     aget-object v1, v0, v2  
  114.   
  115.     .line 92  
  116.     .local v1, command:Lcom/android/commands/uiautomator/Launcher$Command;  
  117.     invoke-virtual {v1}, Lcom/android/commands/uiautomator/Launcher$Command;->name()Ljava/lang/String;  
  118.   
  119.     move-result-object v4  
  120.   
  121.     invoke-virtual {v4, p0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z  
  122.   
  123.     move-result v4  
  124.   
  125.     if-eqz v4, :cond_13  
  126.   
  127.     .line 96  
  128.     .end local v1           #command:Lcom/android/commands/uiautomator/Launcher$Command;  
  129.     :goto_12  
  130.     return-object v1  
  131.   
  132.     .line 91  
  133.     .restart local v1       #command:Lcom/android/commands/uiautomator/Launcher$Command;  
  134.     :cond_13  
  135.     add-int/lit8 v2, v2, 0x1  
  136.   
  137.     goto :goto_4  
  138.   
  139.     .line 96  
  140.     .end local v1           #command:Lcom/android/commands/uiautomator/Launcher$Command;  
  141.     :cond_16  
  142.     const/4 v1, 0x0  
  143.   
  144.     goto :goto_12  
  145. .end method  
  146.   
  147. .method public static main([Ljava/lang/String;)V  
  148.     .registers 6  
  149.     .parameter "args"  
  150.   
  151.     .prologue  
  152.     const/4 v4, 0x0  
  153.   
  154.     const/4 v3, 0x1  
  155.   
  156.     .line 74  
  157.     const-string v2, "uiautomator"  
  158.   
  159.     invoke-static {v2}, Landroid/os/Process;->setArgV0(Ljava/lang/String;)V  
  160.   
  161.     .line 75  
  162.     array-length v2, p0  
  163.   
  164.     if-lt v2, v3, :cond_22  
  165.   
  166.     .line 76  
  167.     aget-object v2, p0, v4  
  168.   
  169.     invoke-static {v2}, Lcom/android/commands/uiautomator/Launcher;->findCommand(Ljava/lang/String;)Lcom/android/commands/uiautomator/Launcher$Command;  
  170.   
  171.     move-result-object v1  
  172.   
  173.     .line 77  
  174.     .local v1, command:Lcom/android/commands/uiautomator/Launcher$Command;  
  175.     if-eqz v1, :cond_22  
  176.   
  177.     .line 78  
  178.     new-array v0, v4, [Ljava/lang/String;  
  179.   
  180.     .line 79  
  181.     .local v0, args2:[Ljava/lang/String;  
  182.     array-length v2, p0  
  183.   
  184.     if-le v2, v3, :cond_1e  
  185.   
  186.     .line 81  
  187.     array-length v2, p0  
  188.   
  189.     invoke-static {p0, v3, v2}, Ljava/util/Arrays;->copyOfRange([Ljava/lang/Object;II)[Ljava/lang/Object;  
  190.   
  191.     move-result-object v0  
  192.   
  193.     .end local v0           #args2:[Ljava/lang/String;  
  194.     check-cast v0, [Ljava/lang/String;  
  195.   
  196.     .line 83  
  197.     .restart local v0       #args2:[Ljava/lang/String;  
  198.     :cond_1e  
  199.     invoke-virtual {v1, v0}, Lcom/android/commands/uiautomator/Launcher$Command;->run([Ljava/lang/String;)V  
  200.   
  201.     .line 88  
  202.     .end local v0           #args2:[Ljava/lang/String;  
  203.     .end local v1           #command:Lcom/android/commands/uiautomator/Launcher$Command;  
  204.     :goto_21  
  205.     return-void  
  206.   
  207.     .line 87  
  208.     :cond_22  
  209.     sget-object v2, Lcom/android/commands/uiautomator/Launcher;->HELP_COMMAND:Lcom/android/commands/uiautomator/Launcher$Command;  
  210.   
  211.     invoke-virtual {v2, p0}, Lcom/android/commands/uiautomator/Launcher$Command;->run([Ljava/lang/String;)V  
  212.   
  213.     goto :goto_21  
  214. .end method  

貌似做了代碼混淆。只能去網上搜搜看啦!


還是在之前的源碼網站上找到了。


[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /* 
  2.  * Copyright (C) 2012 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.commands.uiautomator;  
  18.   
  19. import android.app.UiAutomation;  
  20. import android.graphics.Point;  
  21. import android.hardware.display.DisplayManagerGlobal;  
  22. import android.os.Environment;  
  23. import android.view.Display;  
  24. import android.view.accessibility.AccessibilityNodeInfo;  
  25.   
  26. import com.android.commands.uiautomator.Launcher.Command;  
  27. import com.android.uiautomator.core.AccessibilityNodeInfoDumper;  
  28. import com.android.uiautomator.core.UiAutomationShellWrapper;  
  29.   
  30. import java.io.File;  
  31. import java.util.concurrent.TimeoutException;  
  32.   
  33. /** 
  34.  * Implementation of the dump subcommand 
  35.  * 
  36.  * This creates an XML dump of current UI hierarchy 
  37.  */  
  38. public class DumpCommand extends Command {  
  39.   
  40.     private static final File DEFAULT_DUMP_FILE = new File(  
  41.             Environment.getLegacyExternalStorageDirectory(), "window_dump.xml");  
  42.   
  43.     public DumpCommand() {  
  44.         super("dump");  
  45.     }  
  46.   
  47.     @Override  
  48.     public String shortHelp() {  
  49.         return "creates an XML dump of current UI hierarchy";  
  50.     }  
  51.   
  52.     @Override  
  53.     public String detailedOptions() {  
  54.         return "    dump [--verbose][file]\n"  
  55.             + "      [--compressed]: dumps compressed layout information.\n"  
  56.             + "      [file]: the location where the dumped XML should be stored, default is\n      "  
  57.             + DEFAULT_DUMP_FILE.getAbsolutePath() + "\n";  
  58.     }  
  59.   
  60.     @Override  
  61.     public void run(String[] args) {  
  62.         File dumpFile = DEFAULT_DUMP_FILE;  
  63.         boolean verboseMode = true;  
  64.   
  65.         for (String arg : args) {  
  66.             if (arg.equals("--compressed"))  
  67.                 verboseMode = false;  
  68.             else if (!arg.startsWith("-")) {  
  69.                 dumpFile = new File(arg);  
  70.             }  
  71.         }  
  72.   
  73.         UiAutomationShellWrapper automationWrapper = new UiAutomationShellWrapper();  
  74.         automationWrapper.connect();  
  75.         if (verboseMode) {  
  76.             // default  
  77.             automationWrapper.setCompressedLayoutHierarchy(false);  
  78.         } else {  
  79.             automationWrapper.setCompressedLayoutHierarchy(true);  
  80.         }  
  81.   
  82.         // It appears that the bridge needs time to be ready. Making calls to the  
  83.         // bridge immediately after connecting seems to cause exceptions. So let's also  
  84.         // do a wait for idle in case the app is busy.  
  85.         try {  
  86.             UiAutomation uiAutomation = automationWrapper.getUiAutomation();  
  87.             uiAutomation.waitForIdle(10001000 * 10);  
  88.             AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();  
  89.             if (info == null) {  
  90.                 System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");  
  91.                 return;  
  92.             }  
  93.   
  94.             Display display =  
  95.                     DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);  
  96.             int rotation = display.getRotation();  
  97.             Point size = new Point();  
  98.             display.getSize(size);  
  99.             AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y);  
  100.         } catch (TimeoutException re) {  
  101.             System.err.println("ERROR: could not get idle state.");  
  102.             return;  
  103.         } finally {  
  104.             automationWrapper.disconnect();  
  105.         }  
  106.         System.out.println(  
  107.                 String.format("UI hierchary dumped to: %s", dumpFile.getAbsolutePath()));  
  108.     }  
  109. }  

發佈了133 篇原創文章 · 獲贊 17 · 訪問量 116萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章