vysor原理代碼實現

看過 vysor原理以及Android同屏方案 , 我突然想到整個過程應該如何驗證的問題。于是反編譯了vysor 最新的apk, 其中的代碼邏輯依然具有很強的借鑒意義。其中通過 shell 環境下調用 adb 獲取截屏權限成為了全篇的亮點所在。以下文字簡要地記錄了個人的理解過程,同時希望增進對Android Framework 的理解。

0. 背景介紹

關于App的創建

由于 Zygote 在系統啟動時冷啟動了一個Dalvik / ART VM, 并開啟對創建新APP請求的監聽。隨后所有新的應用進程都由Zygote 執行 fork 操作而創建的。具體流程如下所述:

Linux內核啟動后,就開始了初始化 Android 系統(init process)的過程。/system/bin/app_process 運行并啟動了 Android運行時(AndroidRuntime.start()),在這期間運行時啟動了Dalvik 虛擬機,并且創建了zygote進程,以及開啟com.android.server.SystemServer 系統服務進程。Zygote將在有新的應用啟動時被激活,為了加速應用啟動的過程,Zygote會預加載公用的Java類和資源到RAM中,以供應用在實際運行時使用。最終,Zygote將fork自己并啟動這一新的應用進程。

Android Boot Sequence (from Embedded Android)

Java 應用與 Android app的差異

典型 Android 應用模塊的構建流程

從以上Android APP的編譯流程上,我們也不難看出:由于Android 平臺使用了一個不同于一般 JVM 的虛擬機,這就使得Java class 文件需要額外的處理(即 dex化)之后才能運行。

作為一個"推進器",上述 app_process 除了啟動 Zygote進程外,還可以創建其它進程。有興趣的讀者可以進一步參考鏈接中的 Run a Java main on Android 部分, 在命令行中實際編譯Java代碼,dex 處理以及通過 adb shell 命令打印出 Android 平臺上的"Hello World"。

1. 實現

先上一個截取屏幕并在瀏覽器中顯示的效果圖:


Screen Shot.png

1.0 與截屏的相關API

在OS 4.3 之前有標注為(@hide)的API android.view.Surface.screenshot (); 而4.3之后API變為 android.view.SurfaceControl.screenshot(). 非root的設備上,一般的APP是沒有權限調用以上的接口的。而在shell 環境下確實具備權限的,而這正一點好成為了一個突破口。

1.1 代碼入口方法

Java 類Main的靜態方法 main() 中簡單實現了一個 HandlerThread (可類比源碼中ActivityThread.main())。在looper 正在開始處理消息前,啟動本地的server, 并設置對screenshot GET請求的回調方法。回調處理過程使用上述的 screenshot() 方法進行屏幕截圖,并設置Bitmap數據為對應的 HTTP response。最后設置 adb forward tcp:53516 tcp:53516 將PC上所有 53516 端口通信數據重定向到手機端 53516 端口server上。

1.2 調用隱藏的API

在App內部,通常在有 Context 的情況下我們可以很方便地獲取系統服務:

WindowManager window = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

而此時的入口 Main 是由 app_process 啟動的一個獨立進程。于是問題就出現了,如何獲取當前屏幕的寬和高呢?想想框架中的 Java 部分代碼是如何進行進程間通信的,常見的 AIDL 成為了一個較好的方案。同樣利用框架提供的WINDOW_SERVICE, 我們可以將系統源碼中的 IWindowManager.aidl 拷貝到工程目錄中,利用對應生成的 local stub 通過編譯,運行時通過反射調用對應所需的服務。

1.3 自動化ADB設置的命令行工具

類Unix系統上的工具 [cmd_runner.c] (https://github.com/rayworks/DroidCast/blob/master/cmd_tool/cmd_runner.c#L284).

為達到跨平臺功能,最近更新的 python 腳本 (Python 2.7.15)
已實現了自動化啟用截圖功能,其中包括 adb forward/unforward PC網絡請求,通過管道 (pipe) 跨進程通信得到已安裝APP的實際路徑,以及 shell 環境下調用 app_process 啟動內部截圖服務等。

對于已連接的單臺設備/模擬器,默認調用:

python scripts/automation.py

即可在自動打開的默認瀏覽器中查看已連接設備的瞬時截圖。

2. 源碼

目前代碼已經放在github DroidCast,歡迎大家 star 和 fork,并與我交流。

3. 最近更新

  • 2019-12-9 使用 ip 命令替換ifconfig獲取設備IP地址,避免執行時的權限問題

  • 2019-11-5 獨立腳本支持Python 3.6+

  • 2019-9-11 遷移到 AndroidX

  • 2019-8-22 利用Python 2to3 轉換工具,支持Python 3.6+

  • 2019-5-30 Python 腳本中支持在有多個連接的設備時,對選定設備進行操作。

  • 2019-5-18 用 Python 腳本自動化實現 adb 命令相關的設定和自動重置,以及打開默認瀏覽器查看截圖

  • 2019-5-7 增加對 websocket 的支持,在屏幕旋轉后 web 頁面自動刷新顯示新截圖

  • 2019-4-14 支持不同格式的image請求(png, jpeg, webp)以及屏幕旋轉后的截圖

  • 2019-2-28 適配 Android Pie

  • 2019-2-16 更新命令行工具,實現自動打開默認瀏覽器查看截圖

  • 2018-11-7 支持通過指定的大小截屏并顯示圖片

  • 2018-10-30 增加adb設置說明,支持(相同網段WIFI環境下)無線使用場景

  • 2018-9-5 更新了命令行工具,使其能定位安裝到設備上的 apk 位置,解決 OS 4.3及以下的設備上出現的無法找到 class 導致的 crash。

  • 2018-4-5 增加 *nix 環境下 command line tool (C 程序) 簡化對 adb 命令相關的設定和自動重置

  • 2018-3-28 解決OS 8.0 下 加載 base.apk 失敗的問題

  • You can no longer assume that APKs reside in directories whose names end in -1 or -2. Apps should use sourceDir to get the directory, and not rely on the directory format directly.

4. 參考引用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,841評論 25 708
  • Android Studio JNI流程首先在java代碼聲明本地方法 用到native關鍵字 本地方法不用去實現...
    MigrationUK閱讀 11,932評論 7 123
  • 1:InputChannel提供函數創建底層的Pipe對象 2: 1)客戶端需要新建窗口 2)new ViewRo...
    自由人是工程師閱讀 5,368評論 0 18
  • 我不想象牲口一樣活下去時,人己到中年。華服、美食、燈紅酒綠盡管日日在城市上演喜劇,內心深處向往的依然是一片空遠遼...
    綠木木子閱讀 330評論 0 1
  • 1. 我夢中遇見你 你叫等等 愛涂綠色的面膜 喜歡和我一起吃火鍋 我上班遲到了 因為夢境中的路和現實中的不一樣 夢...
    小紅帽Jane閱讀 713評論 0 3