Android Binder-framework->native(transact->onTransact)

Posted by Ajita on Thu, 18 Jun 2020 03:26:45 +0200

Previous Binder mRemote's Past and Present PowerManger.isScreenOnThe calling process for () has already been called BinderProxy.transact();

/frameworks/base/core/java/android/os/Binder.java

------> Binder.java->BinderProxy
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }

    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

BinderProxy's transact method calls the JNI method transactNative method;
android_util_Binder.cpp

------> android_util_Binder.cpp
static const JNINativeMethod gBinderProxyMethods[] = {
     /* name, signature, funcPtr */
    {"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},
    {"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},
    {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
    {"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
    {"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
    {"destroy",             "()V", (void*)android_os_BinderProxy_destroy},
};

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    if (dataObj == NULL) {
        jniThrowNullPointerException(env, NULL);
        return JNI_FALSE;
    }

    Parcel* data = parcelForJavaObject(env, dataObj);//Converting Java-tier Parcel object data to native Parcel
    if (data == NULL) {
        return JNI_FALSE;
    }
    Parcel* reply = parcelForJavaObject(env, replyObj);
    if (reply == NULL && replyObj != NULL) {
        return JNI_FALSE;
    }

    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);//Core Core,
                            //Remember hereGBinderProxyOffsets.mObjectAre you?If you go to the previous article and search, you will find that
        //Env->SetLongField(object,GBinderProxyOffsets.mObject, (jlong)Val.get(); value is a BpBinder object obtained from Native
        //setLongField is the mObject object that saves the native BpBinder object in the Java layer BinderProxy.
        //getLongField is a native BpBinder object taken from the mObject object object of BinderProxy; therefore, a target is a BpBinder object;
        //Many system classes of practical JNI do this so that they don't have to go to the bottom every time
    if (target == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
        return JNI_FALSE;
    }

    //printf("Transact from Java code to %p sending: ", target); data->print();
    status_t err = target->transact(code, *data, reply, flags);//Core core, calling the transact method of the target,
    //That is, call the transact method of BpBinder;
    //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
#if ENABLE_BINDER_SAMPLE
    if (time_binder_calls) {
        conditionally_log_binder_call(start_millis, target, code);
    }
#endif
    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
    return JNI_FALSE;
}

Here IBinder* target is the mObject variable of the Java-tier BinderProxy class, that is, the BpBinder object saved by SetLongField was called in the javaObjectForIBinder method in the previous article; the next step is to call the transact method of BpBinder:

/frameworks/native/libs/binder/BpBinder.cpp

------> BpBinder.cpp
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);//Core Core Core,
            //Here mHandle is the handle** referenced by the corresponding remote service**
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

This is simply a call to IPCThreadState::self()->transact, where mHandle is assigned when the BpBinder object is initialized, that is, we used readStrongBinder to call getStrongProxyForHandle (flat->handle) in the service process before; this mHandle is used to identify a reference to this service.
/frameworks/native/libs/binder/IPCThreadState.cpp

------> IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();//data verification
    flags |= TF_ACCEPT_FDS;
    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }    
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
        //Convert data to binder_transaction_data
    }    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }    
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);//Core Core Core
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif   
        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }    
    return err;

From the first Binder to now, I have written so much in one breath that I almost forgot if I wrote it correctly. This is a bad habit. Here I use the Log method to verify that the above explanation is correct?

 
 
Log.png

For the logs I added, paste the code directly:
APP:

                String TAG = "Bindertest MainActivity";
                Log.e(TAG,"App begin nativeCall");
                boolean bool = powerManager.isScreenOn();
                Log.e(TAG,"App end nativeCall");

Binder.java->BinderProxy class

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        Log.e("BinderProxy.java  transact","Keiven-Chen");//Print Log added by oneself
        return transactNative(code, data, reply, flags);
    }

