Binder Mechanism for Detailed Decomposition and Principle of Plug-in Knowledge

Keywords: Android Java github

Recently, I have been researching plug-in things. I have seen that most of the things on the Internet are to explain the principles directly and then write a demo. This is not a good understanding for many friends who have no introduction. Now I will sort out and explain the knowledge points needed by plug-in step by step through my own research process. In fact, the benefits of learning plug-in are not complete. Because it is a popular technology, plug-in involves a lot of knowledge points, so we can have a qualitative leap in understanding and realm of Android. After I have roughly explained all the knowledge points of design, I will use a demo to achieve plug-in, which will design all the knowledge mentioned.

Plugging is actually dynamic loading, which includes code loading and resource loading.

What can we do?

Plug-in first appeared because of the 65535 problem, which was used to check multiple dex and load dex dynamically to prevent 65535 problem.

Now many companies use plug-in to do dynamic loading of modules, which can not only achieve the same updates on the web side at any time, but also reduce the size of the APK package. In fact, it is loading an uninstalled apk.

Thermal repair, thermal repair is also the principle of dynamic loading.

Skin change, using dynamic resource loading can achieve skin change function

You can also do some bad things you want to do through some classes of hook system.

At present, the well-known plug-in framework:

Ren Yugang's: dynamic-load-apk, this project uses a proxy approach to achieve
https://github.com/singwhatiwanna/dynamic-load-apk

360: Droid Plugin, which is implemented through hook system classes
https://github.com/Qihoo360/DroidPlugin

At present, the thermal repair framework of comparative fire is as follows:

Ali's: andfix, for fixing methods, can take effect immediately and does not support resource and class substitution
https://github.com/alibaba/AndFix

Tencent's: tinker, in addition to not supporting immediate entry into force, all support
https://github.com/Tencent/tinker

American League: robust, no source

If plug-in is to be used as modularity, two issues need to be addressed

Code loading is to load code using ClassLoader
Loading of resources, using the hidden method of AssetManager, addAsssetPath method adds a Resource path to obtain the Resource resources of this Resource
Another problem is the life cycle management of the four components.
Get ready:

Knowledge points that need to be understood and mastered before understanding plug-in
I. Binder mechanism
2. Agency model.
Reflection
IV. Class Loading and dex Loading
V. Application Startup Process and Class Loading Process
6. Realizing plug-in complete demo and thinking analysis
7. Dynamic loading of resources and resolving resource conflicts

Binder mechanism:

In fact, Binder looks at how you understand it. If it's a class from the code point of view, if it's a driver from the hardware point of view, if it's a communication mechanism from the IPC point of view, it's a link bridge between various service managers at the framework level.
We know that the system service objects we usually use are actually systems. The process of their existence and our application are not in the same process, but why can we use them directly? In fact, it is because of the existence of Binder, cross-process communication, and to put it plainly, we often use the aidl, Binder is very complex, here is just to pave the way for plug-in. For further understanding, please consult the information yourself.

Interprocess communication process

1. First, the client should link to the server.
2. The server then returns a client's object (proxy object)
3. When the client uses the method in the proxy object, the system calls the method of the server first, and then returns the result of the operation to the client.
We write an aidl by ourselves and compare it with the source code of the system.

// our own aidl interface
//IMyAidlInterface.aidl
package com.huanju.chajianhuatest;
import com.huanju.chajianhuatest.aidlmode.TestBean;

interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);

 String getS(in TestBean s);

 TestBean getInfoBean(out TestBean b);

}
The system will automatically create an IMyAidlInterface.Java file for us to see.

>

package com.huanju.chajianhuatest;

public interface IMyAidlInterface extends android.os.IInterface {

public static abstract class Stub extends android.os.Binder implements com.huanju.chajianhuatest.IMyAidlInterface {
    private static final java.lang.String DESCRIPTOR = "com.huanju.chajianhuatest.IMyAidlInterface";


    public Stub() {

        this.attachInterface(this, DESCRIPTOR);
    }

