在源码下写程序可以摆脱SDK的限制,毕竟SDK开放的API有限,比如我们实现模拟按键时,需要用到IWindowManager这个类,但是SDK中是不提供这个类的。
首先下载编译源码,然后在源码的frameworks/base/cmds下新建一个文件夹作为你新扩展模块的一个目录。比如叫做autotest,在autotest下创建一个java文件,比如AutoTest.java。编写你的程序代码,在此你可以使用IWindowManager类,在此,我模拟了按键key,长按键keypress,点触笔touch,点触笔长按touchpress,以及移动move等,代码如下:
import android.view.MotionEvent;
import android.view.KeyEvent;
import android.view.IWindowManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.RemoteException;
import android.util.Log;
public class AutoTest
{
public static void main(String args[])throws Exception
{
String[] mArgs = args;
try
{
String opt = mArgs[0];
if(opt.equals("touch"))
{
float x = Float.valueOf(mArgs[1]);
float y = Float.valueOf(mArgs[2]);
MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
sendPointerSync(e);
}
else if(opt.equals("move"))
{
float x = Float.valueOf(mArgs[1]);
float y = Float.valueOf(mArgs[2]);
float x2 = Float.valueOf(mArgs[3]);
float y2 = Float.valueOf(mArgs[4]);
MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x2, y2, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x2, y2, 0);
sendPointerSync(e);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x2, y2, 0);
sendPointerSync(e);
}
else if(opt.equals("key"))
{
int keycode = Integer.valueOf(mArgs[1]);
KeyEvent k = new KeyEvent(KeyEvent.ACTION_DOWN,keycode);
sendKeySync(k);
k = new KeyEvent(KeyEvent.ACTION_UP,keycode);
sendKeySync(k);
}
else if(opt.equals("wait"))
{
int millsecond = Integer.valueOf(mArgs[1]);
Thread.sleep(millsecond);
}
else if(opt.equals("keypress"))
{
int keycode = Integer.valueOf(mArgs[1]);
int millsecond = Integer.valueOf(mArgs[2]);
KeyEvent k = new KeyEvent(KeyEvent.ACTION_DOWN,keycode);
sendKeySync(k);
Thread.sleep(millsecond);
k = new KeyEvent(KeyEvent.ACTION_UP,keycode);
sendKeySync(k);
}
else if(opt.equals("touchpress"))
{
float x = Float.valueOf(mArgs[1]);
float y = Float.valueOf(mArgs[2]);
int millsecond = Integer.valueOf(mArgs[3]);
MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
sendPointerSync(e);
Thread.sleep(millsecond);
e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
sendPointerSync(e);
}
else
System.err.println("** Error: Unknown option: " + opt);
}
catch (RuntimeException ex){}
Thread.sleep(2000);
}
private static void sendPointerSync(MotionEvent event)
{
try
{
(IWindowManager.Stub.asInterface(ServiceManager.getService("window"))).injectPointerEvent(event, true);
}
catch (RemoteException e) {}
}
private static void sendKeySync(KeyEvent event)
{
try
{
(IWindowManager.Stub.asInterface(ServiceManager.getService("window"))).injectKeyEvent(event, true);
}
catch (RemoteException e) {}
}
}
在与java文件同级创建Android.mk文件,内容如下:
# Copyright 2008 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
AutoTest.java
LOCAL_MODULE := autotest
LOCAL_MODULE_TAGS := eng
include $(BUILD_JAVA_LIBRARY)
//**********************************Android.mk 文件详解
1、LOCAL_PATH:一个Android.mk 首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk文件的目录)。
2、CLEAR_VARS 由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
3、LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'HcSyncml'的共享库模块,将会生成'libHcSyncml.so'文件。
4、LOCAL_C_INCLUDES := $(LOCAL_PATH)/extra_inc$(LOCAL_PATH)/main_inc4、
LOCAL_C_INCLUDES 中加入所需要包含的头文件路径【针对C文件来说的】
5、 LOCAL_SRC_FILES: LOCAL_SRC_FILES中加入源文件路径(需要编译的文件),多个文件用 ‘\’ 隔开//**************************************
在终端命令行下进入autotest文件夹,输入mm命令,如果报错,则返回Android源码主目录,输入如下命令:
. build/envsetup.sh此时再返回你的工程目录输入mm就可以了,编译生成一个.jar文件,位于源码的/out/target/product/generic/system/framework下
将编译好的.jar文件及.odex文件 放在设备的/system/framework下,新建一个文件,名称为autotest,内容如下:
# Script to start "monkey" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/autotest.jar
exec app_process $base/bin AutoTest $*
将autotest文件放在/system/bin下,用chmod修改文件属性(777)这样你可以在shell下调用你的.jar文件了
比如在shell中输入: autotest key 24,向系统注入了调节音量的按键事件。
===========================================================
以上过程中出现的问题解析:
一、/system没有空间:
1、启动模拟器时,用命令 android sdk tools目录下 emulator -avd my_avd -partition-size 512 启动后 system目录的空间就变为了512M
2、获得root权限 adb root
3、设置/system为可读写 adb remount //解决可读写的问题
二、启动emulator时 ,有如下 错误提示
ERROR: You did not specify a virtual device name, and the system
directory could not be found.
If you are an Android SDK user, please use '@<name>' or '-avd <name>'
to start a given virtual device (see -help-avd for details).
Otherwise, follow the instructions in -help-disk-images to start the emulator
原因是你没有建立avd,建立方法如下:
1、终端中输入 ./android 执行该脚本,启动 Android SDK Manager -> Tools -> Manage AVDs -> 打开 Android Virtual Device Manager 后,新建一个avd,然后再执行emulator -avd my_avd -partition-size 512 便可正常。
三、在tools目录下,用adb push 把相应的文件push到指定目录下,便可在emulator下的shell中执行autotest key 24命令,把指定的键值推送给android系统了。
adb push /home/yinjk/Android/Android-source-code/djasdjaskdj/autotest /system/bin/