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/
关键技术:
Java web start
适合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));