    public static com.huanju.chajianhuatest.IMyAidlInterface asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.huanju.chajianhuatest.IMyAidlInterface))) {
            return ((com.huanju.chajianhuatest.IMyAidlInterface) iin);
        }
        return new com.huanju.chajianhuatest.IMyAidlInterface.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
        return this;
    }

    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_basicTypes: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                long _arg1;
                _arg1 = data.readLong();
                boolean _arg2;
                _arg2 = (0 != data.readInt());
                float _arg3;
                _arg3 = data.readFloat();
                double _arg4;
                _arg4 = data.readDouble();
                java.lang.String _arg5;
                _arg5 = data.readString();
                this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_getS: {
                data.enforceInterface(DESCRIPTOR);
                com.huanju.chajianhuatest.aidlmode.TestBean _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                java.lang.String _result = this.getS(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            case TRANSACTION_getInfoBean: {
                data.enforceInterface(DESCRIPTOR);
                com.huanju.chajianhuatest.aidlmode.TestBean _arg0;
                _arg0 = new com.huanju.chajianhuatest.aidlmode.TestBean();
                com.huanju.chajianhuatest.aidlmode.TestBean _result = this.getInfoBean(_arg0);
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                if ((_arg0 != null)) {
                    reply.writeInt(1);
                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    private static class Proxy implements com.huanju.chajianhuatest.IMyAidlInterface {
        private android.os.IBinder mRemote;

        Proxy(android.os.IBinder remote) {
            mRemote = remote;
        }

        @Override
        public android.os.IBinder asBinder() {
            return mRemote;
        }

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }


        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeInt(anInt);
                _data.writeLong(aLong);
                _data.writeInt(((aBoolean) ? (1) : (0)));
                _data.writeFloat(aFloat);
                _data.writeDouble(aDouble);
                _data.writeString(aString);
                mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                _reply.readException();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }

        @Override
        public java.lang.String getS(com.huanju.chajianhuatest.aidlmode.TestBean s) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.lang.String _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((s != null)) {
                    _data.writeInt(1);
                    s.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }
                mRemote.transact(Stub.TRANSACTION_getS, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            com.huanju.chajianhuatest.aidlmode.TestBean _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getInfoBean, _data, _reply, 0);
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    _result = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(_reply);
                } else {
                    _result = null;
                }
                if ((0 != _reply.readInt())) {
                    b.readFromParcel(_reply);
                }
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }

    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getS = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_getInfoBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}


public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

public java.lang.String getS(com.huanju.chajianhuatest.aidlmode.TestBean s) throws android.os.RemoteException;

public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException;

}
It looks like a lot of code, but it's nothing. Let's analyze the structure.

1. Based on the above code, the class we created inherits the IInterface interface

2. Internal class Stub inherits Binder

3. Look at the asInterface method to determine if a process does not return to the proxy class

4. Each method corresponds to an id, which is used to determine which method is accessed when accessing across processes. The onTransact method on the server side is invoked through the transact method.

Let's look at the classes of the system and see the most familiar Activity Manager. To know that Activity Manager is actually a proxy wrapping class of Activity Manager Service in our process. It uses Activity Manager Native. getDefault () to operate the process internally. So let's look directly at some code of Activity Manager Native.

1.asInterface
/* {@hide} /
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
/**
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*/
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}

    return new ActivityManagerProxy(obj);
}

2.onTransact
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
int startFlags = data.readInt();
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
int result = startActivity(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
reply.writeNoException();
reply.writeInt(result);
return true;
}
If there is too much code, let's just look at these two. Do you feel familiar with them? It's almost the same as our own aidl. Although he's called Activity Manager Native, in fact, he's the inner class Stub in our own aidl.

Now let's analyze the running process of aidl

Let's look directly at the way the proxy object is returned if it's remote. Let's look at the method of the proxy object. This method is the method defined by the aidl result and see how it is implemented.
>

@Override
public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.huanju.chajianhuatest.aidlmode.TestBean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getInfoBean, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
b.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
Client Initiates Request

1. First create the output type data
2. Create the accept type reply
3. Create the required parameters
4. Write parameters into data
5. Initiate a remote call, and the current thread suspends calling the mRemote.transact() method. This method is implemented in Binder. He calls the onTransact method on the server side until the result is returned.
6. Return the result from _result
When the server receives a request, it goes to the onTransact method, which runs in the server's Binder thread pool. Let's see how it works.

>

case TRANSACTION_getInfoBean: {
data.enforceInterface(DESCRIPTOR);
com.huanju.chajianhuatest.aidlmode.TestBean _arg0;
_arg0 = new com.huanju.chajianhuatest.aidlmode.TestBean();
com.huanju.chajianhuatest.aidlmode.TestBean _result = this.getInfoBean(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
1. Identify which method to access by id
2. Then extract the required parameters from the target parameter data
3. Then call the corresponding method of the request
4. Write the return value to reply
5. Returning to true, we need to say that if returning false represents the failure of client access, we can actually do remote verification based on this feature. After all, our remote method does not want anyone to access it.
Which method to access is determined by id, then the required parameters are extracted from the target parameter data, and the corresponding method is called to write the return value to reply.
Well, the communication process of Binder is over here. In fact, we can see that as long as we understand the process and principle of aidl written by ourselves, so is the communication at the system level. In the next article, we will continue to talk about proxy mode and reflection.

Links to the original text http://blog.csdn.net/yulong0809/article/details/56841993

Posted by chiaki*misu on Thu, 27 Jun 2019 14:15:40 -0700