A series of introduction

Starting today, I will spend more time with you to learn about Android plug-ins. This article is the launch of the Android plug-in.

Android plugins have been a hot technology concept for the last few years. The technology has been in the works since 2012. From the rough AndroidDynamicLoader framework, to the first generation of DroidPlugin, etc., and then to the second generation of VirtualApk, Replugin, etc., and now to the VirtualApp, Atlas. With the gradual development and improvement of plug-ins in China, however, substitutes such as RN have emerged in recent years and gradually become weak.

Although the buzz around plug-in technology is over, there is still a lot of practice in the technology itself that helps us understand Android’s mechanics. Therefore, I will write a series of articles from this article, plus my own practice of plug-in, and finally analyze several excellent plug-in libraries from the perspective of source code, forming a complete set of theoretical system of plug-in.

The following is a technical framework for plugins, which is my idea for this series of articles,

Binder mechanism

There are many articles on the Internet analyzing Binder mechanism. In this article, I will not explain the use of Binder, but explain the design ideas, design principles and the use of plug-ins.

Why Binder is needed

First of all, we know that Android is based on the Linux kernel. For Linux, the operating system creates a fragment of memory for a binary executable containing the file’s own stack, heap, data map, and shared library, and assigns it a special internal management structure. This is a process. The operating system must provide a fair usage mechanism so that each process can start, execute, and end normally.

Now, that introduces a problem. Can a process manipulate data from another process? We can think about this, this is absolutely not to occur, especially system level process, if affected by other processes may cause the whole system collapse. So it was natural to think that we should isolate processes, and Linux does the same. Its virtual memory mechanism allocates contiguous memory space to each process, and processes can only operate on their own virtual memory space. At the same time, it must also meet the ability to maintain communication between processes. After all, unity is strong, and the independent operation of a single process cannot support the functional requirements of the operating system.

To solve this problem, Linux introduced the distinction between User Space and Kernel Space. The only way user space can access kernel space is through a system call. Kernel space interfaces application requests to the kernel for processing and back to the application. Also, if a user-space process wants to be upgraded to a kernel-space process, it needs to perform a security check.

Sidebar: System calls are made primarily through two methods:

  • Copy_from_user () : copies data from user space to kernel space

  • Copy_to_user () : copies data from kernel space to user space

This is the cross-process communication mechanism in Linux. Binder, which we’ll talk about in a minute, is one way to cross processes

Binder model

Binder is an interprocess communication (IPC) method. Common Android process communication methods include file sharing, Bundle, AIDL, Messenger, ContentProvider, and Socket. AIDL, Messenger, and ContentProvider are all based on Binder. Linux systems manage Binder mechanisms through Binder drivers.

Binder’s implementation is based on the Mmap () system call and only needs to be copied once, faster than regular file page operations. Wechat open source MMKV is also based on this. Those who are interested can know about it.

First, Binder mechanism has four participants: Server, Client two processes, ServiceManager, Binder driver (kernel space). ServiceManager and Binder drivers are implemented by the system, while Server and Client are implemented by developers themselves. Of the four, only Binder drivers run in kernel space.

The ServiceManager here acts as the Manager, responsible for the establishment of Binder communication, Binder registration and delivery capabilities. The Service creates a Binder, gives it a character name, and registers the Binder and name with the ServiceManager through the Binder driver, using the ServiceManager’s built-in Binder. Note that a Service and ServiceManager need a Binder for cross-process communication. ServerManager comes with a Binder, so a Service is a Client for a ServiceManager.

After the Service registers the Binder, the Client gets a reference to the Binder by name. The cross-process communication becomes Client and ServiceManager, and then the ServiceManager fetchthe Binder reference from the Binder table and sends it back to the Client, so that if there are multiple clients, it can return the reference multiple times. But in fact all references are services placed in the ServiceManager.

When clients communicate with services through Binder drivers, they often need to obtain an object of the Service. Binder returns object’s proxy proxyObject for security purposes, which has the same methods but no specific capabilities and only accepts parameters for use by the real Object.

So the complete Binder communication process is

OK, cross-process communication is clear. Now let’s talk about Binder in plug-in.

Binder and plug-in

Instrumentation calls ActivityManagerNative, which is our local object, and then AMN calls getDefault to get ActivityManagerProxy. This person, AMP, is the local representative of AMS. Equivalent to Client in the Binder model, this AMP inherits from IActivityManager and has all the methods that require AMS to participate in the four major components. AMS is indirectly called locally by calling this AMP method. Thus, we call AMS to start the Activity.

