1. 程式人生 > >【Android】Binder傳送檔案描述符分析

【Android】Binder傳送檔案描述符分析

在進行dumpsys呼叫的時候,dump方法的第一個引數是檔案描述符

BinderProxy.java

    publicvoid dump(FileDescriptor fd, String[] args) 

通過傳送檔案描述符來讓服務端向給定的檔案寫資料,

等等,仔細想想,好像有什麼不對

是啊,每個程序中的檔案描述符是獨立無關的,你把C程序中的檔案描述符傳給S程序,這不是刻舟求劍麼?

但是,Android的binder看上去就是傳送了檔案描述符,這裡面暗藏了什麼玄機呢?

我們在讀寫檔案描述符的地方加上log

{
    int dupFd = dup(fd);
    if
(
dupFd < 0) {
        return -errno;
    }
//在這裡新增log,檢視fd的值
ALOGE("writeDupFileDescriptor: fd = %d, dupfd =  %d\n", fd, dupFd);
    if (err) {
close(dupFd);
    }
    return err;
}


{
    if (
ALOGD("00 ========== android_os_Parcel_readFileDescriptor, fd=%d", fd);
        if (fd < 0) return
NULL;
fd = dup(fd);
ALOGD("========== android_os_Parcel_readFileDescriptor, fd=%d", fd);
        if (fd < 0) return NULL;
    }
    return NULL;
}

進行一次dump呼叫,檢視log

我們會發現,寫入的fd和服務端讀出的fd不一樣,但是服務端卻正確的完成了資料的輸出,為什麼呢?

肯定有地方進行了處理。

通過檢視,我們發現,原來是在資料傳輸的過程中被做了手腳,binder驅動在傳送資料的時候,在服務端上建立了一個新的fd,並且把這個fd和客戶端fd所對應的檔案進行了關聯。

處理流程

BinderProxy.java

    public void dump(FileDescriptor fd,String[] args) throws RemoteException {

        Parcel data = Parcel.obtain();

        Parcel reply = Parcel.obtain();

        data.writeFileDescriptor(fd);

        data.writeStringArray(args);

        try {

            transact(DUMP_TRANSACTION, data,reply, 0);

            reply.readException();

        } finally {

            data.recycle();

            reply.recycle();

        }

    }

呼叫到

Parcel.java

    public final void writeFileDescriptor(FileDescriptor val) {

        updateNativeSize(nativeWriteFileDescriptor(mNativePtr,val));

    }

frameworks/base/core/jni/android_util_Binder.cpp

staticjboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,

        jint code, jobject dataObj, jobjectreplyObj, jint flags) // throws RemoteException

