Android Binder 的996.icu

本文详细介绍了Android中的Binder机制,包括Binder的由来、特性及其在进程间通信中的作用。从不同层级解析Binder的工作原理,如Binder的组成、架构及通信原理等。

一、主要内容

Binder在Android中有举足轻重的地位,不管是Activty的启动流程,还是四大组件的通讯方式都一一和binder有着细密的关联。 主要从四个大方向介绍Binder:

  • Binder由来和特性:首先是对Binder有个完整的认识和他存在的优势分析
  • Linux下IPC原理:Android是基于Linux的,所以先了解下Linux固有的进程通讯的方式有助于对Binder的理解
  • Binder原理:主要包括组件的构成、架构和通讯原理
  • Binder有关的常见问题:一些常见的和不常见的有关Binder的问题有助于更好的理解和加深对Binder的认识

二、Binder概述

1.什么是Binder?

  • 从IPC角度来说:Binder是Android中的一种跨进程通信方式,该通信方式在linux中没有,是Android独有;
  • 从Android Driver层:Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder;
  • 从Android Native层:Binder是创建Service Manager以及BpBinder/BBinder模型,搭建与binder驱动的桥梁;
  • 从Android Framework层:Binder是各种Manager(ActivityManager、WindowManager等)和相应xxxManagerService的桥梁;
  • 从Android APP层:Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的 Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

2.由来?

Android 系统是基于 Linux 内核的,Linux 已经提供了管道、消息队列、共享内存和 Socket 等 IPC 机制。那为什么 Android 还要提供 Binder 来实现 IPC 呢?主要是基于性能、稳定性和安全性几方面的原因。

  • 管道:任何进程都能通讯,但速度慢
  • 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
  • 共享内存:能够容易控制容量,速度快,但要保持同步,比如写一个进程的时候,另一个进程要注意读写的问题,相当于线程中的线程安全。当然,共享内存同样可以作为线程通讯,不过没有这个必要,线程间本来就已经共享了同一个进程内的一块内存。
  • socket:本机进程之间可以利用socket通信,跨进程之间也可利用socket通信,通常RPC的实现最底层都是通过socket通信。socket通信是一种比较复杂的通信方式,通常客户端需要开启单独的监听线程来接受从服务端发过来的数据,客户端线程发送数据给服务端,如果需要等待服务端的响应,并通过监听线程接受数据,需要进行同步,是一件很麻烦的事情。socket通信速度也不快。

3.Binder特性?

3.1 性能?

  • 共享内存虽然无需拷贝,但控制复杂,难以使用。
  • Binder 只需要一次数据拷贝,性能上仅次于共享内存。
  • Socket 作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。
  • 消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。
3.2 稳定性?
  • Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。
  • 共享内存虽然无需拷贝,但是控制负责,难以使用。从稳定性的角度讲,Binder 机制是优于内存共享的。
3.3 安全性?
  • 首先传统的 IPC 接收方无法获得对方可靠的进程用户ID/进程ID(UID/PID),从而无法鉴别对方身份。
  • Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。
  • 同时 Binder 既支持实名 Binder,又支持匿名 Binder,安全性高(Binder.getCallingUid())。
3.4 结论:

三、Linux下IPC原理

1.进程隔离

  • 进程与进程间内存是不共享的。两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。
  • A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。

2.用户空间与内核空间

  • 每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。
  • 简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(User Space)是用户程序运行的空间。为了保证安全性,它们之间是隔离的。

3.用户态与内核态

  • 虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?

  • 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(内核态)此时处理器处于特权级最高的(0级)内核代码中执行。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。处理器在特权等级高的时候才能执行那些特权CPU指令。

  • 系统调用主要通过如下两个函数来实现:

    copy_from_user() //将数据从用户空间拷贝到内核空间
    copy_to_user() //将数据从内核空间拷贝到用户空间
复制代码

四、Binder原理

