Android插件化之—— 绑定和解绑插件Service

上篇文章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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值