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.