4.1 Binder组成

Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。

  • Client

    如同互联网中客户端(Client),Binder是Server本地对象的一个引用,这个引用实际上是一个代理对象,Client通过这个代理对象来间接访问Server的本地对象;

  • Server

    如同互联网中服务器(Server),Binder是提供具体实现的本地对象,需向ServiceManager注册;

  • ServiceManager

    如同互联网中DNS域名服务器(ServiceManager),管理Servive注册和查询,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址意外还有自己的网址。

  • 驱动

    如同互联网中路由器(Binder 驱动),是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

4.2 Binder架构

Binder架构采用分层架构设计,每一层都有其不同的功能,以大家平时用的startService为例子,AMP为ActivityManagerProxy,AMS为ActivityManagerSerivce。BpBinder是Client端创建的用于向Server发送消息的代理,而BBinder是Server端用于接受消息的通道。

  • Binder 通信采用 C/S 架构,从组件视角来说,包含 Client、 Server、 ServiceManager 以及 Binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。
  • Binder 在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构。
  • Native 层: 对于Native层,如果需要使用Binder,则可以直接使用BpBinder和BBinder(也包括JavaBBindder)即可,对于上一层Java IPC通信也是基于这个层面。
  • Kernel物理层:这里是Binder Driver,前面三层都跑在用户控件,对于用户控件内存资源是不共享的,每个Android的进程只能运行在自己基础讷航所拥有的虚拟地址空间,而内核空间却是可共享的。真正通信的核心环节还是Binder Driver。

4.3 Binder机制通讯原理

  • Server向ServiceManager注册Server通过Binder驱动向ServiceManager注册,声明可以对外提供服务。ServiceManager中会保留一份映射表。
  • Client向ServiceManager请求Server的Binder查询Client想要请求Server的数据时,需要先通过Binder驱动向ServiceManager请求Server的Binder引用:我要向名字为zhangsan的Server通信,请告诉我Server的Binder引用。
  • 向具体的Server发送请求Client拿到这个Binder引用后,就可以通过Binder驱动和Server进行通信了。
  • Server返回结果Server响应请求后,需要再次通过Binder驱动将结果返回给Client。

4.4 总结

  • 从进程间通信的角度看,Binder 是一种进程间通信的机制;
  • 从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
  • 从 Client 进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理
  • 从传输过程的角度看,Binder 是一个可以跨进程传输的对象;Binder 驱动会对这个跨越进程边界的对象对一点点特殊处理,自动完成代理对象和本地对象之间的转换。

五、Binder有关问题

转载于:https://juejin.im/post/5c9dc6995188251d7d02689c

