原文地址: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. 【手打】LZW编码的C/C++实现

    LZW编码通过建立一个字符串表,用较短的代码来表示较长的字符串来实现压缩. LZW压缩算法是Unisys的专利,有效期到2003年,所以相关算法大多也已过期. 本代码只完毕了LZW的编码与解码算法功能 ...

  2. poj2533--Longest Ordered Subsequence(dp:最长上升子序列)

    Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 33943   Acc ...

  3. android Context的理解

    很多初入Android开发的网友向我们问到Context有什么作用,很多地方都用到它,这里Android123给这些新入门的网友做个简单的解释:  Context字面意思上下文,位于framework ...

  4. 集合简单总结 ArrayList、List、Hashtable、Dictionary

      ============================ 集合综述 ============================== 1.什么是泛型: 泛型就是限制了操作类型,意思如下:       ...

  5. 【虚拟化实战】容灾设计之一VR vs SRM

    作者:范军 (Frank Fan) 新浪微博:@frankfan7 从本文开始,我们将介绍一系列的关于容灾的解决方案.先探讨应用的场景,然后再深入介绍技术架构. 情景一: 某小型公司的虚拟化环境中,在 ...

  6. Android - Animation 贝塞尔曲线之美

    概述 贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计.贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算 ...

  7. Qt 中文乱码解决大全

    源地址:http://blog.csdn.net/xcy2011sky/article/details/7168376 解决中文乱码,最好知道乱码是什么格式比如说:utf-8. 解决方案: 1.让整个 ...

  8. delphi与汇编

    我一直认为Delphi功能与C++相比毫不逊色,提供了丰富的控件和类.全部API以及嵌入的汇编.最近小弟在把C版的Huffman压缩改用Delphi写时,顺便“研究”了一下Delphi的位操作和嵌入式 ...

  9. 基于visual Studio2013解决C语言竞赛题之1052求根

       题目 解决代码及点评 /* 功能:用简单迭代法解方程 e^x - x - 2 = 0 它有两个根(如图),其迭代公式为: 1) x[n+1]= e^x*n-2 (初值X<0时) ...

  10. Python写入文件,但是发现文件为空,竟然未写入!

    问题描述: fw=open(r'C:\test.txt','w') s="Hello World!" fw.write(s) ========== 此时查看C盘根目录,发现test ...