原文地址:http://www.androiddesignpatterns.com/2013/07/binders-window-tokens.html

安卓的一项核心设计思想是希望能提供一个不须要依赖中央检验部门来检验程序请求的开放平台.为此,Android使用了程序沙盒和Linux的进程分离来防止程序以无法控制和不安全的方式訪问系统内部或者其它程序.这样的架构是开发人员与使用者共同选择的:既不须要额外的保护来防止恶意程序,同一时候系统要自己主动的处理好全部事情.
在非常长一段时间我对这样的安全机制都是知其然却不知其所以然,可是近期我開始好奇了起来.究竟是什么机制能防止我们欺骗系统,比如使我们不能通过一个程序来隐藏还有一个程序的界面?简单来说,Android的核心系统服务怎样既高效又安全地响应第三方应用程序的请求的呢?

出乎我意料的是,这全部问题的答案都异常简单:是Binder.Binders是Android系统架构的基石;他们为开发人员抽象了IPC底层的很多细节,使程序能简单地与系统服务或者其它远程服务组件对话.并且Binder还有更多非常cool的功能,所以它在系统中被广泛的使用,贯穿整个系统,使底层framework能解决好安全问题.这篇文章将具体解读这些功能中的当中一种,Binder 令牌(tokens).


Binder 令牌(Tokens)

binder有一个有趣的属性:不管跨越了多少个进程,每一个实例在整个系统中都仅仅维护同一个唯一的ID.这是由Binder内核驱动通过分析每一个Binder的transaction后为其分配的一个32位int值.为了保证Java中的"=="操作能适用于Binder的唯一性与跨进程的对象身份约定,Binder对象的引用的推断相对于其它对象有一些不同.准确的讲,每一个Binder对象引用都是由下面两者之中的一个分配的:
  1. 同一个进程中指向一个Binder对象的虚拟内存地址,或
  2. 一个唯一的32位句柄(由Binder内核驱动分配的)指向不同进程中Binder的虚拟内存地址.
Binder内核驱动中为每个Binder都维护了一个本地地址与远程Binder句柄的Map(反之亦然),然后为每个Binder对象的引用都分配了一个合适的值,保证他们在远程进程中也能相同的按预期工作.
Binder的唯一对象ID规则使它们有了一项特殊的用途:共享,安全訪问令牌(shared,security access token)(文档中明白提示了Binder能够被这样使用"你能够简单的实例化一个原始的Binder对象直接用于跨进程共享").Binders是全局唯一的,这意味着你生成了一个,没有其它不论什么人能生成还有一个全然同样的.因此,系统的frameword广泛地使用Binder令牌来保证跨进程交互的安全性:一个client能够通过创建一个Binder对象作为令牌与服务进程共享,而且服务端能使用它验证来自client的请求排除全部伪造请求.

我们来通过一个简单的样例看看它是怎样工作的.假如一个程序向PowerManager发出了一个请求,请求获得屏幕唤醒锁(后面会释放掉):

/**
* An example activity that acquires a wake lock in onCreate()
* and releases it in onDestroy().
*/
public class MyActivity extends Activity { private PowerManager.WakeLock wakeLock; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wakeLock.acquire();
} @Override
protected void onDestroy() {
super.onDestroy(); wakeLock.release();
}
}

阅读PowerManager的源代码能够帮助我们理解底层发生了 一些什么(源代码经过精简):
/**
* The interface that applications use to talk to the global power manager
* system service.
*
* @see frameworks/base/core/java/android/os/PowerManager.java
*/
public final class PowerManager { // Our handle on the global power manager service.
private final IPowerManager mService; public WakeLock newWakeLock(int levelAndFlags, String tag) {
return new WakeLock(levelAndFlags, tag);
} public final class WakeLock {
private final IBinder mToken;
private final int mFlags;
private final String mTag; WakeLock(int flags, String tag) {
// Create a token that uniquely identifies this wake lock.
mToken = new Binder();
mFlags = flags;
mTag = tag;
} public void acquire() {
// Send the power manager service a request to acquire a wake
// lock for the application. Include the token as part of the
// request so that the power manager service can validate the
// application's identity when it requests to release the wake
// lock later on.
mService.acquireWakeLock(mToken, mFlags, mTag);
} public void release() {
// Send the power manager service a request to release the
// wake lock associated with 'mToken'.
mService.releaseWakeLock(mToken);
}
}
}

发生了一些什么呢?我们来一步步阅读代码:
  1. client程序在onCreate()中请求了PowerManager类的一个实例.PowerManager类提供给client程序一个与执行在系统服务进程中的负责设备电源状态(比如决定屏幕亮度,检查设备是否插入dock等)的全局PowerManagerService对话的接口.
  2. client程序在onCreate()中创建并获得了一个唤醒锁(wake lock).PowerManager发送了一个WakeLock的创建的唯一的Binder令牌作为acquire()请求的參数.当PowerManagerService接收到了请求,它将接收到的令牌安全的保存起来,并强制设备保持唤醒状态,直到...
  3. 客户程序在onDestroy()中释放了唤醒锁.PowerManager发送了WakeLock创建的唯一的Binder令牌作为请求的參数.当PowerManagerService接收到请求,它将这个令牌与它保存的全部令牌进行比較,假设发现同样的就释放唤醒锁.这额外的一步"确认步骤"是保证PowerManagerService不会被别的应用程序欺骗而释放唤醒锁的重要安全措施.
