AIDL原理和相关文件解析

2023-11-10

Binder概述

  相信从事Android相关的研发人员,都对Binder有个或多或少的了解,相关技术博客也有一大推,我今天对Binder的学习过程进行一个记录,理论性的叙述会少一点,更多的是基于AS自动生成的AIDL文件进行代码分析。但读者事先应该对Binder的Server、Client、ServerManager和Binder驱动要有一定的了解。

AIDL代码简单分析

接口总览

  看过《Android开发艺术探索》的同行应该记得,有个经典的BookManager的Demo,如下:

public interface BookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements android.tplink.sjj.aidltest.BookManager {
        private static final java.lang.String DESCRIPTOR = "android.tplink.sjj.aidltest.BookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an android.tplink.sjj.aidltest.BookManager interface,
         * generating a proxy if needed.
         */
        public static android.tplink.sjj.aidltest.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof android.tplink.sjj.aidltest.BookManager))) {
                return ((android.tplink.sjj.aidltest.BookManager) iin);
            }
            return new android.tplink.sjj.aidltest.BookManager.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_getBooks: {
                data.enforceInterface(descriptor);
                java.util.List<android.tplink.sjj.aidltest.Book> _result = this.getBooks();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(descriptor);
                android.tplink.sjj.aidltest.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = android.tplink.sjj.aidltest.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
            }
        }

        private static class Proxy implements android.tplink.sjj.aidltest.BookManager {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public java.util.List<android.tplink.sjj.aidltest.Book> getBooks() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<android.tplink.sjj.aidltest.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(android.tplink.sjj.aidltest.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(android.tplink.sjj.aidltest.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public java.util.List<android.tplink.sjj.aidltest.Book> getBooks() throws android.os.RemoteException;

    public void addBook(android.tplink.sjj.aidltest.Book book) throws android.os.RemoteException;
}

  简单的说,通过AIDL,AS构建了统一的跨进程通信接口和相应的Binder,其内容如下:

public interface BookManager extends android.os.IInterface {

	public static abstract class Stub extends android.os.Binder implements android.tplink.sjj.aidltest.BookManager
    public java.util.List<android.tplink.sjj.aidltest.Book> getBooks() throws android.os.RemoteException;
    public void addBook(android.tplink.sjj.aidltest.Book book) throws android.os.RemoteException;
}

  标准化后的AIDL继承了IInterface这个系统接口,内部有一个binder子类和我们定义的两个接口。

Stub文件

    public static abstract class Stub extends android.os.Binder implements android.tplink.sjj.aidltest.BookManager {
        private static final java.lang.String DESCRIPTOR = "android.tplink.sjj.aidltest.BookManager";
        
        public Stub() { ...... }

        public static android.tplink.sjj.aidltest.BookManager asInterface(android.os.IBinder obj) { ...... }

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

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { ......}

        private static class Proxy implements android.tplink.sjj.aidltest.BookManager { ...... }

        static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

Stub内部结构

Stub描述符

  Stub继承了系统的Binder类并且实现了我们自定义的接口,继承了系统Binder类,就可以走系统那一套,可以将自己注册到ServerManger中,其中有自己的一个引用和一个唯一的描述符:

						DESCRIPTOR = "android.tplink.sjj.aidltest.BookManager";

  这样客户端请求服务端时,就可以用这个描述符进行检索并判断客户端和服务端是否在同一进程内,具体代码后面分析;实现的接口中有我们自定义的方法,这个方法通过Binder机制完成跨进程调用。
  Stub构造函数就是构造该Binder子类,其中应该涉及到将自己注册到ServerManager的过程(具体待验证);
  asInterface()方法代码如下,我们结合具体代码分析它的功能:

	public static android.tplink.sjj.aidltest.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof android.tplink.sjj.aidltest.BookManager))) {
                return ((android.tplink.sjj.aidltest.BookManager) iin);
            }
            return new android.tplink.sjj.aidltest.BookManager.Stub.Proxy(obj);
        }

  该方法传入的是一个Binder接口,方法先判空,非空情况下,在本地池(C/S同一个进程情况)中检索该服务,用的就是该binder的描述符,如果检索到,则直接返回该引用,说明传入的参数就是客户端所需的服务的引用;如果没有在本地检索到,说明这是一个跨进程的调用,返回该binder的一个代理Proxy。
  asBinder返回自身,一般是在服务端(四大组件Service的bindService方法中返回Binder的引用),该引用作为客户端调用服务端的凭据。