So how does AMS communicate with clients? Now that we’ve launched our Activity with a Launcher, we’re sure to tell you the word Launcher: “You’re done. Go to bed.” As you can see, the roles have changed. AMS needs to send messages and assume the role of Client, while the Launcher serves as a Service. This communication is also a Binder mechanism. AMS holds an ApplicationThreadProxy object that is the proxy for the ApplicationThread of the Launcher. AMS sends messages to App via ATP, which is processed by ApplicationThread.

The above is the application of Binder mechanism in Android. We will implement the plug-in through hook later.

conclusion

Binder may not be the best Binder Binder. I didn’t know what a Binder was for a long time. Then I came across this definition:

  • From the perspective of interprocess communication, Binder is an interprocess communication mechanism.

  • From the perspective of Server processes, Binder refers to the Binder entity objects in Server.

  • From a Client process perspective, a Binder refers to a remote proxy of a Binder entity object

  • From a transport process perspective, a Binder is an object that can be transferred across processes; Binder drivers automatically convert proxy objects to local objects with a little extra processing for objects that cross process boundaries.

2. This

Parental delegation model

There are three classLoaders in Java by default. Respectively is:

  • BootStrap ClassLoader: Starts the ClassLoader, the topmost loader. Responsible for loading core classes in the JDK. The Ext ClassLoader and App ClassLoader are constructed after the JVM is started.
  • Extension ClassLoader: the Extension ClassLoader is responsible for loading Java Extension class libraries.
  • App ClassLoader: system class loader, responsible for loading all jar and class files of an application.
  • Custom ClassLoader: Must inherit from the ClassLoader class.

The ClassLoader defaults to using the parent delegate model to search for classes. Each ClassLoader has a reference to its parent class. When the ClassLoader needs to load a Class, it checks whether it has been loaded. If so, it returns a Class object. Otherwise, it will be loaded by its parent class and continue to determine whether it has been loaded. In this way, the BootStrap ClassLoader at the top level tries to load. If you haven’t even loaded the top-level Bootstrap ClassLoader, load it. If the load fails, it is passed to the child ClassLoader and loaded layer by layer until the bottom layer. If you can’t load it, you throw an exception.

With this parental delegation model, the benefits are:

  • More efficient, the parent class can be loaded once can avoid the child class repeatedly loaded
  • More secure, preventing outsiders from forging Java core classes.

This in the Android

Android has been using art virtual machines since 5.0. These virtual machines also need a ClassLoader to load classes into memory while the program is running, but unlike Java, Java virtual machines load classes by reading class bytecode, whereas ART is loaded by dex bytecode. This is an optimization to merge multiple class files into a single classes.dex file.

There are three types of android loaders:

  • BootClassLoader: Parent class constructor

  • PathClassLoader: Generally loads apK in the specified path /data/app, that is, apK installed in the mobile phone. So it is generally used as the default loader.

  • DexClassLoader: a loader that loads classes from a JAR or APK containing classes.dex, which can be used for dynamic loading.

See PathClassLoader and DexClassLoader source code, are inherited from BaseDexClassLoader.

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
    super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}


public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null.null, parent);  / / see below}}public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(parent);  / / see below
        // Collect dex files and Native dynamic libraries [see Section 3.2]
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); }}Copy the code

We can see that both parameters of the PathClassLoader are null, indicating that only the fixed dex file can be accepted, which is only available after installation. In DexClassLoader, optimizedDirectory and librarySearchPath can be defined by themselves, indicating that we can pass in a JAR or APK package to ensure that a DEX file can be operated after decompression. Therefore, we usually use DexClassLoader for plugins and hot fixes.

As you can see, a fairly important part of BaseDexClassLoader is to initialize DexPathList. The process of initialization DexPathList mainly collecting dexElements and nativeLibraryPathElements. A Classloader can contain multiple dex files, and each dex file is packaged into an Element object. This Element object is important in initialization and hot repair logic. DexElements is iterated over when a class is found, returned if found, and continued otherwise. So when there is the same class in multiple dex, only the class in the preceding dex will be loaded. The following is an implementation of this logic

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if(dex ! =null) {
            // If the target class is found, return directly
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if(clazz ! =null) {
                returnclazz; }}}return null;
}
Copy the code

conclusion

We first explained the Java class loading parent delegation mechanism, and then introduced several Android classLoaders, from the source point of view of the two ClassLoader loading mechanism is different. In the future plug-in practice, we will use DexClassLoader frequently.

The resources

Binder interprocess Communication with Android

Binder principle analysis for Android application engineers

In-depth understanding of ClassLoader in Android

Android ClassLoader ClassLoader

I am Android stupid bird journey, a accompany you slowly stronger public number, welcome to pay attention to me to learn together, progress together ha ~