{

frameworks/native/libs/binder/BpBinder.cpp

status_tBpBinder::transact(

    uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)

{

    // Once a binder has died, it will nevercome back to life.

    if (mAlive) {

        status_t status =IPCThreadState::self()->transact(

            mHandle, code, data, reply, flags);

./frameworks/native/libs/binder/IPCThreadState.cpp

status_tIPCThreadState::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_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(">>>> SENDfrom 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);

    }

… …

        if (reply) {

            err = waitForResponse(reply);

        } else {

            Parcel fakeReply;

            err =waitForResponse(&fakeReply);

        }

status_tIPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

    uint32_t cmd;

    int32_t err;

#ifdef_MTK_ENG_BUILD_

    cmd = 0;// initialze it for build error[-Werror=maybe-uninitialized]

#endif

    while (1) {

        if ((err=talkWithDriver()) < NO_ERROR) break;

        err = mIn.errorCheck();

        if (err < NO_ERROR) break;

        if (mIn.dataAvail() == 0) continue;

        cmd = (uint32_t)mIn.readInt32();

status_tIPCThreadState::talkWithDriver(bool doReceive)

{

        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ,&bwr) >= 0)

然後呼叫到binder驅動裡

最終,呼叫到方法binder_transaction

在裡面對fd進行了處理

kernel-3.18/drivers/staging/android/binder.c

static void binder_transaction(structbinder_proc *proc,

                               structbinder_thread *thread,

                               structbinder_transaction_data *tr, int reply)

{

… …

                case BINDER_TYPE_FD:{

                                int target_fd;

                                struct file *file;

                                if (reply) {

                                        if(!(in_reply_to->flags & TF_ACCEPT_FDS)) {

                                               binder_user_error

                                                    ("%d:%dgot reply with fd, %d, but target does not allow fds\n",

                                                    proc->pid, thread->pid, fp->handle);

                                               return_error = BR_FAILED_REPLY;

                                               goto err_fd_not_allowed;

                                        }

                                } else if(!target_node->accept_fds) {

                                       binder_user_error

                                           ("%d:%d got transaction with fd, %d, but target does not allowfds\n",

                                            proc->pid, thread->pid, fp->handle);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_fd_not_allowed;

                                }

//根據檔案描述符獲取到其對應的檔案資訊

//在某些Unix系統中,該方法名是getf

                                file =fget(fp->handle);

                                if (file ==NULL) {

                                       binder_user_error

                                           ("%d:%d got transaction with invalid fd, %d\n",

                                            proc->pid, thread->pid, fp->handle);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_fget_failed;

                                }

                                if(security_binder_transfer_file

                                   (proc->tsk, target_proc->tsk, file) < 0) {

                                       fput(file);

                                       return_error = BR_FAILED_REPLY;

                                        goto err_get_unused_fd_failed;

                                }

//在目標程序中獲取一個沒有使用的檔案描述符

                                target_fd =task_get_unused_fd_flags(target_proc, O_CLOEXEC);

                                if (target_fd< 0) {

                                        fput(file);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_get_unused_fd_failed;

                                }

//把目標程序中新建的fdfile資訊關聯起來,實現了程序間fd的複製

                               task_fd_install(target_proc, target_fd, file);

                               trace_binder_transaction_fd(t, fp->handle, target_fd);

                               binder_debug(BINDER_DEBUG_TRANSACTION,

                                             "        fd %d -> %d\n", fp->handle,target_fd);

                                /* TODO: fput?*/

                                fp->binder =0;

                                fp->handle = target_fd;

#ifdefBINDER_MONITOR

                                e->fd =target_fd;

#endif

                        }

                        break;


相關推薦

AndroidBinder傳送檔案描述分析

在進行dumpsys呼叫的時候,dump方法的第一個引數是檔案描述符 BinderProxy.java     publicvoid dump(FileDescriptor fd, String[] args)  通過傳送檔案描述符來讓服務端向給定的檔案寫資料, 等等,仔

Androiddumpsys為什麼要傳送檔案描述

 突然間想起一個問題:dumpsys為什麼要傳送檔案描述符 而不採用獲取返回字串,然後再進行列印呢?   考慮了下,發現傳送檔案描述符這個大招確實很妙。 1. dump列印的資訊有時候很大,而binder不適合傳送大量的資料,binder驅動中分配的空間也是有限的

android程序間傳遞檔案描述原理

在linux中,程序開啟一個檔案,返回一個整數的檔案描述符,然後就可以在這個檔案描述符上對該檔案進行操作。那麼檔案描述符和檔案到底是什麼關係?程序使用的是虛擬地址,不同程序間是地址隔離的,如何在兩個程序中傳遞檔案描述符,然後指向同一檔案(binder傳遞檔案描述符)? li

Android XML佈局檔案中,使用自定義屬性不提示和不生效

在XML檔案中使用首先要宣告 xmlns:toolbar=http://schemas.android.com/apk/res/cn.zzm.toolbar 注意,“toolbar”可以換成其他的任何名字,後面的url地址必須最後一部分必須用上自定義元件的包名。自定義屬性了

android各映象檔案img介紹

       Android 原始碼編譯後,在out/target/product/generic下生成的三個映象檔案:ramdisk.img,system.img,userdata.img以及它們對應的目錄root,system,data。 ramdisk.img是根檔案

android點選touch事件流程分析

1、onTouch和onTouchEvent的區別 public boolean dispatchTouchEvent(MotionEvent event) {         if (mOnTouchListener != null && mOnTouch

Java基礎:常見修飾(權限修飾以及abstract、static、final等)與變量的描述

線程 cte string 數據 執行 style 權限 實例 類名 1. 修飾符 public、protected、private、default abstract、static、final、 abstract:抽象類、抽象方法 static:靜態變量、

Android刪除已知路徑的檔案或資料夾

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80153517 【功能】 delete(String delFile):刪除檔案或資料夾 deleteSingleFile(String fil

Android複製assets裡的單檔案到指定資料夾

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80069811 本方法使用前提是已擁有許可權,未對許可權不足情況進行處理,如有需要可自行新增。 關於讀寫許可權的總結請參考:https://blog.csdn.n

Android系統Android檔案目錄結構

system/app:存放系統軟體 system/data:存放系統軟體的資料 data/app:存放使用者安裝的軟體 data/data:存放使用者軟體的資料 storage/emulated/0:儲存卡 storage/sdcard:虛擬路徑,快捷方式,實際

Android獲取手機中已安裝apk檔案資訊(PackageInfo、ResolveInfo)(應用圖片、應用名、包名等)

眾所周知,通過PackageManager可以獲取手機端已安裝的apk檔案的資訊,具體程式碼如下 PackageManager packageManager = this.getPackageManager();  List<PackageInfo> pac

Android檔案儲存-內部儲存

Table of Contents 檔案的操作模式 儲存資料  讀取資料   檔案的操作模式 MODE_PRIVATE:該檔案只能被當前程式讀寫 MODE_APPEND:該檔案的內容可以追加 MODE_WORLD_READABLE:可

Android專案中資料夾和檔案的作用

Table of Contents 資料夾的作用  檔案的作用    資料夾的作用  No. 資料夾 描述 1 src 存放

Androidapp打包成apk檔案以後,如何檢視VersionCode、VersionName等版本資訊

Android App打包成Apk後,其實是一個壓縮檔案,可以將字尾名apk改為zip然後用winrar開啟也能看到裡面的檔案結構。還能看到AndroidManifest.xml。但是裡面的內容經過編碼顯示為亂碼,不方便檢視。 aapt工具:

android簡易檔案管理器(列表式檔案目錄)

、    核心程式碼:                                 File fatherFile = new File(path); File[] files = fatherFile.listFiles();         效果圖:        

AndroidGreenDao操作外部DB資料庫檔案

1.背景 所謂外部資料庫檔案此處指的就是一個在外部單獨建立的db檔案,假設有這麼一個場景,我們專案中有一些本地資料,不需要介面去獲取的(不需要進行網路操作),寫死的資料,比如全國各個省各個市的一些基本

Androiddebug 狀態下其簽名檔案 debug.keystore 相關(如何獲得該檔案,其密碼,獲取其sha1、MD5等)

Eclipse,Android studio 編譯執行 APP 的時候是生成一個 apk 的,它預設的簽名是 debug.keystore 。 有時候我們需要拿到這個簽名檔案,下面就來說說它的預設路徑: Windows下: C:\Users\<使用者名稱>\.A

pythonopen函式檔案操作讀、寫和轉義‘\n’

1、open()語法 open(file[, mode[, buffering[, encoding[, errors[, newline[, closefd=True]]]]]]) open函式使用一個檔名作為唯一的強制引數,然後返回唯一的檔案物件。 o

Androidpull解析xml檔案+將資料儲存為xml格式,並儲存在記憶體裡

在解析中,常用到的還有一種解析就是pull去解析xml格式的檔案。事實上android內部也是這樣做的。今天這個demo是來自傳智播客,可能技術已經被翻新了。 但是基礎原理還是那樣,希望基礎學習者,能夠理解,並實際寫一寫。 首先在src目錄下匯入我們將要解析的xml檔案:

Android安卓佈局檔案中xmlns屬性

定義及使用 xmlns是XML Namespaces的縮寫,中文名稱是XML(標準通用標記語言的子集)名稱空間 自定義View的時候有時候會在佈局檔案中使用到 命名規則如下: xmlns:字首=http://shemas.android.com/ap