这是一篇从前博客迁移来的文章。
categories:
- 搞代码
- Android
- Android Framework
引言 #
开发中,使用以下简单的代码,即可实现 按♂摩:
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate();
可是,为什么 Vibrator
需要 getSystemService
获得?系统服务 又是什么概念?它是如何工作的?本文从一个简单常见的系统服务Vibrator
入手,分析 SystemService
的注册、获取和工作方式。最后,带领读者自己做一个 ShutdownService
系统服务,并使用 Root 方式注册到系统并使用。
getSystemService
发生了什么 #
我们要控制震动,需要首先使用 Context#getSystemService(String)
获取到 Vibrator
系统服务对象:
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
然后便可以控制震动了。但是,一个简单的 getSystemService
,这背后发生了什么呢:
我们看到, Vibrator
是一个抽象类,它的震动方法是抽象的,并且整个 Vibrator
仅仅提供了些 API,没有什么作用,所以说明有东西在继承它。
找到 android.os.SystemVibrator
,它其实是震动 API 的真面目,里面直接使用 IVibratorService
控制了震动。(它还是运行在你的进程中。)
通过搜索 SystemVibrator
,我们发现类 android.app.SystemServiceRegistry
,根据注释,它就是 getSystemService
的实际实现,它静态地注册了可以获取的服务到 Map,然后再调用 ServiceFetcher
获取服务。
Vibrator 的注册 #
由于是系统服务,Vibrator
首先需要在 ServiceManager
注册。系统服务是跑在 system_server
进程中的,它们也将在 SystemServer
初始化的时候注册到系统,随后我们才可以通过 getSystemService
获取到它(单例模式)。
它的注册发生在 com.android.server.SystemServer
的 792 行:
// 前面初始化其他服务,如电话服务等
// 开始注册震动服务
// 先立个 Flag,事件开始
traceBeginAndSlog("StartVibratorService");
// 创建新 VibratorService 实例
vibrator = new VibratorService(context);
// 调用 ServiceManager.addService(String, IBinder) 添加服务
ServiceManager.addService("vibrator", vibrator);
// 事件结束
traceEnd();
从源码中不妨看出,这段代码很工整,都是如上面所示的类似,一个个服务初始化,并注册到系统。从方法名也可以看出: startOtherServices
。
而这个方法的调用者,则直接是在 SystemServer#run()
里面,第 396 行。 这里会启动三种服务,如果失败都将导致 system_server
崩溃。
这也就是 SystemServer
启动系统服务的基本流程。
忽然发现除了 otherSerivces
,其他服务都是直接调用 SystemServiceManager#startService
启动,而其他服务则是使用ServiceManager#addService
添加,原因未知。
Vibrator 的初始化 #
刚才讲到,VibratorService
是在 SystemServer
初始化的时候 new 出来的。
再谈 getSystemService
#
刚才提到了 SystemServiceRegistry
。现在再结合刚才的 ServiceManager
,回头研究它。ServiceManager#addService
仅仅是把 IBinder
加进系统,但 getSystemService
仍然无法获取到,因为还没有在 SystemServiceRegistry
中注册,它静态地注册了需要的系统服务,其中包含 VIBRATOR_SERVICE
。看第 518 行:
// 把 SystemVibrator 注册为 VIBRATOR_SERVICE
registerService(Context.VIBRATOR_SERVICE /* 名字,这个应该很熟悉 */, Vibrator.class /* Service 的类,应该是 Context#getSystemService 返回的(?) */,
new CachedServiceFetcher<Vibrator>() {
@Override
public Vibrator createService(ContextImpl ctx) {
// 这里创建了 SystemVibrator
return new SystemVibrator(ctx);
}} /* 在这里使用抽象方法 createService 创建服务单例 */);
所以,每次试图通过 getSystemService
获取震动服务,其实拿到的是 SystemVibrator
,其中再调用 IVibratorService
连接系统,控制震动。可是 IVibratorService
是怎么创建的呢?
又是 ServiceManager
#
写过 AIDL/IBinder
的朋友都应该知道,IBinder
是 Android 的 IPC 方式。如果需要 IPC,客户端需要在 ServiceConnection
的 onServiceConnected(ComponentName name, IBinder service)
里面获得服务接口:
IXXXService = IXXXService.Stub.asInterface(service);
而看 SystemVibrator
的构造器代码:
IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
它没有用 ServiceConnection
,而是直接调用 ServiceManager#getService(String)
获取刚刚在SystemServer
注册的震动服务。这也就是 Vibrator
的初始流程。
SystemVibrator
和 VibratorService
#
SystemVibrtor
是运行在你的进程中的 API,也就是 Client
而 VibratorService
是运行在 system_server
中的系统服务,它掌管系统的震动功能,直接控制震动,也就是 Server,这也就是 Android 应用与系统服务之间的 CS 架构。
小结 #
总上所述,Vibrator
系统服务的启动和使用流程如下:
ROM | 应用 |
---|---|
system_server 启动:SystemServer#main → SystemServer#run |
|
开始初始化 Vibrator :startOtherServices → 创建 VibratorService 实例 → 使用 ServiceManager#addService 注册到系统 |
|
链接到 getSystemService : SystemServiceRegistry 静态注册服务 → 创建 SystemVibrator 并添加到 Map,以便 Context#getSystemService 使用。 |
|
SystemVibrator : 构造器从 ServiceMangaer#getService 创建 IVibratorService |
|
开发者调用 Vibrator#vibrate :由继承 Vibrator 的 SystemVibrator 调用 IVibratorService ,进行 IPC 到 VibratorService 。 |
|
VibratorService 是继承 IVibratorService.Stub 的,接受请求。 |
|
VibratorService 开始执行震动:验证权限,调用 Navite …. |
一个系统服务的基本构造 #
系统服务和 bindService
都是 Binder IPC
方式。它的不同点就在于,一个是由应用启动 Service,然后进行绑定;而系统服务则是由系统启动,并保持运行,注册到系统中(它相当于 服务),最后再由应用从系统中取出、调用。本质上没有区别。
所以,一个系统服务也就需要:
-
Binder
接口 -
一个保持运行的程序,作为
system_service
(后面将使用 Java+AppProces+Looper 实现) -
继承接口的服务类
-
客户端 API,负责
Binder
调用(如SystemVibrator
) -
API,如
XXXManager
。有了这些,我们就可以实现一个系统服务了。接下来,我们自己实现一个
ShutdownService
,提供关机 API。
写一个系统服务 #
目标:实现一个提供 关机、重启 接口的 ShutdownService
,并进行权限验证。
使用 AppProcess #
要使用 ROOT 启动 Java 程序,需要调用 AppProcess
启动。更多信息,我的另一篇博客
同时,它需要保持运行,我们模仿 SystemServer
,使用 Looper
使其常驻运行 。以下是 Server 代码:
public class MainServer {
public static void main (String[] args) {
new MainServer().run(args);
}
/**
* 运行服务
* @param args {@link MainServer#main(String[])} 中的参数
*/
private void run (String[] args) {
// 使用 Looper 保持运行
Looper.prepareMainLooper();
// 执行代码
// Loop forever
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
然后使用 Root 启动(来自 Shizuku
项目):
public static void start (Context context) {
try {
Log.d(TAG, "Starting server...");
String path = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.publicSourceDir;
// 这里用 AndroidUtilCode,不知道为什么 libsuperuser 的 Daemon 无效,会阻塞线程
ShellUtils.execCmd(new String[]{
// 停止旧的服务器
"killall shutdown_server",
// 设置 CLASSPATH
"export CLASSPATH=" + path,
// 启动服务器
"exec app_process /system/bin --nice-name=shutdown_server " + MainServer.class.getName() + /* Daemon 运行 */" &"
}, true);
} catch (Exception e) {
Log.e(TAG, "Unable to start server", e);
}
}
创建服务 #
我们使用 AIDL 生成 Binder:
// IShutdownService.aidl
// IShutdownService.aidl
package xxxxx;
interface IShutdownService {
void shutdown ();
void reboot ();
}
然后创建服务端程序 ShutdownService
,使其继承 IShutdownService.Stub
:
public class ShutdownService extends IShutdownService.Stub {}
注册 #
模仿 SystemServer
的做法,直接调用 ServiceManager.addService()
就可以添加服务了:
ShutdownService shutdownService = new ShutdownService();
ServiceManager.addService(SERVICE_NAME, shutdownService);
创建一个 Manager
#
然后,我们需要创建一个提供给开发者的 Manager,起名为 ShutdownManager
,它主要对 API 进行简化,并提供帮助方法。
先在 Manager 中添加获取接口的方法:
// ShutdownManager.java
/**
* 获取 Binder 接口(单例)
* @return Binder
*/
private synchronized IShutdownService getService () {
if (mService == null) {
mService = IShutdownService.Stub.asInterface(ServiceManager.getService(MainServer.SERVICE_NAME));
if (mService == null) {
Log.w(MainServer.TAG, "Shutdown service not found");
}
}
return mService;
}
然后提供一些便利的方法:
// ShutdownMangaer.java
/**
* Shutdown device
*/
public void shutdown () {
try {
getService().shutdown();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
使用 Manager
调用 shutdown
,然后运行测试,可以看到 ShutdownService
里面的 shutdown
被调用了!
真的关机 #
然后,我们加入关机 API,让 ShutdownService
发挥作用。
关机相关 API 主要是 PowerManager
的作用,请参见 com.android.internal.app.ShutdownActivity
。
PowerManager
依然是一个系统服务,我们还可以通过上面的方式获取:
IPowerManager powerManager =
IPowerManager.Stub
.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
然后调用 shutdown
方法,实现关机:
powerManager.shutdown(true /* 提醒用户确认 */,
PowerManager.SHUTDOWN_USER_REQUESTED /* 关机原因 */
, false /* wait,什么玩意儿(x */);
随后,手机就弹出了 您的手机即将关机
的确认框。
权限验证 #
直接提供关机服务很不安全,没有任何验证的情况下可能会被恶意使用,所以我们需要加入权限验证,如震动也需要 VIBRATE
权限,同理。
观察 VibratorService
的代码,不难看出是调用了 Context#checkCallingOrSelfPermission
,我们常用它检查客户端权限。但是,AppProcess
里面运行的是纯 Java 程序,是没有 Context
的,如何获取呢?
获取 Context
#
看 SystemServer
的 475 行(传送门),它也是纯 Java 程序,但通过了 activityThread.getSystemContext
获取系统上下文,我们也使用这种方式。而 ActivityThread
则是通过 ActivityThread#systemMain
静态方法拿到的。
拿到了 Context
,就可以验证权限了。为了绕过 android.permission.SHUTDOWN
系统权限验证,自己定义权限即可:
<permission-group android:name="yuuta.permission.POWER"
android:icon="@drawable/ic_power_settings_new_black_24dp"
android:label="控制设备电源" />
<permission android:name="yuuta.permission.SHUTDOWN"
android:permissionGroup="yuuta.permission.POWER"
android:label="关机机"
android:protectionLevel="dangerous"
android:icon="@drawable/ic_power_settings_new_black_24dp" />
<permission android:name="yuuta.permission.REBOOT"
android:permissionGroup="yuuta.permission.POWER"
android:label="重启"
android:protectionLevel="dangerous"
android:icon="@drawable/ic_power_settings_new_black_24dp" />
然后在没有权限时抛出 SecurityException
,它会抛给客户端:
if (mContext.checkCallingOrSelfPermission(MainServer.PERMISSION_SHUTDOWN) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("You don't have shutdown permission!");
}
运行测试,没有权限的时候程序崩溃了。
尾声 #
咸鱼博主又闲来无事研究了下系统服务的原理,分享给大家,就这么多~