/* ** DroidPlugin Project ** ** Copyright(c) 2015 Andy Zhang <zhangyong232@gmail.com> ** ** This file is part of DroidPlugin. ** ** DroidPlugin is free software: you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation, either ** version 3 of the License, or (at your option) any later version. ** ** DroidPlugin is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt> ** **/ package com.morgoo.droidplugin.stub; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.IBinder; import com.morgoo.droidplugin.core.Env; import com.morgoo.droidplugin.core.PluginProcessManager; import com.morgoo.droidplugin.pm.PluginManager; import com.morgoo.droidplugin.reflect.FieldUtils; import com.morgoo.droidplugin.reflect.MethodUtils; import com.morgoo.helper.compat.ActivityThreadCompat; import com.morgoo.helper.compat.CompatibilityInfoCompat; import com.morgoo.helper.compat.QueuedWorkCompat; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/9. */ public class ServcesManager { private Map<Object, Service> mTokenServices = new HashMap<Object, Service>(); private Map<String, Service> mNameService = new HashMap<String, Service>(); private Map<Object, Integer> mServiceTaskIds = new HashMap<Object, Integer>(); private ServcesManager() { } private static ServcesManager sServcesManager; public static ServcesManager getDefault() { synchronized (ServcesManager.class) { if (sServcesManager == null) { sServcesManager = new ServcesManager(); } } return sServcesManager; } public boolean hasServiceRunning() { return mTokenServices.size() > 0 && mNameService.size() > 0; } private Object findTokenByService(Service service) { for (Object s : mTokenServices.keySet()) { if (mTokenServices.get(s) == service) { return s; } } return null; } private ClassLoader getClassLoader(ApplicationInfo pluginApplicationInfo) throws Exception { Object object = ActivityThreadCompat.currentActivityThread(); if (object != null) { final Object obj; if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) { obj = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginApplicationInfo, CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO()); } else { obj = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginApplicationInfo); } /*添加ClassLoader LoadedApk.mClassLoader*/ return (ClassLoader) MethodUtils.invokeMethod(obj, "getClassLoader"); } return null; } //这个需要适配,目前只是适配android api 21 private void handleCreateServiceOne(Context hostContext, Intent stubIntent, ServiceInfo info) throws Exception { // CreateServiceData data = new CreateServiceData(); // data.token = fakeToken;// IBinder // data.info =; //ServiceInfo // data.compatInfo =; //CompatibilityInfo // data.intent =; //Intent // activityThread.handleCreateServiceOne(data); // service = activityThread.mTokenServices.get(fakeToken); // activityThread.mTokenServices.remove(fakeToken); ResolveInfo resolveInfo = hostContext.getPackageManager().resolveService(stubIntent, 0); ServiceInfo stubInfo = resolveInfo != null ? resolveInfo.serviceInfo : null; PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName); PluginProcessManager.preLoadApk(hostContext, info); Object activityThread = ActivityThreadCompat.currentActivityThread(); IBinder fakeToken = new MyFakeIBinder(); Class CreateServiceData = Class.forName(ActivityThreadCompat.activityThreadClass().getName() + "$CreateServiceData"); Constructor init = CreateServiceData.getDeclaredConstructor(); if (!init.isAccessible()) { init.setAccessible(true); } Object data = init.newInstance(); FieldUtils.writeField(data, "token", fakeToken); FieldUtils.writeField(data, "info", info); if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) { FieldUtils.writeField(data, "compatInfo", CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO()); } Method method = activityThread.getClass().getDeclaredMethod("handleCreateService", CreateServiceData); if (!method.isAccessible()) { method.setAccessible(true); } method.invoke(activityThread, data); Object mService = FieldUtils.readField(activityThread, "mServices"); Service service = (Service) MethodUtils.invokeMethod(mService, "get", fakeToken); MethodUtils.invokeMethod(mService, "remove", fakeToken); mTokenServices.put(fakeToken, service); mNameService.put(info.name, service); if (stubInfo != null) { PluginManager.getInstance().onServiceCreated(stubInfo, info); } } private void handleOnStartOne(Intent intent, int flags, int startIds) throws Exception { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { Service service = mNameService.get(info.name); if (service != null) { ClassLoader classLoader = getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(classLoader); Object token = findTokenByService(service); Integer integer = mServiceTaskIds.get(token); if (integer == null) { integer = -1; } int startId = integer + 1; mServiceTaskIds.put(token, startId); int res = service.onStartCommand(intent, flags, startId); QueuedWorkCompat.waitToFinish(); } } } private void handleOnTaskRemovedOne(Intent intent) throws Exception { if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { Service service = mNameService.get(info.name); if (service != null) { ClassLoader classLoader = getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(classLoader); service.onTaskRemoved(intent); QueuedWorkCompat.waitToFinish(); } QueuedWorkCompat.waitToFinish(); } } } private void handleOnDestroyOne(ServiceInfo targetInfo) { Service service = mNameService.get(targetInfo.name); if (service != null) { service.onDestroy(); mNameService.remove(targetInfo.name); Object token = findTokenByService(service); mTokenServices.remove(token); mServiceTaskIds.remove(token); service = null; QueuedWorkCompat.waitToFinish(); PluginManager.getInstance().onServiceDestory(null, targetInfo); } QueuedWorkCompat.waitToFinish(); } private IBinder handleOnBindOne(Intent intent) throws Exception { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { Service service = mNameService.get(info.name); if (service != null) { ClassLoader classLoader = getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(classLoader); return service.onBind(intent); } } return null; } private void handleOnRebindOne(Intent intent) throws Exception { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { Service service = mNameService.get(info.name); if (service != null) { ClassLoader classLoader = getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(classLoader); service.onRebind(intent); } } } private boolean handleOnUnbindOne(Intent intent) throws Exception { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { Service service = mNameService.get(info.name); if (service != null) { ClassLoader classLoader = getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(classLoader); return service.onUnbind(intent); } } return false; } public int onStart(Context context, Intent intent, int flags, int startId) throws Exception { Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT); if (targetIntent != null) { ServiceInfo targetInfo = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0); if (targetInfo != null) { Service service = mNameService.get(targetInfo.name); if (service == null) { handleCreateServiceOne(context, intent, targetInfo); } handleOnStartOne(targetIntent, flags, startId); } } return -1; } public void onTaskRemoved(Context context, Intent intent) throws Exception { Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT); if (targetIntent != null) { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0); Service service = mNameService.get(info.name); if (service == null) { handleCreateServiceOne(context, intent, info); } handleOnTaskRemovedOne(targetIntent); } } public IBinder onBind(Context context, Intent intent) throws Exception { Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT); if (targetIntent != null) { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0); Service service = mNameService.get(info.name); if (service == null) { handleCreateServiceOne(context, intent, info); } return handleOnBindOne(targetIntent); } return null; } public void onRebind(Context context, Intent intent) throws Exception { Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT); if (targetIntent != null) { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0); Service service = mNameService.get(info.name); if (service == null) { handleCreateServiceOne(context, intent, info); } handleOnRebindOne(targetIntent); } } public boolean onUnbind(Intent intent) throws Exception { Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT); if (targetIntent != null) { ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0); Service service = mNameService.get(info.name); if (service != null) { return handleOnUnbindOne(targetIntent); } } return false; } public int stopService(Context context, Intent intent) throws Exception { ServiceInfo targetInfo = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (targetInfo != null) { handleOnUnbindOne(intent); handleOnDestroyOne(targetInfo); return 1; } return 0; } public boolean stopServiceToken(ComponentName cn, IBinder token, int startId) throws Exception { Service service = mTokenServices.get(token); if (service != null) { Integer lastId = mServiceTaskIds.get(token); if (lastId == null) { return false; } if (startId != lastId) { return false; } Intent intent = new Intent(); intent.setComponent(cn); ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0); if (info != null) { handleOnUnbindOne(intent); handleOnDestroyOne(info); return true; } } return false; } public void onDestroy() { for (Service service : mTokenServices.values()) { service.onDestroy(); } mTokenServices.clear(); mServiceTaskIds.clear(); mNameService.clear(); QueuedWorkCompat.waitToFinish(); } }