Qual é o Limite Máximo de Dados em uma Comunicação Binder do Android?

Na comunicação Binder do Android, um meecanismo essencial de IPC, o tamanho máximo dos dados transmissíveis em uma única operação é frequentemente citado como 1MB-8KB. No entanto, essa afirmação requer uma análise mais detalhada das fontes e configurações subjacentes.

A limitação de 1MB-8KB origina-se da classe ProcessState no framework, que define um tamanho de buffer virtual padrão. Ao inicializar o Binder em um processo, ela utiliza uma macro para calcular o limite.

#define LIMITE_BINDER_VM ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)

ProcessState::ProcessState(const char *driver) {
    if (mDriverFD >= 0) {
        mVMStart = mmap(0, LIMITE_BINDER_VM, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            ALOGE("Falha ao mapear memória para transações Binder: %s\n", mDriverName.c_str());
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }
}

Processos comuns de aplicativos são originados do Zygote, que inicializa o Binder com ProcessState, herdando assim o limite de 1MB-8KB. Para superar essa restrição, é possível inicializar o Binder manualmente, sem usar ProcessState, realizando diretamente chamadas open e mmap no driver.

int main(int argc, char **argv) {
    struct binder_state *bs;
    bs = abrir_binder("/dev/binder", 128 * 1024); // Altere o valor para aumentar o limite
    if (!bs) {
        fprintf(stderr, "falha ao abrir o driver binder\n");
        return -1;
    }
    // Restante do código...
}

A função abrir_binder (originalmente binder_open) encapsula as chamadas de sistema.

struct binder_state *abrir_binder(const char* driver, size_t tamanho_mapeamento) {
    // Código omitido para brevidade
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    bs->tamanho_mapeamento = tamanho_mapeamento;
    bs->mapped = mmap(NULL, tamanho_mapeamento, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    // Restante do código...
}

No entento, o driver Binder impõe um limite físico de 4MB na função mmap, independentemente do valor solicitado. Se o tamanho exceder 4MB, o driver ajusta automaticamente para esse máximo.

static int binder_mmap(struct file *filp, struct vm_area_struct *vma) {
    int ret;
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer;

    if (proc->tsk != current)
        return -EINVAL;

    if ((vma->vm_end - vma->vm_start) > TAM_4M) // TAM_4M definido como 4MB
        vma->vm_end = vma->vm_start + TAM_4M;
    // Restante do código...
}

Além disso, há uma restrição específica para chamadas assíncronas (oneway). Durante a alocação de buffer, o espaço livre para operações assíncronas é limitado à metade do tamanho total do buffer mapeado, priorizando chamadas síncronas.

static struct binder_buffer *alocar_buffer_binder(struct binder_proc *proc,
                          size_t tamanho_dados,
                          size_t tamanho_offsets, int eh_assincrono) {
    // Código omitido para brevidade
    if (eh_assincrono &&
        proc->espaco_async_livre < tamanho + sizeof(struct binder_buffer)) {
        // Falha: espaço assíncrono insuficiente
        return NULL;
    }
    // Restante do código...
}

Portanto, os limites efetivos são: até 4MB para inicialziação manual do Binder e 1MB-8KB ao usar ProcessState, com chamadas assíncronas restritas a metade do buffer. Embora seja possível contornar o limite de 1MB-8KB em aplicativos via JNI, isso pode causar instabilidade, já que muitas interações críticas no Android dependem do Binder. A modificação direta do mapeamento deve ser evitada em contextos de produção.

Tags: Binder android IPC mmap processstate

Publicado em 6-19 01:20