Update: 最近觀看了ELC2011的關於android的一個視頻, 裏面提到了Android System Test Environment Runtime這麼一個可以在PC端控制並執行一些定製的script測試腳本的測試框架, 其中PC與Device之間的UI同步用的就是screencast類似的機制--不停的傳遞screenshot, 而不是hack fbdev driver來傳遞實時的framebuffer中的內容。我另外會總結一篇對於ASTER的文章,希望和大家一起學習下.
Screencast是一個C/S的實用小程序, 用來通過desktop pc控制android device. 實現的相當精巧.
項目的地址: http://code.google.com/p/androidscreencast/
關鍵技術:
適合dalvik的可執行jar包的生成
利用ADB實現PC與device的通信
通過app_process直接啓動一個可執行的android jar包,而不需要安裝它
我們先來看看它的client的實現:
1. 把AndroidScreencastClient導入eclipse, 查看對應.classpath, 修改對應的3個jar包指向你的環境中的對應位置
2. 使用提供的MyInjectEventApp.jardesc,生成Server端需要使用的MyInjectEventApp.jar
3. 按照MyInjectEventApp的描述, 生成包含classes.dex的jar包,這個包之後才能在android device被執行!!
!!這一步很重要,像我們展示了,如何使用普通的java工程, 生成普通的jar包(但是,這個包裏包含有android的相關類), 再利用android提供的dx, aapt生成能在android device上執行的可執行jar包!!
以上編譯準備工作做好,就可以看它的實現了
Main.java -- 很簡單的一個入口點, 從PC測接收一個端口號, 以此新建一個socket通信, 並新起一個線程來使用該socket.
ClientHandler.java -- 關鍵做事的類, 利用常用的hook android framework的方法, 使用ServiceManage獲得"window" service的Binder, 然後調用它所對應的WindowManager的內部接口來進行,point/key/trackball事件的觸發
IBinder wmbinder = ServiceManager.getService( "window" ); final IWindowManager wm = IWindowManager.Stub.asInterface( wmbinder );
injectPointerEvent/injectKeyEvent/injectTrackballEvent就是WindowsManager中的內部接口, 對應的代碼在frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
如何讀取設備上的framebufferProcess p = Runtime.getRuntime().exec("/system/bin/cat /dev/graphics/fb0"); InputStream is = p.getInputStream(); System.out.println("Starting sending framebuffer"); OutputStream os = s.getOutputStream();
再來看看Server(即desktop PC)的實現:
使用swt構建了PC側的UI.
使用了非公開的DDMS提供的ADB接口完成通信
AndroidDebugBridge bridge = AndroidDebugBridge.createBridge(); waitDeviceList(bridge); IDevice devices[] = bridge.getDevices();
使用了ADB daemon提供的接口來獲得各種adb service提供的功能, 比如獲得framebuffer的內容,從而顯示到PC端. (adb daemon提供的service文檔, 參見system/core/adb/SERVICES.TXT):具體的通信還是由傳統的socket通信來完成, 沒要求一次screenshot, 就會發一次獲得當前framebuffer的請求.
RawImage rawImage = null; synchronized (device) { rawImage = device.getScreenshot(); }
通過反射,使用ddms提供的syncmanager的接口, 從而提供了DDMS提供的pull/push文件的功能
Method m = device.getSyncService().getClass().getDeclaredMethod( "doPullFile", String.class, String.class, ISyncProgressMonitor.class);
!!通過使用app_process來執行一個android的可執行jar包!!
String fullCmd = "export CLASSPATH=" + REMOTE_AGENT_JAR_LOCATION; fullCmd += "; exec app_process /system/bin " + AGENT_MAIN_CLASS + " " + cmdList; System.out.println(fullCmd); device.executeShellCommand(fullCmd, new OutputStreamShellOutputReceiver(System.out));