BpBinder.cpp

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        ALOGE("Keiven-Chen-BpBinder.cpp transact");//Print Log added by oneself
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;
    if (getpid()==g_nTargetPid)//Judgment Conditions
    ALOGE("Keiven-Chen_IPCThread IPCThreadState::transact()");//Print Log you added yourself, using getpid()==g_nTargetPid filter condition,
    //Because IPCThreadState::transact is frequently invoked by many processes in the system, it will produce a lot of Log output without filtering, which is not easy to trace and analyze.
    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }
......
}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
    ......
    if(getpid()==g_nTargetPid)//Judgment Conditions
    ALOGE("Keiven-Chen IPCThreadState::writeTransactionData()  tr.data_size = %d, offsets_size = %d, 
        target.handle = %d, data.ipcObjects() = %d, ipcObjectsCount() = %d",
        tr.data_size, tr.offsets_size, tr.target.handle, data.ipcObjects(), data.ipcObjectsCount());
    //Print Log added by oneself with filter process number getpid()==g_Log of nTargetPid (because the writeTransactionData method is called frequently by the system);
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}

The logs I added here are based on the mRemote trace in the previous article, and the logs indicate that the previous analysis should be OK.

It is important to be clear that when Binder communication calls the IPCThreadState:: the transact method, it is not cross-process, but is still in the Client process; the getpid() method in the code above is used to get the current Client process ID (PID), then g_Where does nTargetPid come from???g_nTargetPid is the Client Process Number (PID) that I call IPCThreadState in Client and then record in IPCThreadState; what method of calling IPCThreadState can record this PID, certainly not every Client calls by default, but only in my Client?This certainly requires us to be there by ourselves IPCThreadState.cpp Add a specific method to record this g_nTargetPid, which is also very simple, is to give g_nTargetPid assignment (g_nTargetPid = getpid(), so that when my client enters IPCThreadState again:: transact, it can compare g_The value of nTargetPid and getpid() to determine if it is my client process; this was called in the previous articleProcessInfo.nativeSelfCall() to implement, nativeSelfCall is a JNI that calls IPCThreadState::selfCall() in its implementation and records the implementation g_in the selfCall() methodNTargetPid = getpid() will be able to record our Client process ID;

APP section

        findViewById(R.id.mybtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ProcessInfo processInfo = new ProcessInfo();
                processInfo.nativeSelfCall();//JNI Call IPCThreadState.cpp SelfCall of
                PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
                Log.e(TAG,"App begin nativeCall");
                boolean bool = powerManager.isScreenOn();
                Log.e(TAG,"App end nativeCall");
                Log.e(TAG,"" + bool);
            }
        });

ProcessInfo.java The full package name must be com.example.bindservice.ProcessInfo.java
public class ProcessInfo {
    static {
        System.loadLibrary("jnidemo");
    }
    public native void nativeSelfCall();

}

JNI Part

jni It can be created in many places in the system, and I put it here /frameworks/native/libs Under Catalog:
------> /frameworks/native/libs/jnidemo/ProcessInfo.cpp

static void JNI_nativeSelfCall(JNIEnv* env, jobject thiz)//Implement JNI method
{
    ALOGE("Keiven-Chen JNI_nativeSelfCall 111");//Log
    IPCThreadState::self()->selfCall();//Core call, call IPCThreadState.cpp Self-implemented methods in
    ALOGE("Keiven-Chen JNI_nativeSelfCall 222");
}

