This article is Zhang Tao on the Android plug-in series of the latest Service, for the convenience of everyone to read, today also synchronous push his earlier one [8 classes to solve the plug-in — Activity implementation scheme], we can go to understand.
The cover is the rainbow of Beijing today, from my circle of friends 🙂
This article focuses on Android plug-in development, how to run not installed apK Service. Unlike the solution I described two years ago (running services that are not installed in APK), the solution implemented this time is completely open-ended and the plug-in APK can be a completely standalone application without special syntax changes.
Review images
Service loading process
As with the dynamic loading principle of an Activity, we need to start and load the Service first. The main process is as follows:
A Service starts like an Activity and eventually calls a method inside ActivityManagerService.
-
StartServiceLocked () security monitoring;
-
After security verification is complete, the scheduleCreateService() is ready to create the Service
-
ScheduleServiceArgs () is called to send messages
-
The messages sent by Handle will eventually be processed in ActivityThread.callback.
After defining the entire process for starting a Service, we find that although the process is very similar to the Activity startup process described in the previous article, we cannot use the same method for activities, because the Instrumentation class is not used at all.
And just like in the Activity, there is no way to override the startServiceLocked() method to tamper with the system validation because it runs in another system process, system_server.
A final problem is that unlike an Activity, a Service can start multiple instances, and the onCreate() method is never called again after the same Service has been executed.
Replace the system Service creation process
Although there is no way to create a Service through Instrumentation, there are ways to replace the system creation process.
In the last activityThread. Callback step, Handle sends a number of message types, including: CREATE_SERVICE, SERVICE_ARGS, BIND_SERVICE, etc… Not only the creation of the service, but also the Activity’s lifecycle methods are invoked in this callback.
CREATE_SERVICE calls a method called handleCreateService(CreateServiceData data) with the main code:
Review images
As you can see, the Service is also a normal class, where the system comes out new and executes its onCreate() method.
So we can do our own Service creation logic by replacing the callback class and modifying its logic if the message is CREATE_SERVICE.
If the service being loaded is a plug-in, the service will replace the ClassLoader with the plug-in ClassLoader, and the loaded class will follow the process of the original host service. The attach() and onCreate() methods are all called manually.
Instead, reflection is used to find mH in the original ActivityThread class.
Review images
I really want to make fun of the Android source code, which is full of various weird names. For example, mH is actually a Handle, but its class name is only one letter, a capital H, so its object is called mH. Then there’s an ActivityInfo variable called aInfo, and then there’s an ApplicationInfo object called aInfo, and every now and then there’s an AI, and you don’t know what it is and you have to go back and find its type.
OK, back to the point, after the callback is replaced, creating the Service can be performed by our own methods. The problem is that onCreate is not called more than once, so we also need to change the logic of handleMessage(). If it is SERVICE_ARGS or BIND_SERVICE, we need to check first. If the plug-in service is passed in that it has not been created, then you need to run the handleCreateService() method again to create it.
Review images
Step on pit and climb pit
If you implement the whole plugin along the same lines, you’ll notice two huge pitfalls:
-
The plug-in service is created, but if more than one plug-in service is started, none of the other plug-in service lifecycle methods are called except onCreate() of the last one started.
-
The plug-in Service does not call the onDestroy() method.
Let’s start with the first problem, the lifecycle approach. As mentioned earlier, each lifecycle method is actually handled through this Handle as well. Services = mservices.get (data.token); Services = mservices.get (data.token); All created services are added to a map (HashMap before 4.0, ArrayMap after 4.0), from which they are read based on keys or tokens when needed. Life cycle methods are not called.
HandleCreateService () : mservices.put (data.token, service); That’s what it was for. It also explains why other services do not call lifecycle methods because map values are overwritten. So simple, this key value token we create and add to it.
The second trap, onDestroy(), was not executed. After repeated testing, it was found that the message with STOP_SERVICE identifier was not sent. The solution is also very simple, since the system did not send, then manually send this message on the line.
Find the source of all messages, ActivityManagerService, and it’s easy to replace our focus with a dynamic proxy.
Find two methods related to destroy called stopServiceToken() and unbindService(). While both methods are executing, call doServiceDestroy() to manually send the message yourself. And then when the other side receives this message execute the plug-in onDestroy()
Review images
Resources are loaded dynamically with so files
In this way, the dynamic loading of activities and services in the uninstalled APK is resolved. As a review, there are only six classes in total, so why eight classes to handle the plug-in? There are two classes for handling the dynamic loading of resources and so files.
So file first, in fact, DexClassLoader native support dynamic loading, but why we passed solib did not load, or because of permissions. SD cards on Android phones do not have executable permission, so we must copy the so file to the application package storage area, either getFilesDir() or getCacheDir() are directories with executable permission. When constructing the plug-in DexClassLoader, pass the third parameter to a path with executable permissions.
Resources are even simpler, because we only need to load an APK dynamically, so there is no plugin resource conflict involved at all, just one method: view the image
At the end
No tidbits at the end ~ so simple 8 classes, do you have any questions?
Baichuan.taobao.com is the wireless open platform of Alibaba Group. Through the opening of “technology, business and big data”, baichuan.taobao.com provides high cohesion, open, industry-leading technology product matrix, mature business components and perfect service system in mobile scenes. Help mobile developers quickly build apps, accelerate the process of APP commercialization, and empower mobile developers and mobile entrepreneurs in an all-round way.
                                                Â
     About Alibaichuan     Review images
Click [read article] for more highlights!