A, description,

Android plug-in related articles are based on Bao Jianqiang’s “Android plug-in development Guide” books to learn, but the subsequent code part is based on Android P source code. To learn more about how to implement a plug-in for an Activity, you can go directly to the Activity article, which is also recommended to start reading. This article will help you better understand the plug-in in Android.

The use of Service

In Android, Service is mainly used to perform background tasks, such as playing music or downloading files through Service in the background, or starting a foreground Service to achieve the purpose of keeping alive and so on. In Android, there are two ways to start a Service:

1, the startService

If the Service is started, the onCreate and onStartCommand methods of the Service are executed. If the method is invoked again, only the onStartCommand method is executed. The corresponding stopping Service is the method stopService. In addition, the Serive started in this way belongs to its process, that is, in other components we can also call the stopService method to stop the Service.

2, bindService

If you start a Service, the onCreate and onBind methods are executed. No matter how many times the methods are called, the onCreate and onBind methods are not executed. The corresponding stopping Service is the method unBindService. If the Context is destroyed, the Service will be destoried as well. For example, if you use this method to start a Service in an Activity, if the Activity is destroyed, the started Service is also destroyed.

ActivityThread layer Service startup source code parsing

In the ActivityManager layer, the source code of the Service and the source code of the Activity are very much the same, so I do not do too much parsing here, the subsequent implementation of ActivityManager hook is also reuse in the Activity chapter implementation. Just add the above methods for starting and stopping Service to the filter method. No more talking and get straight to the point.

Because the life cycle of Service is not as much as Activity, so its source code implementation part is not as complex as Activity, plus the previous understanding of Activity plug-in, understanding the source code of Activity is not too simple.

1. Call onCreate

The lifecycle method calls to the four major components in ActivityThread are distributed through the inner class mH Handler. The onCreate method, the life cycle source of the Service, hits the CREATE_SERVICE(114) branch of mH and is called into the handleCreateService. The corresponding source code is as follows:

private void handleCreateService(CreateServiceData data) { ....... The final implementation of this method is also called in the getPackageInfo method. LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; Try {// See if this is more familiar. Yes, the ClassLoader that loads the Activity class is also obtained from this LoadedApk. So that in the implementation of the follow-up Service plug-in, we can directly reuse implementation Activity in the plug-in code. Java lang. This cl = packageInfo. GetClassLoader (); / / the pain eventually is corresponding to this load before name for the data info. Name the corresponding Service the Service = packageInfo. GetAppFactory () instantiateService (cl, data.info.name, data.intent); } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to instantiate service " + data.info.name + ": " + e.toString(), e); } } try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); // Initialize the Service, Service and call its onCreate method. The attach (context, this, the data. The info. Name, data. The token, app, ActivityManager. GetService ()); service.onCreate(); Mservices. put(data.token, Service); // Save the created Service for onStart, onBind, unBind, and onDestroy. try { ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to create service " + data.info.name + ": " + e.toString(), e); }}}Copy the code

2. Other lifecycle methods

The other lifecycle methods of a Service correspond to the activityThread.java method implementation as follows:

OnStartCommand: handleServiceArgs

OnBind: handleBindService

OnUnbind: handleUnbindService

OnDestroy: handleStopService

After looking at the source code implementation, you will see that the above lifecycle methods operate on Service instance objects created in the method handleCreateService. So for the plug-in implementation of Service we only need to intercept the method handleCreateService is enough.

Three, Service plug-in implementation

1. Implementation steps

With the above source code understanding analysis and the implementation of the Activity plug-in support, the implementation of the Service plug-in is simply not too simple. It can be roughly divided into the following steps:

The first step is to reuse the Activity plug-in implemented in the AMNHook and ActivityThread Handler hook

The second step is to intercept the related methods startService, startService and bindService in the AMN of hook, and replace the started Service with the Service embedded in the host.

The third step is to intercept the method handleCreateService in the hook Handler.

2. Code implementation

Because the AMN and the corresponding hook to the Handler are annotated in the Activity article, we will not do too much verbose here and skip to the code implementation of the second step.

/** ** @param realIntent = @param name = @param name = @param name = @param name Private void hookServiceOperate(Intent realIntent, String name) {if (null! = realIntent) { ComponentName pluginComponentName = realIntent.getComponent(); if (null ! = pluginComponentName) { String pluginServiceName = pluginComponentName.getClassName(); / / get to launch the plug-in Service by the corresponding host Service name String hostServiceName = HostToPluginMapping. GetHostService (pluginServiceName); if (! TextUtils.isEmpty(hostServiceName)) { Log.i(TAG, "current is hooking " + name); // Replace the actual Service to be started with the Service in the host to deceive AMS ComponentName = new ComponentName(pluginComponentName.getPackageName(), hostServiceName); realIntent.setComponent(componentName); }}}}Copy the code

It is so easy to deceive AMS by replacing the plugin Service that needs to be started by the upper-layer application with the Service embedded in the host.

Finally, the hook Handler intercepts the method handleCreateService. The corresponding implementation is as follows:

Public void handleCreateService(Object Object) {try {// First get the ServiceInfo property ServiceInfo in the CreateServiceData Object passed from AMS serviceInfo = (ServiceInfo) RefInvoke.getFieldValue(RefInvoke.getField(object.getClass(), "info"), object); String hostServiceName = serviceInfo.name; String pluginServiceName = HostToPluginMapping.getPluginService(hostServiceName); if (TextUtils.isEmpty(pluginServiceName)) { Log.i(TAG, "not found host service,so no need replace"); return; } String path = DePluginSP.getInstance(DePluginApplication.getContext()).getString(Constants.COPY_FILE_PATH, ""); ReplaceClassloader (path); // replaceClassloader(path); // replaceClassloader(path); ServiceInfo. Name = pluginServiceName; ServiceInfo = pluginServiceName; serviceInfo.applicationInfo.packageName = mPathToPluginNameMap.get(path); Log.i(TAG, "replaced to plugin service success"); } catch (Exception e) { Log.i(TAG, "handle create service failed"); }... // Call the handleCreateService method of the ActivityThread to generate the corresponding Service object and initialize it.Copy the code

3, mergeDex

Of course, there is no need to generate ClassLoader to load the plug-in Service class. We can add the dex file corresponding to the plug-in to the pathList of BaseDexClassLoader by mergeDex. This can be loaded directly from the default ClassLoader of the current application, which is one of the ways hot fixes are implemented in Android. The corresponding implementation is as follows:

public static void mergeDex(ClassLoader classLoader, File apkFile, File optDexfile) { try { Object pathListObj = RefInvoke.getFieldValue(RefInvoke.getField(BaseDexClassLoader.class, "pathList"), classLoader); if (null == pathListObj) { Log.i(TAG, "get path list failed"); return; } Field dexElementsField = RefInvoke.getField(pathListObj.getClass(), "dexElements"); Object[] elements = (Object[]) RefInvoke.getFieldValue(dexElementsField, pathListObj); if (null == elements) { Log.i(TAG, "get elements failed"); return; } int length = elements.length; Class<? > elementCls = elements.getClass().getComponentType(); Object[] newElemets = (Object[]) Array.newInstance(elementCls, length + 1); // The ClassLoader traverses the Dex Class from front to back. // Insert the Dex of the repaired Class into the first Element of the Element array. Object elementObj = RefInvoke.createObject(elementCls, new Class[]{DexFile.class, File.class}, new Object[]{DexFile.loadDex(apkFile.getCanonicalPath(), optDexfile.getAbsolutePath(), 0), apkFile}); newElemets[0] = elementObj; System.arraycopy(elements, 0, newElemets, 1, length); RefInvoke.setFieldValue(dexElementsField, pathListObj, newElemets); Log.i(TAG, "merge dex success"); } catch (Exception e) { Log.e(TAG, "mergeDex failed " + e); }}Copy the code

Four,

So, with the Activity plug-in implementation, the Service plug-in implementation is a simple two-step process.

There is a view of the source can directly jump to Github, and want to learn more detailed students can read Bao Jianqiang’s “Android plug-in development Guide” books