static JNINativeMethod gMethods[] = {  
    {"nativeSelfCall", "()V", (void*)JNI_nativeSelfCall}, //Binding JNI Method
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* reserved) {
    JNIEnv* env = NULL;  
    jint result = -1;  

    if ((jvm)->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    {
        return -1;
    }

    jclass clazz = (env)->FindClass("com/example/bindservice/ProcessInfo");//Binding Java classes,
    //nativeSelfCall must be declared in this full class name, which specifies the Java class that uses the JNI, so my ProcessInfo must have the above package name.
    if (clazz)
    {
        if((env)->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0)//Register JNI
            ALOGE("Keiven-Chen RegisterNatives natives NOT ok");
        else
            ALOGE("Keiven-Chen RegisterNatives natives ok");
    }
    else
        ALOGE("Keiven-Chen could not find class");

    result = JNI_VERSION_1_4;
    return result; 
}

Compile JNI scripts

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS    := -lm -llog
LOCAL_MODULE := libjnidemo //generateJnidemo.solibrary

LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libbinder

LOCAL_SRC_FILES := ProcessInfo.cpp
include $(BUILD_SHARED_LIBRARY)

Here you can see that JNI is actually very simple: it calls IPCThreadState:: self()->selfCall();, need to go IPCThreadState.cpp Implement selfCall method in; JNI knowledge can be found in previous articles JNI/NDK

IPCThreadState.cpp

------> IPCThreadState.cpp
void IPCThreadState::selfCall()
{
    ALOGE("Keiven-Chen_IPCThread IPCThreadState::selfCall() 111 pid = %d, size of bwr is %d", 
                                                          getpid(), sizeof(binder_write_read));
    ioctl(mProcess->mDriverFD, 123456, NULL);//This passes command data to the Binder driver to track the kernel driver, and so on.
    g_nTargetPid = getpid();/Here is g_nTargetPid Assignment to save the current process ID,Follow through the process ID To filter LOG
    ALOGE("Keiven-Chen_IPCThread IPCThreadState::selfCall() 222");
}

Depending on the logic in the application Activity, the process of executing the program is to execute the JNI call firstIPCThreadState.cppSelfCall, which executes the method first, gets my application process number (PID) g_nTargetPid = getpid()=4385, based on this PID inIPCThreadState.cppOther cross-process calls to the transact method Log are filtered out in; here is a complete Log screenshot, calling selfCall before isScreenOn.

 
 
log.png

Here, for how the APP layer calls to IPCThreadState.cpp The transacts of IPCThreadState should be clear, and follow up from the transacts of IPCThreadState;
IPCThreadState:: The core of the transact method is to call the waitForResponse method:

------> IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;
    if (getpid()==g_nTargetPid)
    ALOGE("Keiven-Chen_IPCThread IPCThreadState::transact()");

  ......

        if (reply) {
            err = waitForResponse(reply);//Core Call
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
   ......
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
    //If((g_NTargetPid>0) & (getpid()==g_NTargetPid)//Add Log for filtering
    //ALOGE("11111 IPCThreadState::waitForResponse() mOut.data %d", *(int*)(mOut.data()));
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; //Core calls, and driver communication
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
        cmd = mIn.readInt32();//Get Binder Driver Return Command from mIn

        switch (cmd) {   //Perform different operations depending on the return value of the driver
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd); //Core call to handle drive return cmd
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
......

The core of waitForResponse is to call talk WithDriver, which actually deals with the driver; waitForResponse also needs to process the driver return value and perform other operations based on the cmd returned by the Binder driver.

------> IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }    
    binder_write_read bwr;    
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();    
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();//mOut data is initialized in the previous writeTransactionData, and bwr is the structure used to communicate with the Binder driver.
    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;//No read/write data

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(HAVE_ANDROID_OS)
        //if(getpid()==g_nTargetPid)
        //    ALOGE("Keiven-Chen IPCThreadState::talkWithDriver() now into ioctl");
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) //Communicate with Binder Driver through continuous read and write operations in ioctl
            err = NO_ERROR;
        else
            err = -errno;
        //if(getpid()==g_nTargetPid)
        //    ALOGE("Keiven-Chenkai IPCThreadState::talkWithDriver() now out of ioctl");
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);
    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
     
        return NO_ERROR;
    }
    
    return err;
}

Binder_Write_The read structure, which is used to exchange data with Binder devices and communicates with mDriverFD through ioctl, is a real process of data read-write interaction with Binder drivers.

