user-space 应用程序如何控制 Android 中的硬件 (Location/Network/Wifi)?
How do user-space applications control hardware (Location/Network/Wifi) in Android?
我一直在阅读我能找到的所有内容,试图找出 Android 的 BINDER IPC 机制背后的软件架构。据我了解,BINDER 位于 Kernel-space 中并临时为 user-space 应用程序分配共享内存,通过该共享内存可以在进程之间中继消息。
我开始失去控制的地方是实际实施的工作方式 - 特别是与 parcels 有关。
我在网上查找,发现了 Android 提供的任意 服务 的实现,例如 Network/Wifi/Notificaiton/Battery,等(Docs).根据我的阅读,我了解到用户 space 程序不应实例化服务 class 本身,而是通过 Context.getSystemService(Context.X)
获取对服务的引用。因此,我认为这是一种间接的说法,即 Android 已经有 服务 运行,或者至少有资源在需要时启动它。实施基本上是这样的:
Battery.class
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
实际的服务控制器进行通信(哪个是同class的实例?).但是,然而,上面显示的代码 是 系统电池监控服务的实现,那么实际上谁在接收 BINDER 数据呢?
TL;DR:如果这一切都非常混乱,很可能是因为我试图将 1000 行代码压缩成 10 行,总结是:用户打算控制硬件的状态 - 例如 Network/Wifi/Location/Notifcations(触摸屏) - 实际上 在 Android 和 who 中发生了什么 真的 控制与这些抽象服务关联的硬件吗?
注意:以上代码完全是捏造的,旨在仅显示一般结构。
大多数系统服务 运行 作为 system_server
进程中的线程。在启动时,他们将通话邀请(请参阅 SystemServer.java
中对 addService()
的调用)传递给 servicemanager
,然后后者能够将邀请分发给调用 getSystemService
的应用程序。
一旦一切顺利,您可以将整个设置视为一种客户端-服务器架构,其中您的应用程序是客户端(远程或代理端),服务器(本地或存根端)是系统服务你正在与之交谈。客户端和服务器通过称为绑定器的进程间通信 (IPC) 子系统进行通信。活页夹有不同的部分:框架组件执行包裹的编组和解组,而内核驱动程序执行实际内存复制 to/from ioctl 调用并跟踪谁被邀请在进程和线程级别调用。
应用程序通过代理与活页夹交互。例如,当您使用 LocationManagerService
时,您将获得 android.location.ILocationManager
的实例。 Proxy class 中的一种方法是 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
连同任何必要的数据被写入接口,并读取结果。在存根端,服务进程 space 中有一个 onTransact()
方法 运行ning,它根据事务代码处理所有传入事务:
...
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) 具有调用邀请的应用程序(通过 getSystemService
从 service_manager
获得)和 2) 通过服务本身实施的任何检查,例如检查 ACCESS_COARSE_LOCATION
或 ACCESS_FINE_LOCATION
在 LocationManagerService
的情况下(在清单中声明并由最终用户在安装时批准)。
更新:在定位服务的情况下,这些硬件操作需要从 GPS 硬件获取实际的 NMEA 数据。目前实现这一点的方式是通过 GpsLocationProvider
class ,它通过 JNI 连接到本机代码。此本机代码 (com_android_server_location_GpsLocationProvider.cpp
) 是打开硬件设备的地方(通过 hw_module_t
结构中的抽象层),进行位置回调(例如 location_callback()
)等。所有system_server
进程 space 中的此 运行 具有特权 UID system
。您可以通过 运行 启用定位功能的应用来验证这一点,在 logcat 中查找 GpsLocationProvider
标签并确认记录的 PID 是 system_server
的 PID。例如:
$ 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 to learn more about this. The talk's slides can be found here。
我一直在阅读我能找到的所有内容,试图找出 Android 的 BINDER IPC 机制背后的软件架构。据我了解,BINDER 位于 Kernel-space 中并临时为 user-space 应用程序分配共享内存,通过该共享内存可以在进程之间中继消息。
我开始失去控制的地方是实际实施的工作方式 - 特别是与 parcels 有关。
我在网上查找,发现了 Android 提供的任意 服务 的实现,例如 Network/Wifi/Notificaiton/Battery,等(Docs).根据我的阅读,我了解到用户 space 程序不应实例化服务 class 本身,而是通过 Context.getSystemService(Context.X)
获取对服务的引用。因此,我认为这是一种间接的说法,即 Android 已经有 服务 运行,或者至少有资源在需要时启动它。实施基本上是这样的:
Battery.class
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
实际的服务控制器进行通信(哪个是同class的实例?).但是,然而,上面显示的代码 是 系统电池监控服务的实现,那么实际上谁在接收 BINDER 数据呢?
TL;DR:如果这一切都非常混乱,很可能是因为我试图将 1000 行代码压缩成 10 行,总结是:用户打算控制硬件的状态 - 例如 Network/Wifi/Location/Notifcations(触摸屏) - 实际上 在 Android 和 who 中发生了什么 真的 控制与这些抽象服务关联的硬件吗?
注意:以上代码完全是捏造的,旨在仅显示一般结构。
大多数系统服务 运行 作为 system_server
进程中的线程。在启动时,他们将通话邀请(请参阅 SystemServer.java
中对 addService()
的调用)传递给 servicemanager
,然后后者能够将邀请分发给调用 getSystemService
的应用程序。
一旦一切顺利,您可以将整个设置视为一种客户端-服务器架构,其中您的应用程序是客户端(远程或代理端),服务器(本地或存根端)是系统服务你正在与之交谈。客户端和服务器通过称为绑定器的进程间通信 (IPC) 子系统进行通信。活页夹有不同的部分:框架组件执行包裹的编组和解组,而内核驱动程序执行实际内存复制 to/from ioctl 调用并跟踪谁被邀请在进程和线程级别调用。
应用程序通过代理与活页夹交互。例如,当您使用 LocationManagerService
时,您将获得 android.location.ILocationManager
的实例。 Proxy class 中的一种方法是 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
连同任何必要的数据被写入接口,并读取结果。在存根端,服务进程 space 中有一个 onTransact()
方法 运行ning,它根据事务代码处理所有传入事务:
...
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) 具有调用邀请的应用程序(通过 getSystemService
从 service_manager
获得)和 2) 通过服务本身实施的任何检查,例如检查 ACCESS_COARSE_LOCATION
或 ACCESS_FINE_LOCATION
在 LocationManagerService
的情况下(在清单中声明并由最终用户在安装时批准)。
更新:在定位服务的情况下,这些硬件操作需要从 GPS 硬件获取实际的 NMEA 数据。目前实现这一点的方式是通过 GpsLocationProvider
class ,它通过 JNI 连接到本机代码。此本机代码 (com_android_server_location_GpsLocationProvider.cpp
) 是打开硬件设备的地方(通过 hw_module_t
结构中的抽象层),进行位置回调(例如 location_callback()
)等。所有system_server
进程 space 中的此 运行 具有特权 UID system
。您可以通过 运行 启用定位功能的应用来验证这一点,在 logcat 中查找 GpsLocationProvider
标签并确认记录的 PID 是 system_server
的 PID。例如:
$ 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 to learn more about this. The talk's slides can be found here。