上篇文章Android 插件化之——启动和停止插件Service讲解了启动插件service的演示,这篇文章,继续绑定插件service和解绑插件service的演示。和上篇启动插件service的原理类似,也是使用代理转发,通过Hook AMS,在AMS的bindService方法中,将要启动的插件service变换成代理service,这样绑定的其实就是代理service,然后将插件service的信息保存到Intent中,并将ServiceConnection对象作为key,插件Intent作为value,保存在一个HashMap的集合中(这样做是方便解绑时,通过unBind方法传入的ServiceConnection对象这个参数作为key从这个HashMap集合,拿到ServiceConnection对应的插件Service的Intent信息,然后,根据这个Intent信息,从插件services集合中,查找到对应的service对象)在代理service的onBind方法中,将实际的要绑定的插件service的信息从Intent中还原,然后创建插件service的实例对象,并调用插件service的attach方法,使绑定的插件service拥有上下文环境,接着调用插件service的onCreate方法,在调用插件service的onBind方法,并将插件service保存到插件services集合中。在解绑时,Hook AMS的 unbindService方法,在这个方法中通过传入的ServiceConnection参数,在保存了ServiceConnection的HashMap集合中,通过这个serviceConnection作为key,拿到相应的插件Service的Intent信息,然后,拿到插件service的Intent信息后,在通过这个Intent信息从插件services集合查找到对应的插件Service,然后调用插件Service的onUnbind方法,onDestroy方法。并将插件service从插件services集合中删除。在将保存了ServiceConnection的HashMap集合中将这个ServiceConnection相关的信息删除。
根据这个思路下面看看具体实现:
首先Hook AMS的bindService方法:
import android.content.Intent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import test.cn.example.com.androidskill.hook.service.ProxyService;
import test.cn.example.com.util.LogUtil;
public class IActivityManagerInvocationHandler implements InvocationHandler {
private final Object mActivityManager;
public IActivityManagerInvocationHandler(Object activityManager){
this.mActivityManager = activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("startActivity")){
// ActivityManagerService类中的startActivity方法的10个参数
// startActivity(IApplicationThread caller, String callingPackage,
// Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
// int startFlags, ProfilerInfo profilerInfo, Bundle bOptions)
Intent intent = null;
int index = -1;
String packageName = "test.cn.example.com.androidskill";
String plugClassName = packageName+".hook.PlugActivity";
LogUtil.i("args.length "+args.length);
for (int i = 0; i < args.length; i++) {
// LogUtil.i(args[i]+"");
if(args[i] instanceof Intent){
Intent tempIntent = (Intent)args[i];
if(null !=tempIntent.getComponent() && plugClassName.equals(tempIntent.getComponent().getClassName())){
index = i;
break;
}
}
}
if(-1 !=index){
//实际要启动的intent
intent = (Intent) args[index];
//用占坑的BackUpActivity来通过AMS的检查
Intent backupIntent = new Intent();
//test.cn.example.com.androidskill.hook.BackUpActivity
backupIntent.setClassName(packageName,packageName+".hook.BackUpActivity");
backupIntent.putExtra(HookHelper.PLUG_INTENT,intent);
args[index] = backupIntent;
LogUtil.i("成功骗过了AMS");
}
}else if("bindService".equals(method.getName())){
// public int bindService(IApplicationThread caller, IBinder token, Intent service,
// String resolvedType, IServiceConnection connection, int flags, String callingPackage,
// int userId) throws TransactionTooLargeException {
//
// }
int index = -1;
for (int i = 0; i < args.length; i++) {
if(args[i] instanceof Intent){
index = i;
break;
}
}
Object serviceConnection = args[4];//这里取角标4,是因为AMS的bindService方法的第五个参数是IServiceConnection类型的。
if(null == serviceConnection){
throw new IllegalArgumentException("connection is null");
}
Intent rawIntent = (Intent) args[index];
String plugClassName = HookHelper.PACKAGENAME + ".hook.service.PlugService2";
String proxyClassName = HookHelper.PACKAGENAME + ".hook.service.ProxyService";
if(plugClassName.equals(rawIntent.getComponent().getClassName())){
Intent newIntent = new Intent();
newIntent.setClassName(HookHelper.PACKAGENAME, proxyClassName);
newIntent.putExtra(HookHelper.PLUG_INTENT,rawIntent);
//方法解绑时,查找相应的插件Service信息
ProxyService.mBindServices.put(serviceConnection,rawIntent);
args[index] = newIntent;
}
}else if("unbindService".equals(method.getName())){
// public boolean unbindService(IServiceConnection connection) {
//
// }
Object connection = args[0];
if(null == connection){
return false;
}
Intent rawIntent = ProxyService.mBindServices.get(connection);
if(null == rawIntent){
return false;
}
boolean result = ProxyService.unBindPlugService(rawIntent);
if(result){
ProxyService.mBindServices.remove(connection);
}
return result;
}
return method.invoke(mActivityManager,args);
}
}
这个类中,通过拦截AMS的bindService方法,将插件service的Intent信息,变为代理Service的Intent信息,并将IServiceConnection对象保存到一个mBindServices集合中。拦截的unbindService方法,是通过传入的IServiceConnection参数,从mBindServices集合中,查找到插件Service的实际Intent信息。然后通过这个实际的Intent信息,找到相应的插件Service,并调用插件Service的unBind,onDestroy方法,进行解绑和销毁。并将插件Service从mServices集合中删除,接着,在mBindServices集合中,删除和插件Service相关的IServiceConnection的信息。
下面看看ProxyService的具体实现:
import android.app.Application;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import androidx.annotation.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import test.cn.example.com.androidskill.hook.HookHelper;
import test.cn.example.com.androidskill.optimize.hotfix.FixDexUtils2;
import test.cn.example.com.util.LogUtil;
public class ProxyService extends Service {
public static HashMap<String,Service> mServices = new HashMap<>();
public static HashMap<Object,Intent> mBindServices = new HashMap<>();
@Override
public void onCreate() {
super.onCreate();
LogUtil.i("代理servcie onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.i("代理servcie onStartCommand startId="+startId);
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@android.support.annotation.Nullable
@Override
public IBinder onBind(Intent intent) {
LogUtil.i("代理onBind "+intent);
if(intent.hasExtra(HookHelper.PLUG_INTENT)){
Intent rawIntent = intent.getParcelableExtra(HookHelper.PLUG_INTENT);
String className = rawIntent.getComponent().getClassName();
try {
Service service = (Service) Class.forName(className).newInstance();
// public final void attach(
// Context context,
// ActivityThread thread, String className, IBinder token,
// Application application, Object activityManager)
Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
Object sCurrentActivityThread = FixDexUtils2.getObject(activityThreadClazz, "sCurrentActivityThread", null);
Class<?> iInterfaceClazz = Class.forName("android.os.IInterface");
Method asBinderMethod = iInterfaceClazz.getDeclaredMethod("asBinder");
asBinderMethod.setAccessible(true);
Object mAppThread = FixDexUtils2.getObject(activityThreadClazz, "mAppThread", sCurrentActivityThread);
IBinder token = (IBinder) asBinderMethod.invoke(mAppThread);
Object iActivityManager = HookHelper.getIActivityManager();
// public final void attach(
// Context context,
// ActivityThread thread, String className, IBinder token,
// Application application, Object activityManager)
Method attachMethod = Service.class.getDeclaredMethod("attach",Context.class,sCurrentActivityThread.getClass(),
String.class,IBinder.class, Application.class, Object.class);
attachMethod.setAccessible(true);
attachMethod.invoke(service,this,sCurrentActivityThread,className,token,getApplication(),iActivityManager);
service.onCreate();
service.onBind(rawIntent);
mServices.put(className,service);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
LogUtil.i("onDestroy");
}
public static boolean unBindPlugService(Intent intent){
Service service = mServices.get(intent.getComponent().getClassName());
boolean result = service.onUnbind(intent);
service.onDestroy();
//解绑后, 将代理service从插件mServices集合中删除
mServices.remove(intent.getComponent().getClassName());
return result;
}
}
完成上面两步后,在Application的子类的attachBaseContext方法中,调用HookHelper.hookAMS();
接着在Activity中,绑定和解绑插件service。
绑定插件Service的代码如下:
Intent intent_3 = new Intent();
Class<?> plugService2Clazz = null;
try {
plugService2Clazz = Class.forName(HookHelper.PACKAGENAME + ".hook.service.PlugService2");
intent_3.setClass(this,plugService2Clazz);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtil.i("onServiceConnected " + name);
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtil.i("onServiceDisconnected " + name.getClassName());
}
};
bindService(intent_3, serviceConnection,Context.BIND_AUTO_CREATE);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
解绑插件Service的代码如下:
if(null != serviceConnection){
unbindService(serviceConnection);
}
绑定和解绑操作后,打印日志如下:
10-22 : ProxyService.java::27::onCreate-->>代理servcie onCreate
10-22 : ProxyService.java::90::onBind-->>代理onBind Intent { cmp=test.cn.example.com.androidskill/.hook.service.ProxyService (has extras) }
10-22 : PlugService2.java::16::onCreate-->>插件servcie222 onCreate
10-22 : PlugService2.java::29::onBind-->>插件onBind Intent { cmp=test.cn.example.com.androidskill/.hook.service.PlugService2 }
10-22 : PlugService2.java::35::onUnbind-->>插件onUnbind Intent { cmp=test.cn.example.com.androidskill/.hook.service.PlugService2 }
10-22 : PlugService2.java::42::onDestroy-->>插件service222 onDestroy