这篇博客主要介绍使用 InvocationHandler 这个接口来达到 hook 系统 service ,从而实现一些颇有意思特殊功能的详细步骤。
转载请注明出处:blog.csdn.net/self_study/…
对技术感兴趣的同鞋加群 544645972 一块儿交流。
javascript
首先咱们要介绍的就是 Java 动态代理,Java 的动态代理涉及到两个类:InvocationHandler 接口和 Proxy 类,下面咱们会着重介绍一下这两个类,而且结合实例来着重分析一下使用的正确姿式等。在这以前简单介绍一下 Java 中 class 文件的生成和加载过程,Java 编译器编译好 Java 文件以后会在磁盘中产生 .class 文件。这种 .class 文件是二进制文件,内容是只有 JVM 虚拟机才能识别的机器码,JVM 虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析 .class 文件内的信息,使用相对应的 ClassLoader 类加载器生成对应的 Class 对象:
html
咱们来分析一下动态代理模式中 ProxySubject 的生成步骤:java
Subject.javaandroid
public interface Subject {
String operation();
}复制代码
RealSubject.javagit
public class RealSubject implements Subject{
@Override
public String operation() {
return "operation by subject";
}
}复制代码
ProxySubject.javagithub
public class ProxySubject implements InvocationHandler{
protected Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//do something before
return method.invoke(subject, args);
}
}复制代码
测试代码设计模式
Subject subject = new RealSubject();
ProxySubject proxy = new ProxySubject(subject);
Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), proxy);
sub.operation();复制代码
以上就是动态代理模式的最简单实现代码,JDK 经过使用 java.lang.reflect.Proxy 包来支持动态代理,咱们来看看这个类的表述:app
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the
superclass of all dynamic proxy classes created by those methods.复制代码
通常状况下,咱们使用下面的 newProxyInstance) 方法来生成动态代理:
框架
Public methods | |
---|---|
static Object | newProxyInstance(ClassLoader loader, Class[]<?> interfaces, InvocationHandler h) Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler. |
对应的参数为:ide
Parameters | |
---|---|
loader | ClassLoader: the class loader to define the proxy class |
interfaces | Class: the list of interfaces for the proxy class to implement |
h | InvocationHandler: the invocation handler to dispatch method invocations to |
咱们来仔细看看这个函数的实现:
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
// 检查 h 不为空,不然抛异常
if (h == null) {
throw new NullPointerException();
}
// 得到与指定类装载器和一组接口相关的代理类类型对象
Class cl = getProxyClass(loader, interfaces);
// 经过反射获取构造函数对象并生成代理类实例
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) { throw new InternalError(e.toString());
} catch (IllegalAccessException e) { throw new InternalError(e.toString());
} catch (InstantiationException e) { throw new InternalError(e.toString());
} catch (InvocationTargetException e) { throw new InternalError(e.toString());
}
}复制代码
Proxy 类的 getProxyClass 方法调用了 ProxyGenerator 的 generatorProxyClass 方法去生成动态类:
public static byte[] generateProxyClass(final String name, Class[] interfaces)复制代码
这个方法咱们下面将会介绍到,这里先略过,生成这个动态类的字节码以后,经过反射去生成这个动态类的对象,经过 Proxy 类的这个静态函数生成了一个动态代理对象 sub 以后,调用 sub 代理对象的每个方法,在代码内部,都是直接调用了 InvocationHandler 的 invoke 方法,而 invoke 方法根据代理类传递给本身的 method 参数来区分是什么方法,咱们来看看 InvocationHandler 类的介绍:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy
instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.复制代码
Public methods | |
---|---|
abstract Object | invoke(Object proxy, Method method, Object[] args) Processes a method invocation on a proxy instance and returns the result. |
方法的参数和返回:
Parameters | |
---|---|
proxy | Object: the proxy instance that the method was invoked on |
method | Method: the Method instance corresponding to the interface method invoked on the proxy instance. The declaring class of the Method object will be the interface that the method was declared in, which may be a superinterface of the proxy interface that the proxy class inherits the method through. |
args | Object: an array of objects containing the values of the arguments passed in the method invocation on the proxy instance, or null if interface method takes no arguments. Arguments of primitive types are wrapped in instances of the appropriate primitive wrapper class, such as java.lang.Integer or java.lang.Boolean. |
Returns | |
---|---|
Object | the value to return from the method invocation on the proxy instance. If the declared return type of the interface method is a primitive type, then the value returned by this method must be an instance of the corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If the value returned by this method is null and the interface method's return type is primitive, then a NullPointerException will be thrown by the method invocation on the proxy instance. If the value returned by this method is otherwise not compatible with the interface method's declared return type as described above, a ClassCastException will be thrown by the method invocation on the proxy instance. |
上面提到的一点须要特别注意的是,若是 Subject 类中定义的方法返回值为 8 种基本数据类型,那么在 ProxySubject 类中必需要返回相应的基本类型包装类,即 int 对应的返回为 Integer 等等,还须要注意的是若是此时返回 null,则会抛出 NullPointerException,除此以外的其余状况下返回值的对象必需要和 Subject 类中定义方法的返回值一致,要否则会抛出 ClassCastException。
那么经过 Proxy 类的 newProxyInstance 方法动态生成的类是什么样子的呢,咱们上面也提到了,JDK 为咱们提供了一个方法 ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 来产生动态代理类的字节码,这个类位于 sun.misc 包中,是属于特殊的 jar 包,因而问题又来了,android studio 建立的 android 工程是无法找到 ProxyGenerator 这个类的,这个类在 jre 目录下,就算我把这个类相关的 .jar 包拷贝到工程里面而且在 gradle 里面引用它,虽然最后可以找到这个类,可是编译时又会出现很奇葩的问题,因此,没办法喽,android studio 没办法建立普通的 java 工程,只能本身装一个 intellij idea 或者求助相关的同事了。建立好 java 工程以后,使用下面这段代码就能够将生成的类导出到指定路径下面:
public static void generateClassFile(Class clazz,String proxyName)
{
//根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = "D:\\"; // 这里写死路径为 D 盘,能够根据实际须要去修改
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盘中
out = new FileOutputStream(paths+proxyName+".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}复制代码
调用代码的方式为:
generateClassFile(ProxySubject.class, "ProxySubject");复制代码
最后就会在 D 盘(若是没有修改路径)的根目录下面生成一个 ProxySubject.class 的文件,使用 jd-gui 就能够打开该文件:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxySubject extends Proxy implements Subject {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxySubject(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String operation()
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("Subject").getMethod("operation", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}复制代码
能够观察到这个生成的类继承自 java.lang.reflect.Proxy,实现了 Subject 接口,咱们在看看生成动态类的代码:
Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), proxy);复制代码
可见这个动态生成类会实现 subject.getClass().getInterfaces()
中的全部接口,而且还有一点是类中全部的方法都是 final 的,并且该类也是 final ,因此该类不可继承,最后就是全部的方法都会调用到 InvocationHandler 对象 h 的 invoke() 方法,这也就是为何最后会调用到 ProxySubject 类的 invoke() 方法了,画一下它们的简单类图:
经过上面对 InvocationHandler 的介绍,咱们对这个接口应该有了大致的了解,可是在运行时动态生成的代理类有什么做用呢,其实它的做用就是在调用真正业务以前或者以后插入一些额外的操做:
第一步
关于 ServiceManager 的详细介绍在个人博客:android IPC通讯(下)-AIDL 中已经介绍过了,这里就不赘述了,强烈建议你们去看一下那篇博客,咱们这里就着重看一下 ServiceManager 的 getService(String name) 方法:
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
/** * Returns a reference to a service with the given name. * * @param name the name of the service to get * @return a reference to the service, or <code>null</code> if the service doesn't exist */
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static void addService(String name, IBinder service) {
...
}
....
}复制代码
咱们能够看到,getService 方法第一步会去 sCache 这个 map 中根据 Service 的名字获取这个 Service 的 IBinder 对象,若是获取到为空,则会经过 ServiceManagerNative 经过跨进程通讯获取这个 Service 的 IBinder 对象,因此咱们就以 sCache 这个 map 为切入点,反射该对象,而后修改该对象,因为系统的 android.os.ServiceManager 类是 @hide 的,因此只能使用反射,根据这个初步思路,写下第一步的代码:
Class c_ServiceManager = Class.forName("android.os.ServiceManager");
if (c_ServiceManager == null) {
return;
}
if (sCacheService == null) {
try {
Field sCache = c_ServiceManager.getDeclaredField("sCache");
sCache.setAccessible(true);
sCacheService = (Map<String, IBinder>) sCache.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
sCacheService.remove(serviceName);
sCacheService.put(serviceName, service);复制代码
反射 sCache 这个变量,移除系统 Service,而后将咱们本身改造过的 Service put 进去,这样就能实现当调用 ServiceManager 的 getService(String name) 方法的时候,返回的是咱们改造过的 Service 而不是系统的原生 Service。
第二步
第一步知道了如何去将改造事后的 Service put 进系统的 ServiceManager 中,第二步就是去生成一个 hook Service 了,怎么去生成呢?这就要用到咱们上面介绍到的 InvocationHandler 类,咱们先获取原生的 Service ,而后经过 InvocationHandler 去构造一个 hook Service,最后经过第一步的步骤 put 进 sCache 这个变量便可,第二步代码:
public class ServiceHook implements InvocationHandler {
private static final String TAG = "ServiceHook";
private IBinder mBase;
private Class<?> mStub;
private Class<?> mInterface;
private InvocationHandler mInvocationHandler;
public ServiceHook(IBinder mBase, String iInterfaceName, boolean isStub, InvocationHandler InvocationHandler) {
this.mBase = mBase;
this.mInvocationHandler = InvocationHandler;
try {
this.mInterface = Class.forName(iInterfaceName);
this.mStub = Class.forName(String.format("%s%s", iInterfaceName, isStub ? "$Stub" : ""));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("queryLocalInterface".equals(method.getName())) {
return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[] { mInterface },
new HookHandler(mBase, mStub, mInvocationHandler));
}
Log.e(TAG, "ERROR!!!!! method:name = " + method.getName());
return method.invoke(mBase, args);
}
private static class HookHandler implements InvocationHandler {
private Object mBase;
private InvocationHandler mInvocationHandler;
public HookHandler(IBinder base, Class<?> stubClass,
InvocationHandler InvocationHandler) {
mInvocationHandler = InvocationHandler;
try {
Method asInterface = stubClass.getDeclaredMethod("asInterface", IBinder.class);
this.mBase = asInterface.invoke(null, base);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (mInvocationHandler != null) {
return mInvocationHandler.invoke(mBase, method, args);
}
return method.invoke(mBase, args);
}
}
}复制代码
这里咱们以 ClipboardService 的调用代码为例:
IBinder clipboardService = ServiceManager.getService(Context.CLIPBOARD_SERVICE);
String IClipboard = "android.content.IClipboard";
if (clipboardService != null) {
IBinder hookClipboardService =
(IBinder) Proxy.newProxyInstance(clipboardService.getClass().getClassLoader(),
clipboardService.getClass().getInterfaces(),
new ServiceHook(clipboardService, IClipboard, true, new ClipboardHookHandler()));
//调用第一步的方法
ServiceManager.setService(Context.CLIPBOARD_SERVICE, hookClipboardService);
} else {
Log.e(TAG, "ClipboardService hook failed!");
}复制代码
分析一下上面的这段代码,分析以前,先要看一下 ClipboardService 的相关类图:
new Class[] { IBinder.class }
其实也是没有问题的(感兴趣的能够试一下,第一个参数修改成 IBinder.class.getClassLoader(),第二个参数修改成 new Class[]{IBinder.class},也是能够的),由于实际使用的时候,咱们只是用到了 IBinder 类的 queryLocalInterface 方法,其余的方法都没有使用到,接下来咱们就会说明 queryLocalInterface 这个函数的做用;new Class[] { IBinder.class }
也是没有问题,由于第一次调用到 queryLocalInterface 函数以后,后续的全部调用都到了 HookHandler 对象中,动态生成的对象中只须要有 IBinder 的 queryLocalInterface 方法便可,而不须要 IClipboard 接口的其余方法;Class.forName(String.format("%s%s", iInterfaceName, isStub ? "$Stub" : ""))//"android.content.IClipboard"复制代码
这个参数我们对照上面的类图,这个类为 ClipboardService 的父类,它里面有一个 asInterface 的方法,经过反射 asInterface 方法而后将 IBinder 对象变成 IInterface 对象,为何要这么作,能够去看看个人博客: java/android 设计模式学习笔记(9)---代理模式 中的最后总结,经过 ServiceManager.getService 方法获取一个 IBinder 对象,可是这个 IBinder 对象不能直接调用,必需要经过 asInterface 方法转成对应的 IInterface 对象才可使用,因此 mBase 对象实际上是一个 IInterface 对象:
public class ClipboardHook {
private static final String TAG = ClipboardHook.class.getSimpleName();
public static void hookService(Context context) {
IBinder clipboardService = ServiceManager.getService(Context.CLIPBOARD_SERVICE);
String IClipboard = "android.content.IClipboard";
if (clipboardService != null) {
IBinder hookClipboardService =
(IBinder) Proxy.newProxyInstance(IBinder.class.getClassLoader(),
new Class[]{IBinder.class},
new ServiceHook(clipboardService, IClipboard, true, new ClipboardHookHandler()));
ServiceManager.setService(Context.CLIPBOARD_SERVICE, hookClipboardService);
} else {
Log.e(TAG, "ClipboardService hook failed!");
}
}
public static class ClipboardHookHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
int argsLength = args.length;
//每次从本应用复制的文本,后面都加上分享的出处
if ("setPrimaryClip".equals(methodName)) {
if (argsLength >= 2 && args[0] instanceof ClipData) {
ClipData data = (ClipData) args[0];
String text = data.getItemAt(0).getText().toString();
text += "this is shared from ServiceHook-----by Shawn_Dut";
args[0] = ClipData.newPlainText(data.getDescription().getLabel(), text);
}
}
return method.invoke(proxy, args);
}
}
}复制代码
因此 ClipboardHookHandler 类的 invoke 方法最终获取到了要 hook 的 Service 的 IInterface 对象(即为 IClipboard.Proxy 对象,最后经过 Binder Driver 调用到了系统的 ClipboardService 中),调用函数的 Method 对象和参数列表对象,获取到了这些以后,不用我说了,就能够尽情的去作一些额外的操做了,我这里是在仿照知乎复制文字时,在后面加上相似的版权声明。
上面就是 ServiceHook 的详细步骤了,了解它必需要对 InvocationHandler 有详细的了解,而且还要去看一下 AOSP 源码,好比要去 hook ClipboardService ,那么就要去先看看 ClipboardService 的源码,看看这个类中每一个函数的名字和做用,参数列表中每一个参数的顺序和做用,并且有时候这还远远不够,咱们知道,随着 Android 每一个版本的更新,这些类可能也会被更新修改甚至删除,颇有可能对于新版原本说老的 hook 方法就无论用了,这时候必需要去了解最新的源码,看看更新修改的地方,针对新版本去从新制定 hook 的步骤,这是一点须要慎重对待考虑的地方。
此为咱们公司某位大神代码,通过整理修改而出,不知道有没有版权问题,哈哈哈,谢谢周杰大神,虽然已经不在公司,感谢感谢~~
源码下载地址:github.com/zhaozepeng/…
先来看看运行的效果:
ServiceManager.java
public class ServiceManager {
private static Method sGetServiceMethod;
private static Map<String, IBinder> sCacheService;
private static Class c_ServiceManager;
static {
try {
c_ServiceManager = Class.forName("android.os.ServiceManager");
} catch (Exception e) {
e.printStackTrace();
}
}
public static IBinder getService(String serviceName) {
if (c_ServiceManager == null) {
return null;
}
if (sGetServiceMethod == null) {
try {
sGetServiceMethod = c_ServiceManager.getDeclaredMethod("getService", String.class);
sGetServiceMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
if (sGetServiceMethod != null) {
try {
return (IBinder) sGetServiceMethod.invoke(null, serviceName);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
public static void setService(String serviceName, IBinder service) {
if (c_ServiceManager == null) {
return;
}
if (sCacheService == null) {
try {
Field sCache = c_ServiceManager.getDeclaredField("sCache");
sCache.setAccessible(true);
sCacheService = (Map<String, IBinder>) sCache.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
sCacheService.remove(serviceName);
sCacheService.put(serviceName, service);
}
}复制代码
ServiceManager 这个类就是使用反射的方式去获取对应 Service (这里不能使用 Context.getSystemService 函数,由于它的返回不是 IBinder 对象,好比对于 ClipboardService,它就是 ClipboardManager 对象)和设置 service 到 sCache 变量中;
ServiceHook.java
public class ServiceHook implements InvocationHandler {
private static final String TAG = "ServiceHook";
private IBinder mBase;
private Class<?> mStub;
private Class<?> mInterface;
private InvocationHandler mInvocationHandler;
public ServiceHook(IBinder mBase, String iInterfaceName, boolean isStub, InvocationHandler InvocationHandler) {
this.mBase = mBase;
this.mInvocationHandler = InvocationHandler;
try {
this.mInterface = Class.forName(iInterfaceName);
this.mStub = Class.forName(String.format("%s%s", iInterfaceName, isStub ? "$Stub" : ""));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("queryLocalInterface".equals(method.getName())) {
return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[] { mInterface },
new HookHandler(mBase, mStub, mInvocationHandler));
}
Log.e(TAG, "ERROR!!!!! method:name = " + method.getName());
return method.invoke(mBase, args);
}
private static class HookHandler implements InvocationHandler {
private Object mBase;
private InvocationHandler mInvocationHandler;
public HookHandler(IBinder base, Class<?> stubClass,
InvocationHandler InvocationHandler) {
mInvocationHandler = InvocationHandler;
try {
Method asInterface = stubClass.getDeclaredMethod("asInterface", IBinder.class);
this.mBase = asInterface.invoke(null, base);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (mInvocationHandler != null) {
return mInvocationHandler.invoke(mBase, method, args);
}
return method.invoke(mBase, args);
}
}
}复制代码
这个类上面介绍的很详细了,在这里就不继续介绍了;
ClipboardHook.java
public class ClipboardHook {
private static final String TAG = ClipboardHook.class.getSimpleName();
public static void hookService(Context context) {
IBinder clipboardService = ServiceManager.getService(Context.CLIPBOARD_SERVICE);
String IClipboard = "android.content.IClipboard";
if (clipboardService != null) {
IBinder hookClipboardService =
(IBinder) Proxy.newProxyInstance(IBinder.class.getClassLoader(),
new Class[]{IBinder.class},
new ServiceHook(clipboardService, IClipboard, true, new ClipboardHookHandler()));
ServiceManager.setService(Context.CLIPBOARD_SERVICE, hookClipboardService);
} else {
Log.e(TAG, "ClipboardService hook failed!");
}
}
public static class ClipboardHookHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
int argsLength = args.length;
//每次从本应用复制的文本,后面都加上分享的出处
if ("setPrimaryClip".equals(methodName)) {
if (argsLength >= 2 && args[0] instanceof ClipData) {
ClipData data = (ClipData) args[0];
String text = data.getItemAt(0).getText().toString();
text += "this is shared from ServiceHook-----by Shawn_Dut";
args[0] = ClipData.newPlainText(data.getDescription().getLabel(), text);
}
}
return method.invoke(proxy, args);
}
}
}复制代码
这里的 hook ClipboardService 使用的是,将复制的文本取出,在结尾处添加咱们自定义的文本,用户再粘贴到其余地方的时候,就会出现咱们添加上咱们自定义文本后的文字了,还有须要注意的是这只会影响应用进程的 ClipboardService,并不会影响主进程的相关 Service,由于无论怎么 hook,修改的都是应用进程的 ServiceManager 里面的 sCache 变量,应用进程的 ServiceManager 其实也就至关于一个 Binder Client,sCache 里面获取不到对应的 Service,它就会自动经过 Binder Driver 和 Binder Server (主进程的 ServiceManager)通讯去获取对应的 Service,因此修改 Binder Client,实际上是不会影响 Binder Server的,不明白的建议仍是看看:android IPC通讯(下)-AIDL。
测试代码
switch (v.getId()) {
case R.id.btn_copy:
String input = mEtInput.getText().toString().trim();
if (TextUtils.isEmpty(input)) {
Toast.makeText(this, "input不能为空", Toast.LENGTH_SHORT).show();
return;
}
//复制
ClipData clip = ClipData.newPlainText("simple text", mEtInput.getText().toString());
clipboard.setPrimaryClip(clip);
break;
case R.id.btn_show_paste:
//黏贴
clip = clipboard.getPrimaryClip();
if (clip != null && clip.getItemCount() > 0) {
Toast.makeText(this, clip.getItemAt(0).getText(), Toast.LENGTH_SHORT).show();
}
break;
}复制代码
测试代码,我这里就不须要说明了,Clipboard 的简单使用而已。
这里只演示了 hook ClipboardService 的例子,其余的使用方式好比插件化等等在这里就不一一介绍了,感兴趣的能够去网上查阅相关的资料。
转载请注明出处:blog.csdn.net/self_study/…
blog.csdn.net/luanlouis/a…
www.cnblogs.com/xiaoluo5013…
blog.csdn.net/self_study/…
paddy-w.iteye.com/blog/841798
www.cnblogs.com/flyoung2008…