vysor原理與程式碼實現

rayworks發表於2018-12-25

看過 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操作生成子程式,並啟動這一新的應用程式。

vysor原理與程式碼實現
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設定的命令列工具

有關命令列工具的實現 cmd_runner.c

已實現自動化的任務包括 forward/unforward PC網路請求,通過管道 (pipe) 跨程式通訊得到已安裝APP的實際路徑,以及shell 環境下呼叫 app_process 啟動內部截圖服務等。

2. 原始碼

目前的原始碼已經放在github DroidCast,歡迎大家 star 和 fork,並與我交流。

3. 最近更新

  • 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. 參考引用

相關文章