anr问题trace如下: "main" prio=5 tid=1 Blocked | group="main" sCount=1 ucsCount=0 flags=1 obj=0x72df6468 self=0xb400007072878c00 | sysTid=3104 nice=-2 cgrp=ssfg sched=0/0 handle=0x71230e9d28 | state=S schedstat=( 273294290029 918944300434 897810 ) utm=18065 stm=9263 core=5 HZ=100 | stack=0x7fe106e000-0x7fe1070000 stackSize=8188KB | held mutexes= at com.android.server.alarm.AlarmManagerService$DeliveryTracker.onSendFinished(AlarmManagerService.java:5798) - waiting to lock <0x09677b19> (a java.lang.Object) held by thread 281 at android.app.PendingIntent$FinishedDispatcher.run(PendingIntent.java:382) at android.os.Handler.handleCallback(Handler.java:973) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:282) at android.os.Looper.loop(Looper.java:387) at com.android.server.SystemServer.run(SystemServer.java:1069) at com.android.server.SystemServer.main(SystemServer.java:677) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:983) DumpLatencyMs: 212.411 "binder:3104_E" prio=5 tid=281 Runnable | group="main" sCount=0 ucsCount=0 flags=0 obj=0x1770d568 self=0xb400006e2db00800 | sysTid=4606 nice=-2 cgrp=ssfg sched=1073741824/0 handle=0x6e0b1ab730 | state=R schedstat=( 113789554601 690500424244 405734 ) utm=6620 stm=4758 core=5 HZ=100 | stack=0x6e0b0b4000-0x6e0b0b6000 stackSize=989KB | held mutexes= "mutator lock"(shared held) at android.icu.util.Currency.getAvailableCurrencyCodes(Currency.java:200) at libcore.icu.ICU.getCurrencyCode(ICU.java:671) at java.util.Currency.getInstance(Currency.java:421) at java.text.DecimalFormatSymbols.initializeCurrency(DecimalFormatSymbols.java:933) at java.text.DecimalFormatSymbols.setCurrency(DecimalFormatSymbols.java:508) at java.text.DecimalFormatSymbols.fromIcuInstance(DecimalFormatSymbols.java:1085) at java.text.DecimalFormat.getDecimalFormatSymbols(DecimalFormat.java:2819) at java.text.SimpleDateFormat.zeroPaddingNumber(SimpleDateFormat.java:1678) at java.text.SimpleDateFormat.subFormat(SimpleDateFormat.java:1593) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:1117) at java.text.SimpleDateFormat.format(SimpleDateFormat.java:1087) at java.text.DateFormat.format(DateFormat.java:333) at java.text.Format.format(Format.java:159) at com.android.server.alarm.AlarmManagerService.setImplLocked(AlarmManagerService.java:2473) at com.android.server.alarm.AlarmManagerService.setImpl(AlarmManagerService.java:2425) - locked <0x09677b19> (a java.lang.Object) at com.android.server.alarm.AlarmManagerService$4.set(AlarmManagerService.java:3103) at android.app.IAlarmManager$Stub.onTransact(IAlarmManager.java:198) at android.os.Binder.execTransactInternal(Binder.java:1523) at android.os.Binder.execTransact(Binder.java:1457) DumpLatencyMs: 84.4358这最后分析是什么结果呢
最新发布
08-20
<think>用户现在遇到了一个SecurityException,提示需要android.permission.WRITE_SECURE_SETTINGS权限。这个问题通常发生在尝试修改系统级设置时,而应用没有相应的权限。 首先,我需要确认用户是否在正确的上下文中执行这些adb命令。通常,通过adb执行这些命令需要设备具有root权限,或者应用已经获取了系统级权限。用户可能是在没有root的设备上运行,或者应用没有签名所需的权限。 接下来,我应该考虑可能的解决方案。比如,用户是否可以通过授予应用WRITE_SECURE_SETTINGS权限来解决这个问题?这可能需要使用adb命令来授予权限,例如:adb shell pm grant <package_name> android.permission.WRITE_SECURE_SETTINGS。但这里需要注意的是,普通应用无法获取这个权限,除非它们是系统应用或者设备已经被root。 另外,用户可能是在使用Appium或Selenium进行自动化测试时遇到这个问题。这时候,可能需要检查测试框架的配置,确保使用的驱动版本兼容,或者尝试切换不同的驱动,比如UiAutomator2,看看是否能避免权限问题。 还有一种可能是设备的Android版本较新,对权限的管理更加严格。用户可能需要调整测试策略,比如使用更早的Android版本进行测试,或者寻找替代方法来修改隐藏API策略,而不需要直接操作settings数据库。 最后,用户可能需要检查设备是否已经正确解锁,并且USB调试模式已经启用,同时确认开发者选项中的相关设置是否允许通过USB进行调试。如果这些基本配置有问题,也可能导致权限不足的错误。</think>要解决`SecurityException: Permission denial`错误,需按以下步骤处理: 1. **授予WRITE_SECURE_SETTINGS权限** 执行命令将权限赋予测试进程: ```bash adb shell pm grant <your_app_package> android.permission.WRITE_SECURE_SETTINGS ``` (需替换`<your_app_package>`为实际应用包名,如`com.example.app`) 2. **使用系统级签名APK** 若权限仍不生效,需重新打包测试APK并使用系统签名(需设备已root或通过OEM解锁): ```bash jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore debug.keystore app-debug.apk androiddebugkey ``` 3. **切换测试驱动至UiAutomator2** 在Appium/Selenium配置中指定`automationName`为`UiAutomator2`: ```python desired_caps = { "automationName": "UiAutomator2", "platformName": "Android", # 其他必要配置 } ``` 4. **降级Android版本测试** 若设备支持,尝试在Android 9.0及以下版本测试,这些版本对隐藏API限制较宽松。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值