This article is from: github.com/SusionSuc/A…

Before continuing to see how to start a plug-in Service in VirtualApk, let’s take a quick look at how Android starts a Service, mainly to get an impression.

The source code below is from Android8.0. The source code of the post only contains some key points.

General process for starting a Service

Let’s start with contextimpl.startService (). Why start here? If you’ve seen the previous article about the Activity startup plug-in, you know that when the Activity is created, the instance of its Context is ContextImpl, so let’s look at its startService().

public ComponentName startService(Intent service) { ..... return startServiceCommon(service, false, mUser); } private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) { .. ActivityManager.getService().startService(mMainThread.getApplicationThread(), service, ....) ; . }Copy the code

That is, ask ActivityManager directly to start the Service. This is a cross-process call through Binder. The last call to ActivityManagerService. The startService (), and then it calls the ActiveServices. StartServiceLocked (), take a look at the key steps:

ComponentName startServiceLocked(....) {// Create ServiceRecord and cache it ServiceLookupResult res = retrieveServiceLocked(.....) ; R = res.record; . ComponentName CMP = startServiceInnerLocked(SMAP, Service, r, callerFg, addToStarting); }Copy the code

In the subsequent process, the real start Service method is ActiveServices bringUpServiceLocked () :

private String bringUpServiceLocked(ServiceRecord r, .....) { ProcessRecord app; . Service Final Boolean Isolated = (R.serviceInfo. flags&ServiceInfo.FLAG_ISOLATED_PROCESS)! = 0; . if (! isolated) { app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); . realStartServiceLocked(r, app, execInFg); Service return null; } // Start a corresponding process app = mam.startProcessLocked (procName, r.apinfo, True, intentFlags, hostingType, r.name. false, isolated, false) ..... // start if (! mPendingServices.contains(r)) { mPendingServices.add(r); }}Copy the code

ActiveServices. RealStartServiceLocked () doing at all is very familiar with this method:

app.thread.scheduleCreateService(r, r.serviceInfo,....) ; // This method causes service.oncreate () to call.... sendServiceArgsLocked(r, execInFg, true); // This method results in a call to service.onstartCommand ()Copy the code

After switching to our application’s process, start the Service in ActivityThread:

//ActivityThread.java private void handleCreateService(CreateServiceData data) { ...... java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = (Service) cl.loadClass(data.info.name).newInstance(); . ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); / / to null, not directly return to service. The attach (context, this, the data. The info. Name, data. The token, app, ActivityManager. GetService ()); service.onCreate(); mServices.put(data.token, service); . }Copy the code

Ok analysis At this point, we have a rough idea of how the Service is started. The binding of services will not be analyzed here. So what’s the point of analyzing the source code? This is, of course, to facilitate the subsequent startup of the analysis plug-in Service.

VirtualApk- Start of plug-in Service

  • Does the server validate Service startup?

Comments were noted earlier when analyzing the source code. ActiveServices. RetrieveServiceLocked () this approach in constructing ServiceRecord will do the check. It must be registered in the manifest file. So to start a plug-in Service, we need the same plug-in Activity to start a spoiling Service.

Dynamic proxy ActivityManagerService

So VirtualApk for plug-in Service start the first step is to bypass the system verification, the principle is similar to the plug-in Activity start, but because the Service start and Instrumentation relationship is not big, It hooks ActivityManagerService.

protected void hookSystemServices() { Singleton<IActivityManager> defaultSingleton; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get(); } else { defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get(); } // Hook IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[] { IActivityManager.class }, createActivityManagerProxy(defaultSingleton.get())); Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); // hook for android8.0Copy the code

After hook, the purpose of course is to bypass the system to check the plug-in Service. Similar to the start of an Activity, the trap Service is checked against ActivityManagerService when the plug-in Service is started. VirtualApk also defines two trap services:

    <service android:exported="false" android:name="com.didi.virtualapk.delegate.LocalService" />
    <service android:exported="false" android:name="com.didi.virtualapk.delegate.RemoteService" android:process=":daemon"/>
Copy the code

Now let’s look at how VirtualApk replaces the plugin Service with the other two services when the Service starts:

Public class ActivityManagerProxy implements InvocationHandler {// @Override Public Object Invoke (Object Proxy, Method Method, Object[] args) throws Throwable { if ("startService".equals(method.getName())) { return startService(proxy, method, args); } if ("stopService".equals(method.getName())) { return stopService(proxy, method, args); }}Copy the code

That if the call is ActivityManagerService startService, then do some processing:

protected Object startService(Object proxy, Method method, Object[] args) throws Throwable { Intent target = (Intent) args[1]; . Intent = wrapperTargetIntent(Target, serviceInfo, extras) RemoteService.EXTRA_COMMAND_START_SERVICE); return mPluginManager.getHostContext().startService(wrapperIntent); } protected Object stopService(Object proxy, Method method, Object[] args) throws Throwable { .... Process with startService startDelegateServiceForTarget (target, resolveInfo serviceInfo, null, RemoteService.EXTRA_COMMAND_STOP_SERVICE); } protected Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) { target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name)); String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation(); Is the service started by the plug-in local or remote Class<? extends Service> delegate = PluginUtil.isLocalService(serviceInfo) ? LocalService.class : RemoteService.class; Intent intent = new Intent(); intent.setClass(mPluginManager.getHostContext(), delegate); intent.putExtra(RemoteService.EXTRA_TARGET, target); intent.putExtra(RemoteService.EXTRA_COMMAND, command); // Notice this command. EXTRA_COMMAND_START_SERVICE Intent. PutExtra (RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation); . return intent; }Copy the code