The core of talk WithDriver is to call IOCTL to communicate with mDriverFD. How does this IOCTL work?IOCTL is actually a SysCall, here we record the calling process of this SysCall of ioctl;

IPCThreadState.cpp
ioctl(#include <sys/ioctl.h>)
=====>
ioctl.h(bionic/libc/include/sys/ioctl.h)
__BEGIN_DECLS
extern int ioctl(int, int, ...);
__END_DECLS
======>
bionic/ioctl.cpp(/bionic/libc/bionic/ioctl.c)
#include <stdarg.h>
extern int __ioctl(int, int, void *);
int ioctl(int fd, int request, ...)
{
    va_list ap;
    void * arg;

    va_start(ap, request);
    arg = va_arg(ap, void *);
    va_end(ap);
    return __ioctl(fd, request, arg);
}
======>
/bionic/libc/arch-arm/syscalls/__ioctl.S
#include <private/bionic_asm.h>
ENTRY(__ioctl)
    mov     ip, r7
    ldr     r7, =__NR_ioctl //_u NR_ioctl is the system call number of ioctl
    swi     #0 //Soft interrupt command
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(__ioctl)

_uNR_IOCTL is a macro defined in / kernel/include/uapi/asm-generic/unistd.h

------> unistd.h
/* fs/ioctl.c */ //Describe sys_ioctl is implemented in this file
#define __NR_ioctl 29
__SC_COMP(__NR_ioctl, sys_ioctl, compat_sys_ioctl)
------> kernel/arch/arm/kernel/calls.S
        CALL(sys_ni_syscall)        /* was sys_lock */
        CALL(sys_ioctl) //Call sys_ioctl
------> /kernel/include/linux/syscalls.h

#define SYSCALL_DEFINE0(sname)                  \
    SYSCALL_METADATA(_##sname, 0);              \
    asmlinkage long sys_##sname(void)

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, sname, ...)              \
    SYSCALL_METADATA(sname, x, __VA_ARGS__)         \
    __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...)                 \
    asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));  \  //Our sys_ioctl is expanded from here
    static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));  \
    asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))   \
    {                               \
        long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));  \
        __MAP(x,__SC_TEST,__VA_ARGS__);             \
        __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));   \
        return ret;                     \
    }                               \
    SYSCALL_ALIAS(sys##name, SyS##name);                \
    static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd,
                unsigned long arg);

asmlinkage is a gcc tag, meaning that the parameters read by the function come from the stack, not from registers.
From the code above, we know that our IOCTL is via SYSCALL_Defined by DEFINE3, in kernel/fs/ioctl.c;

------> ioctl.c

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
    int error;
    struct fd f = fdget(fd);//We are in IPCThreadState.cpp MDriverFD passed in, on behalf of/dev/binder device

    if (!f.file)
        return -EBADF;
    error = security_file_ioctl(f.file, cmd, arg);
    if (!error)
        error = do_vfs_ioctl(f.file, fd, cmd, arg);
    fdput(f);
    return error;
}

Here we know how our ioctl will be called, and here the complete call process for ioctl is as follows:
ioctl()→do_vfs_ioctl()→vfs_ioctl()→f_op->unlocked_ioctl()->binder_ioctl()

------> /kernel/drivers/staging/android/binder.c
static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "binder",//Register binder device as miscellaneous device/dev/binder
    .fops = &binder_fops
};

This clears up IPCThreadState.cpp How ioctl in drives binder.c to binder_ioctl;

There are countless gods in the Binder driver layer, such as Lao Luo and Yuan Shen; for the sake of the integrity of the article, a brief mention of the Binder kernel-driven processing process; the above mentioned talkWithDriver method passes BINDER_WRITE_READ cmd is given to the kernel, which operates on the driver based on this cmd; the core is to call binder_based on whether there is data and Binder driver interactionThread_Write or binder_thread_read method; binder_Thread_Bider_is called in the write methodTransaction method handles CMD as BC_TRANSACTION and BC_REPLY; binder_transaction returns BR_based on the result of the processingXxx, the Server side waitForResponse will be based on BR_xxx is processed differently;