因为它们的对象唯一性,Binder令牌在系统中被广泛(任意选择一个文件frameworks/base/services/java/com/android/server,就能发现它使用了几种形式的Binder令牌.还有一个非常cool的样例涉及状态栏,通知管理,和系统UI.详细的说,StatusBarManagerService维护一个全局的Binder令牌到通知的Map.当NotificationManagerService发出一个请求使状态栏管理器加入一个通知到状态栏,状态栏管理器就生成一个唯一的Binder令牌同一时候传递到通知管理器和系统UI.这样三方都知道了通知的Binder令牌,不论什么通知的改变(比如通知管理器取消了一条通知或者系统UI侦測到用户将一条通知划掉)都会首先通过状态管理器.这使的3个系统服务能更易保持同步:状态栏管理器能集中控制全部的当前正在显示的通知而不用与系统UI和通知管理器互相交互.)用于安全保障.也许是在全部framework中最有意思的样例就是"窗体令牌"(window
token)了,接下来我们来讨论它.

窗体令牌(Window Tokens)

假设你曾翻阅过官方关于View类的文档,你可能会困惑于getWindowToken()方法不知道它的意义.顾名思义,一个窗体令牌是一种特殊的Binder令牌,窗体管理器用于唯一标识系统中的一个窗体.窗体令牌对于安全十分重要,由于它们会阻止恶意程序出如今其它程序界面之上.窗体管理器通过要求应用程序将它们的窗体令牌作为加入或者删除一个窗体的參数传递过来(拥有 android.permission.SYSTEM_ALERT_WINDOW
权限的程序,即"在其它程序界面上绘制"权限,对此规则是个例外.Facebook Messenger和DicePlayer是两个经常使用的需求这个权限的程序,而且用此在后台服务中在其它程序的界面上加入窗体).假设令牌不匹配,窗体管理器拒绝请求并抛出一个BadTokenException,假设没有了窗体令牌,这必要的身份建议步骤就不可能实现,窗体管理器就没办法防止防止恶意程序了.

通过这一点,你可能想知道自己在实际开发中何时须要一个窗体令牌.这里有几个样例:

  • 当一个应用程序第一次启动,ActivityMangerService(这是一个全局系统服务,执行于系统服务进程中,负责启动和管理新的组件,比如Activities和Services,同一时候它还涉及维护OOM调整,被用于内核低内存时的处理,权限,任务管理等)创建了一个特殊的窗体令牌称之为应用程序窗体令牌(application window token),它唯一地标识应用程序顶层容器的窗体(你能够通过调用getApplicationWindowToken()获得一个引用).Activity管理器将这个令牌同一时候传给应用程序和窗体管理器,然后每次应用程序想要加入窗体的时候都要向窗体管理器传入这个令牌.这确保了应用程序与窗体管理器的安全交互(由于使得别的程序不可能向顶层加入窗体),同一时候也让Activity管理器向窗体管理器直接发送请求变得更简单.比如,Activity管理器能够说,"隐藏这个令牌的全部窗体",然后窗体管理器就能正确的选择出须要关闭的窗体了.
  • 实现自己定制桌面程序(Launchers)的开发人员能够与动态壁纸窗体交互,通过调用sendWallpaperCommand(IBinder
    windowToken, String action, int x, int y, int z, Bundle extras)
    使之直接位于后面.为了确保除了桌面没有别的应用程序可以与动态壁纸交互,framework须要开发人员传入一个窗体令牌作为该方法的第一个參数.假设窗体令牌与当前位于壁纸前的Activity的窗体不匹配,这条命令将被忽略而且打印出一条警告.
  • 应用程序能通过调用hideSoftInputFromWindow(IBinder windowToken, int flags)方法请求InputMethodManager隐藏软键盘,可是必须提供一个窗体令牌作为參数,假设令牌不匹配当前接受输入的窗体令牌,InputMethodManager会拒绝请求,这确保恶意程序无法强制关闭由其它程序打开的软键盘.
  • 手动加入新窗体到屏幕上的应用程序(比如,使用addView(View,WindowManager.LayoutParams)方法)可能须要通过设置WindowManager.LayoutParams.token属性来指定他们应用程序的窗体令牌.一般正常的程序都不太会这样做,由于在使用getWindowManager()方法时返回的WindowManager对象已经自己主动为你设置好了令牌值.也就是说,假设在以后你遇到须要从后台服务向屏幕加入一个窗体这样的情况时,你要知道你应该手动设置你程序的窗体令牌才干成功.

总结

