【问题标题】:How do user-space applications control hardware (Location/Network/Wifi) in Android?用户空间应用程序如何控制 Android 中的硬件(位置/网络/Wifi)?
【发布时间】:2015-09-08 04:25:42
【问题描述】:

我一直在阅读我能找到的所有内容,试图找出 Android 的 BINDER IPC 机制背后的软件架构。据我了解,BINDER 位于内核空间中,并临时为用户空间应用程序分配共享内存,通过该内存可以在进程之间中继消息。

我开始失去控制的是实际实施的工作方式 - 特别是与 parcels 相关。

我在网上查找并找到了 Android 提供的任意服务的实现,例如 Network/Wifi/Notificaiton/Battery 等 (Docs)。从我的阅读中,我了解到用户空间程序不应该实例化服务类本身,而是通过Context.getSystemService(Context.X) 获取对服务类的引用。因此,我认为这是一种间接的方式,即 Android已经服务在运行,或者至少有资源在需要时启动它。实现基本上是这样的:

电池类

BatteryManager.setBatteryState(){
    Parcel parcelLocal = Parcel.obtain();
    parcelLocal.writeInterfaceToken("android.power.BatteryManager");
    parcelLocal.writeInt(1/0); //Depending on requested state
    BinderObject.transact      //Send the data using BINDER
    CheckForExceptions();      //Upon return, check for exceptions 
    ReadResponse();            //If none, read the response from the target
    DoAppropriateAction();     //Whatever we need to do after setting the state
    parcelLocal.recycle();     //Return the parcel resource
}

起初看起来很简单:当用户执行以下操作时:

BatteryMonitor bMonitor = Context.getSystemService(Context.POWER_SERVICE);
bMonitor.setBatteryStatus(1); 

那么user's实例会使用BINDER机制与system's实际的服务控制器进行通信(哪个是同一个类的实例?)。但是,上面显示的代码IS是系统电池监控服务的实现,那么究竟是谁在接收 BINDER 数据呢?

TL;DR:如果这一切都非常混乱,很可能是因为我试图将一千行代码压缩成 10 行,总结是:当用户打算控制硬件状态 - 例如网络/Wifi/位置/通知(触摸屏) - 实际上 在 Android 中发生了什么, 真的 > 控制与这些抽象服务相关的硬件?

注意:以上代码完全是虚构的,仅用于显示一般结构。