If the intent is to start a Service, store information about the Service in the intent, and then start the trap Service. The EXTRA_COMMAND_START_SERVICE and EXTRA_COMMAND_STOP_SERVICE above are marked as different commands in the intent for different operations. The others are EXTRA_COMMAND_BIND_SERVICE and EXTRA_COMMAND_UNBIND_SERVICE.

Do I need to change the Activity back again just as I started the plug-in Activity? VirtualApk does not do this. Instead, it simply starts LocalService or RemoteService, but handles the EXTRA_COMMAND_START_SERVICE differently in the two services:

public class LocalService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { int command = intent.getIntExtra(EXTRA_COMMAND, 0); switch (command) { case EXTRA_COMMAND_START_SERVICE: { ActivityThread mainThread = ActivityThread.currentActivityThread(); IApplicationThread appThread = mainThread.getApplicationThread(); Service service; if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) { service = this.mPluginManager.getComponentsHandler().getService(component); } else {// Instantiate the plugin Service Service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance(); Application app = plugin.getApplication(); IBinder token = appThread.asBinder(); Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class); IActivityManager am = mPluginManager.getActivityManager(); attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am); service.onCreate(); / / cache Service, avoid duplication of instantiation enclosing mPluginManager. GetComponentsHandler () rememberService (component, Service); OnStartCommand (target, 0, 0); this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement()); break; }}}Copy the code

That is, it simulates the call of the life cycle method of the Service in the onStartCommand of LocalService. The specific life cycle method call steps can be reviewed before the source of the Service. For other StopServices, bindService is also emulated by LocalService in onStartCommand. For RemoteService, the process is the same as LocalService, but RemoteService runs in a different process.

Serivice VirtualApk: Serivice VirtualApk: Serivice VirtualApk: Serivice VirtualApk

  1. Plug-in ServiceIt’s all running onLocalServiceIn the. In fact, it doesn’t matter, it’s all running in the main process.
  2. Plug-in ServiceThe related life cycle methods areLocalServiceTo simulate the call (in the main thread).
  3. That is, the Service that is really running in the background isLocalService.

So from the source code, VirtualApk creates its own plug-in Service runtime model by relying on the system Service. Are there any disadvantages? Of course there are

  1. The Service lifecycle approach that you emulate is not a system after all and needs to be maintained as the system version changes
  2. No real support for openMulti-process Service. And the name of the remote Service changes, need to modify the source code.

Well, for VirtualApk plug-in Service management source code read here. Welcome to my Android advancement plan: github.com/SusionSuc/A…