------>/kernel/drivers/staging/android/binder.c   binder_ioctl

    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) {
            ret = -EINVAL;
            goto err;
        }
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        binder_debug(BINDER_DEBUG_READ_WRITE,
                 "%d:%d write %lld at %016llx, read %lld at %016llx\n",
                 proc->pid, thread->pid,
                 (u64)bwr.write_size, (u64)bwr.write_buffer,
                 (u64)bwr.read_size, (u64)bwr.read_buffer);

        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
            trace_binder_write_done(ret);
            if (ret < 0) {
                bwr.read_consumed = 0;
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            trace_binder_read_done(ret);
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            if (ret < 0) {
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }

Client encapsulates handle, cmd, data, code, etc. into binder_transaction_data, encapsulated in binder_write_read structure, call ioctl and drive interaction, drive call binder_thread_write and binder_thread_read handles related transactions;IPCThreadState.cpp: BC_TRANSACTION - > binder.c:binder_ioctl - ->IPCThreadState.cpp: BR_TRANSACTION

The following figure draws on Yuan Shen:

 
binder_transaction.jpg
------> IPCThreadState.cpp  waitForResponse ---> executeCommand 
switch (cmd) {
            ......
      case  BR_TRANSACTION:
            ......
             if (tr.target.ptr) {
                sp<BBinder> b((BBinder*)tr.cookie);//Here the BBinder object b, the cookie field holds the Binder object
                ////Core Core
                ALOGE("Keiven-Chen_IPCThread IPCThreadState::BR_TRANSACTIO tr.target.ptr=true , %d \n",tr.target.ptr);
                error = b->transact(tr.code, buffer, &reply, tr.flags);//Call the transact method of the BBinder object;

              } else {
                ALOGE("Keiven-Chen_IPCThread IPCThreadState::BR_TRANSACTIO tr.target.ptr=false  \n");
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }
}

BR_TRANSACTION By Server End processing, so this has been switched to Server Process; subsequent processes are all in Server Processing in process;

    Log.png

The Server process calls BBinder's transact method. BBinder's transact calls onTransact method. Where BBinder is implemented is in JavaBBinder, so JavaBBinder's onTransact method is ultimately called and JavaBBinder is defined in android_Util_Binder.cppMedium;

------> Binder.cpp class BBinder
status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);
    ALOGE("Keiven-Chen_IPCThread BBinder::transact \n");
    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            err = onTransact(code, data, reply, flags);//Here onTransact is implemented by a subclass, calling the onTransact method of the subclass
            break;
    }
    if (reply != NULL) {
        reply->setDataPosition(0);
    }
    return err;
}
------> android_util_Binder.cpp
class JavaBBinder : public BBinder

virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
        JNIEnv* env = javavm_to_jnienv(mVM);
        IPCThreadState* thread_state = IPCThreadState::self();
   
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,//Core Core,
                          //call Binder.java From this method to the onTransact method of the Java layer Stub
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);  

        if (thread_state->getStrictModePolicy() != strict_policy_before) {
            set_dalvik_blockguard_policy(env, strict_policy_before);
        }
        // Need to always call through the native implementation of
        // SYSPROPS_TRANSACTION.
        if (code == SYSPROPS_TRANSACTION) {
            BBinder::onTransact(code, data, reply, flags);
        }
    }

Looking at this, I'm really questioning why we must follow JavaBBinder's onTransact method, which starts with the registration service.