Stub内部类Proxy

  Proxy类是Stub类的内部类,Stub通过它可以完成远程代理。Proxy对客户透明,就如同其他系统服务AMS、WMS、PMS和UMS等等,也是通过代理为客户端(app进程)提供服务。实现代码如下:

	private static class Proxy implements android.tplink.sjj.aidltest.BookManager {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public java.util.List<android.tplink.sjj.aidltest.Book> getBooks() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<android.tplink.sjj.aidltest.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(android.tplink.sjj.aidltest.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(android.tplink.sjj.aidltest.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

  Proxy利用binder接口中的transact方法,将序列化后的客户端请求参数传送到服务端,并把服务端的数据用同样的方法解析出来返回给客户端,具体需要依赖底层binder驱动。

Stub中的接口描述符

  可以看到Proxy调用_mRemote.tranact()方法时第一个参数是Stub.TRANSACTION_addBook,它和binder描述符类似,也是一个描述符,只不过它是标识调用的方法,服务端接收到Proxy的请求后,通过该参数路由到具体的方法,我们定义的方法有两个,它们的描述符如下:

		static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

看过AMS源码的同行应该很熟悉这些代码,在IActivityManager中有大量的方法描述符。

Stub中的onTransact方法

  然后再看看Stub的onTransact中:

        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_getBooks: {
                data.enforceInterface(descriptor);
                java.util.List<android.tplink.sjj.aidltest.Book> _result = this.getBooks();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(descriptor);
                android.tplink.sjj.aidltest.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = android.tplink.sjj.aidltest.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
            }
        }

可以看到,onTransact中的操作和Proxy中的transact是一一对应的,那边发出的请求这边根据binder描述符合接口描述符进行处理,有返回数据时,就写入reply这个数据结构中,依赖底层binder传递给客户端。

AIDL调用流程

  根据上面分析,binder跨进程调用通过Proxy实现,Stub和Proxy都实现了我们定义的接口,所有客户端调用都一一对应到服务端,底层的一切包括代理都对客户没有影响,客户端就好像调用本地方法一样。我们传输的参数对象需要序列化,具体可以查一下Parcel接口和原理相关知识。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AIDL原理和相关文件解析 的相关文章

随机推荐

  • Web Spider 常见混淆EVAL、AA、JJ、JSFUCK

    文章目录 一 资源推荐 二 EVAL constructor 三 AA混淆 四 JJ混淆 五 JSFUCK 六 针对混淆的方法 简单hook 总结 混淆的原理 就是把原本清晰的代码故意搞得花里胡哨 让局外人看不懂 混淆的目的 让逆向者很难理
  • Python中使用input()无输入

    Python中使用input 无输入 sublime Text编译器因页面美观 插件众多 功能强大深受编程爱好者的青睐 然而在编程中由于插件忘记安装导致程序运行失败 使人颇为烦恼 问题描述 在sublime Text编译器中使用input
  • Git 如何压缩 commit

    今天同事突然问我 由于在给老大的开源项目提 pr 的时候 自己比较长时间没有 rebase 的老大的项目 master 分支了 而自己提交的 commit 又很多 有些 commit 又是实验性质的 乱七八糟的 其实就是没有用熟 git 还
  • Java工作流框架:探索流程引擎的实现和应用

    目前 市面上有很多基于SpringBoot Vue前后端分离的Java快速开发框架和工作流开发框架可供选择 以下是一些比较流行的框架 1 Spring Cloud Spring Cloud是一套基于Spring Boot的开发工具 用于快速
  • sqli-labs Less18 原理到实现详解

    目录 前期知识储备 一 初始思路 1 思路 2 user agent后加入 发现出现sql错误日志 3 我尝试了之前用的各种闭合方法 都已失败告终 4 新的问题 5 现在想的是如何利用mysql插入语句insert实现注入 前期知识储备 从
  • 【Qt styleSheet样式表】

    一 Qt styleSheet样式表语法 1 属性列表 下表列出了 Qt 样式表支持的所有属性 可以为属性赋予哪些值取决于属性类型 除非另有说明 否则以下属性适用于所有小部件 标有星号 的属性为Qt特定的 在 CSS2 或 CSS3 中没有
  • Python+PySide2:使用多线程处理界面卡死的情况

    本篇主要是梳理一下 在使用 Pyside2 模块的时候 利用多线程处理页面假死 exe未响应 的问题 一 这边举个例子吧 如下图所示 测试代码如下 class Stats def init self 从文件中加载UI定义 super ini
  • 32位 与64位编译

    为了适应现在越来越流行的64位系统 经常需要将代码分别编译为32位版和64位版 其次 除了需要生成debug版用于开发测试外 还需要生成release版用于发布 本文介绍了如何利用makefile条件编译来生成这些版本 而且不仅兼容Linu
  • 1、python调用java的jar包

    在python中调用jar包 最近的项目功能需要调用客户的java接口 在调用接口的时候需要使用配套的jar包生成一些参数 但是公司的项目是用django搭建的 经过无数血与泪的尝试 最终终于找到了合适的方法去调用 jpype包是一个Pyt
  • 解决dubbo问题:forbid consumer

    原文地址 http www jameswxx com e4 b8 ad e9 97 b4 e4 bb b6 e8 a7 a3 e5 86 b3dubbo e9 97 ae e9 a2 98 ef bc 9aforbid consumer 线
  • Win10环境python3.7安装dlib模块

    自己按照别人的博客安装了一下dlib 测试成功 在这里自己也稍微总结一下 博客原文 https blog csdn net zhang475546259 article details 84104368 1 安装VS201x 全称visua
  • 简单支付验证-SPV

    SPV的来源 在比特币整个生态圈里 大部分都是普通用户 即只有基本的比特币投资及消费支付需要的用户 他们可能没有矿机 没有高端配置的电脑 那么他们是否也要运行一个全节点程序呢 要知道 现在官方显示的结果 比特币所有区块数据目前在120G左右
  • webstorm等JetBrains全家桶搜索任何插件都无反应

    使用webstorm搜索任何插件都无反应 打开webstorm设置 搜索settings或者是System Settings 找到HTTP Proxy 设置AUTO detect proxy settings后 点击右下角的Apply应用后
  • vue 中provide的用法_Vue中使用provide和inject

    相信大家在工作中一定遇到过多层嵌套组件 而vue 的组件数据通信方式又有很多种 比如vuex dollar parent与 dollar children prop dollar emit与 dollar on dollar attrs与
  • 为什么需要对数值类型的特征做归一化?

    为什么需要对数值类型的特征做归一化 1 举例子 比如分析一个人的身高和体重对健康的影响 身高的单位是m 范围是1 6 1 8 体重的单位是kg 在50kg 100kg之间 分析出的结果自然会倾向于数值差异较大的体重特征 因此我们需要数值归一
  • 新版Spring Boot(10)- Spring Boot 整合数据持久层(1)

    1 整合JdbcTemplate Service Description TODO Author tzb Date 2021 8 22 10 23 Version 1 0 Service public class UserService A
  • 【Mysql】删除表记录,并限制条数

    删除表数据 好删 那如果要限制条数 如何删除呢 例如 有个表tag list 我要删除aid为6666的前100条数据 sql如下 删除表记录limit限制条数 delete from tag list where aid 6666 lim
  • JUC 六. 线程中断 与 LockSupport

    目录 一 基础理解 如何退出一个线程 volatile 与 AtomicBoolean 中断线程示例 Thread中自带的中断api示例 阻塞状态线程中断时异常解决 二 Thread中自带的中断底层分析 三 总结 一 基础理解 先了解几个问
  • 打印九九口诀表(pta练习题)

    下面是一个完整的下三角九九口诀表 本题要求对任意给定的一位正整数N 输出从1 1到N N的部分口诀表 输入格式 输入在一行中给出一个正整数N 1 N 9 输出格式 输出下三角N N部分口诀表 其中等号右边数字占4位 左对齐 include
  • AIDL原理和相关文件解析

    Binder概述 相信从事Android相关的研发人员 都对Binder有个或多或少的了解 相关技术博客也有一大推 我今天对Binder的学习过程进行一个记录 理论性的叙述会少一点 更多的是基于AS自动生成的AIDL文件进行代码分析 但读者