虽然它们的存在对于大部分开发人员来说是屏蔽掉的,可是Binder令牌在系统被广泛应用于安全性.Android是一个大规模的分布式协作系统依赖于Binder对象在整个设备上的全部进程中都是唯一的。Binder令牌是整个framework相互协作的背后驱动力,假设没有他们保证应用程序进程间的安全交互,整个系统将会非常难运作.

Binders 与 Window Tokens(窗体令牌)的更多相关文章

  1. 【转载】Layered Window(分层窗体,透明窗体)

    本文转载自花间醉卧<Layered Window(分层窗体,透明窗体)> //为窗体添加WS_EX_LAYERED属性,该属性使窗体支持透明 ModifyStyleEx(0, WS_EX_ ...

  2. WmS具体解释(二)之怎样理解Window和窗体的关系?基于Android7.0源代码

    上篇博客(WmS具体解释(一)之token究竟是什么?基于Android7.0源代码)中我们简要介绍了token的作用,这里涉及到的概念非常多,当中出现频率最高的要数Window和窗体这一对搭档了,那 ...

  3. SQL Server Window Function 窗体函数读书笔记二 - A Detailed Look at Window Functions

    这一章主要是介绍 窗体中的 Aggregate 函数, Rank 函数, Distribution 函数以及 Offset 函数. Window Aggregate 函数 Window Aggrega ...

  4. 信頼済みサイト对window.open窗体大小影响原因之一

    如果某站点被添加进去之后,这个站点窗体限制被决定了,window.open里面,status bar 无效的设置不再起作用.而且,如果原来status bar被 任务栏挡住的话,这个时候它就会被显示出 ...

  5. SQL Server Window Function 窗体函数读书笔记一 - SQL Windowing

    SQL Server 窗体函数主要用来处理由 OVER 子句定义的行集, 主要用来分析和处理 Running totals Moving averages Gaps and islands 先看一个简 ...

  6. 浏览器根对象window之窗体和工具条

    1. 窗体和工具条 1.1 窗体 frames.self.window.parent.top.opener. frames 数组类型,页面中iframe的引用,如果页面有2个iframe,则frame ...

  7. Qt :非window子窗体的透明度设置

    ✿问题的由来            心血来潮,想利用QTimer 配合 setWindowOpacity()方法来实现一个窗体淡入的效果.   ✿实验代码    粗糙的实验代码: void Widge ...

  8. Android应用程序窗体设计框架介绍

    在Android系统中,一个Activity相应一个应用程序窗体.不论什么一个Activity的启动都是由AMS服务和应用程序进程相互配合来完毕的.AMS服务统一调度系统中全部进程的Activity启 ...

  9. window的对象有哪些(笔记)

    window的主对象主要有如下几个: document 对象: frames 对象: history 对象: location 对象: navigator 对象: screen 对象: 全局变量和函数 ...

随机推荐

  1. docker学习笔记2:容器操作

    一.列出主机上已经创建的容器 docker ps -a 二.创建交互式容器 命令: docker run -i -t ubuntu /bin/bash 其中-i -t 表示创建一个提供交互式shell ...

  2. Math.random与java.util.Random的差别

    今天在做一道习题时想到了Math.random()与Random类有什么区别,查阅了一些资料,感觉讲的不是太好. 首先两者的区别是一个是方法,一个是类. 其实前者的实现借助与后者.大家可以看一下Mat ...

  3. JPA相关知识点滴--持续更新中.....

    Java 持久化(JPA)  •Java EE 5 在EJB 3.0 中包含JPA 1.0 •参考实现:TopLink Essentials •Java EE 6 包含JPA 2.0 •参考实现:Ec ...

  4. 用反射,将DataRow行转为Object对象

    /// <summary> /// 反射辅助类 /// </summary> public class ReflectionHelper { /// <summary&g ...

  5. java基本数据类型转换成byte[]数组

    import java.io.UnsupportedEncodingException;  public class ConToByte {      /**     * double转换byte   ...

  6. 2数组的slice和splice方法

    var colors=["blue","red","black","yellow","gray",& ...

  7. backbone入门小例子

    最近听了个backbone的分享,为了避免听不懂,就先做了个小例子 例子很简单,效果如下 基本视图模板: <script type="tex/template" id=&qu ...

  8. 看到关于socket非阻塞模式设置方式记录一下。

    关于socket的阻塞与非阻塞模式以及它们之间的优缺点,这已经没什么可言的:我打个很简单的比方,如果你调用socket send函数时: 如果是阻塞模式下: send先比较待发送数据的长度len和套接 ...

  9. 创建采购订单批到程序用的BAPI

    CALL FUNCTION 'BAPI_PO_CREATE1' EXPORTING poheader = poheader poheaderx = poheaderx * POADDRVENDOR = ...

  10. Servlet过滤器——日志记录过滤器

    1.概述 在实际的项目开发过程中,经常需要在项目运行时,记录并在控制台中输出运行时的日志信息,便于查看项目的运行状况.本实例将介绍如何应用过滤器实现日志记录.运行本实例,将在控制台中输出项目运行时的日 ...