------> ServiceManagerNative.java   class ServiceManagerProxy
public void addService(String name, IBinder service, boolean allowIsolated)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);//Descriptors written by each service here are fixed
        //This is where Parcel writes, and I'm at Android_in Parcel JNI Os_Parcel_Calling android_in writeInterfaceTokenUtil_Binder.cppTesString method in
        data.writeString(name);
        data.writeStrongBinder(service);//service written here
        data.writeInt(allowIsolated ? 1 : 0);
        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
        reply.recycle();
        data.recycle();
    }
------> android_os_Parcel.cpp
static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr,
                                                  jstring name)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        // In the current implementation, the token is just the serialized interface name that
        // the caller expects to be invoking
        const jchar* str = env->GetStringCritical(name, 0);
        if (str != NULL) {
            testString(String16(str, env->GetStringLength(name)));//Call android_Util_Binder.cppThe way you do it yourself,
            //Pass name to android_util_Binder is used to determine the ibinderForJavaObject internal calling procedure
            parcel->writeInterfaceToken(String16(str, env->GetStringLength(name)));
            env->ReleaseStringCritical(name, str);
        }
    }
}
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
            //ibinderForJavaObject method in android_Util_Binder.javaMedium implementation
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}
------> android_util_Binder.cpp

const char *namePower = NULL;
void testString(const String16& str){//Self-added method, called when writing InterfaceToken is registered, to facilitate Log filtering
     namePower = String8(str).string();
     ALOGE("kaikaikaichenchen %s \n", namePower);
}

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
       if (strcmp(namePower, "android.os.IPowerManager") == 0)
        {
            ALOGE("kaikaikaiChen: Binder  %p",jbh);
            ALOGE("kaikaikaiChen: Binder hahahahaha");//Log tags for easy tracking, Log as shown below
            ALOGE("kaikaikaiChen: Binder    %p ", obj);
        }
        return jbh != NULL ? jbh->get(env, obj) : NULL;//Calling the get method of JavaBBinderHolder,
      //That is to create JavaBBinder, which is where you decide to register IPowerManager
    }
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        if (strcmp(namePower, "android.os.IPowerManager") == 0)
        ALOGE("kaikaikaiChen: BinderProxy    %p ", obj);//Judging by Log what value this returns
        return (IBinder*)
            env->GetLongField(obj, gBinderProxyOffsets.mObject);
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}
 
log.png

To sum up: So the onTransact method of JavaBBinder is called here, and the onTransact of JavaBBinder is called here Binder.java The execTransact, execTransact method calls the onTransact method (the onTransact method is implemented by a subclass, where the onTransact method of the subclass is called),IPowerManger.StubInherits from Binder, implements the onTransact method, so it will eventually be calledIPowerManger.StubFor the onTransact method of Binder, the complete calling process is as follows. The following figure refers to Yuan Shen and the network god:

 
Binder process.png
 
binder_ipc_process.jpg

The two pictures above illustrate Binder's main line.

After all this padding, we PowerManger.isScreenOn() The call process will go to IPowerManager.Stub OnTransact method, onTransact executes different methods based on code, where isScreenOn's code is TRANSACTION_isInteractive:

------> IPowerManager.java class Stub
                case TRANSACTION_isInteractive: {
                    data.enforceInterface(DESCRIPTOR);
                    boolean _result = this.isInteractive();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }

public boolean isInteractive() throws android.os.RemoteException;

PowerManagerService inherits from IPowerManager.Stub That is, the isInteractive method isPowerManagerService.javaIs implemented in, which is called herePowerManagerService.javaIsInteractive; this explains the whole process from Client-side transact->Binder driver->Server-side onTransact, to this end of a complete Binder Call;

Many of the smart hardware require Native Service. Recommend one Chloe_Zhang's Nuative Service

The Native Service implementation steps are as follows:
1. Implement an interface file, IXXXService, inherit IInterface
2. Define BnXXX, inherit BnInterface<IXXXService>.Implements a XXXService, inherits BnXXX, and implements the onTransact() function.
3. Define BpXXX, inherit BpInterface<IXXXService>.

Topics: Mobile Java Android jvm Linux