【问题讨论】:

    标签: android kernel android-service hardware android-binder


    【解决方案1】:

    大多数系统服务在system_server 进程中作为线程运行。在启动时,他们将邀请呼叫(参见SystemServer.java 中对addService() 的呼叫)传递给servicemanager,然后它能够​​将邀请分发给调用getSystemService 的应用程序。

    一旦事情进展顺利,您可以将整个设置视为一种客户端-服务器架构,其中您的应用程序是客户端(远程或代理端),服务器(本地或存根端)是您的系统服务'正在交谈。客户端和服务器通过称为 binder 的进程间通信 (IPC) 子系统进行通信。绑定器有不同的部分:框架组件执行包的编组和解组,而内核驱动程序执行与 ioctl 调用之间的实际内存复制,并在进程和线程级别跟踪谁被邀请调用。

    应用程序通过代理与活页夹进行交互。例如,当您使用LocationManagerService 时,您将获得android.location.ILocationManager 的实例。 Proxy 类中的方法之一是getLastLocation()

    ...
    @Override public android.location.Location getLastLocation(android.location.LocationRequest request, java.lang.String packageName) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    android.location.Location _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    if ((request!=null)) {
    _data.writeInt(1);
    request.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    _data.writeString(packageName);
    mRemote.transact(Stub.TRANSACTION_getLastLocation, _data, _reply, 0);
    _reply.readException();
    if ((0!=_reply.readInt())) {
    _result = android.location.Location.CREATOR.createFromParcel(_reply);
    }
    else {
    _result = null;
    }
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    ...
    

    在这里您可以看到事务代码TRANSACTION_getLastLocation 连同任何必要的数据一起写入接口,并读取了结果。在存根端,有一个onTransact() 方法在服务的进程空间中运行,它根据事务代码处理所有传入的事务:

    ...
    case TRANSACTION_getLastLocation:
    {
    data.enforceInterface(DESCRIPTOR);
    android.location.LocationRequest _arg0;
    if ((0!=data.readInt())) {
    _arg0 = android.location.LocationRequest.CREATOR.createFromParcel(data);
    }
    else {
    _arg0 = null;
    }
    java.lang.String _arg1;
    _arg1 = data.readString();
    android.location.Location _result = this.getLastLocation(_arg0, _arg1);
    reply.writeNoException();
    if ((_result!=null)) {
    reply.writeInt(1);
    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    }
    else {
    reply.writeInt(0);
    }
    return true;
    }
    ...
    

    简而言之,system_service 进程代表调用者执行操作。这允许它对硬件或其他系统资源执行通常具有特权的操作。安全性基于 1) 具有呼叫邀请的应用程序(通过 getSystemServiceservice_manager 获得)和 2) 通过服务本身实施的任何检查,例如在以下情况下检查 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION LocationManagerService(在清单中声明并在安装时由最终用户批准)。

    更新:在定位服务的情况下,这些硬件操作需要从 GPS 硬件获取实际的 NMEA 数据。目前实现这一点的方式是通过 GpsLocationProvider 类,该类通过 JNI 与本机代码接口。本机代码 (com_android_server_location_GpsLocationProvider.cpp) 是打开硬件设备的地方(通过 hw_module_t 结构中保存的抽象层),进行位置回调(例如,location_callback())等。所有这些都在 @ 987654345@ 具有特权 UID system 的进程空间。您可以通过运行支持定位的应用程序来验证这一点,在 logcat 中查找 GpsLocationProvider 标签并确认记录的 PID 是 system_server 的。例如:

    $ adb logcat | grep -i gps
    ...
    D/GpsLocationProvider(  731): Reset GPS properties, previous size = 8
    ...
    

    $ adb shell ps | grep system_server
    system    731   441   1094884 89232 ffffffff b74d1d05 S system_server
    $
    

    最后,我强烈推荐视频教程Deep Dive Into Android IPC/Binder Framework 来了解更多相关信息。演讲的幻灯片可以在here找到。

    【讨论】:

    • 这可能是我读过的关于这个主题的最有用的帖子之一。您在上面提供的代码与我今天早些时候在网上发现的几乎相同。我在代码中看到了一个明显的问题并且无法调和:您的第二个代码 sn-p(由服务运行)实际上调用了 this.getLastLocation,您已经证明它是在这样的一种方法 get it 获得两个包裹,然后将它们发送到服务存根;它似乎是递归的。
    • 此外,服务存根似乎通过查询调用 this.getLastLocation (似乎存在递归问题)在其自身中设置了一个私有成员(_result),并将此结果返回给客户请求它。实际的 GPS 硬件如何被驱动以使服务甚至具有返回给请求客户端的信息的问题仍然存在。服务存根是否以某种方式(通过活页夹)链接到内核驱动程序或其他东西?如何以及谁在实际驱动硬件,谁在命令他们这样做?
    • 我确实花时间观看了该视频并查看了幻灯片。我相信我现在对 Binder 的架构有了更多的了解,至少在进程之间的消息传递、数据编组等方面。我仍然对如何运行较低级别的代码来控制然而,硬件。考虑您在上面发布的幻灯片链接的第 20 页。它表明LocationManagerService 创建并使用location.GpsLocationProviderlinks 在底层.cpp 硬件驱动程序实现中调用。但是,如果您查看上面的代码..
    • 客户端(Stub.Proxy)通过Binder驱动和服务进程(Stub)对话。这个存根解组数据并似乎再次调用getLastLocation。在客户端,我们有这个成员为Stub.Proxy.getLastLocation。这个 LocationManager 的服务实例有什么不同吗?如果不是,为什么当 Stub 调用 getLastLocation 时,它不再使用 Binder 将消息传递给自己?
    • 我不确定我是否可以给你你正在寻找的确切答案。但是,我已经对 GPS 提供商进行了一些试验,并用我所知道的更新了我的答案。我很确定没有任何递归或“服务调用自身”正在进行。我知道在服务/存根端,一切都在system_server 的进程空间中完成。这包括与硬件设备和 JNI 回调或生成发送回代理的回复所必需的实际接口的本机代码。是的,LocationManagerService 覆盖 getLastLocation
    猜你喜欢
    • 2013-08-21
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 2011-09-24
    • 1970-01-01
    • 2011-05-19
    • 2011-08-26
    • 1970-01-01
    相关资源
    最近更新 更多