从 Vibrator 到系统服务

· 710 words · 4 minute read

这是一篇从前博客迁移来的文章。

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.SystemServer792 行

// 前面初始化其他服务,如电话服务等

// 开始注册震动服务
// 先立个 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,客户端需要在 ServiceConnectiononServiceConnected(ComponentName name, IBinder service)里面获得服务接口:

IXXXService = IXXXService.Stub.asInterface(service);  

而看 SystemVibrator 的构造器代码:

IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));

它没有用 ServiceConnection,而是直接调用 ServiceManager#getService(String) 获取刚刚在SystemServer注册的震动服务。这也就是 Vibrator 的初始流程。

SystemVibratorVibratorService #

SystemVibrtor 是运行在你的进程中的 API,也就是 Client

VibratorService 是运行在 system_server 中的系统服务,它掌管系统的震动功能,直接控制震动,也就是 Server,这也就是 Android 应用与系统服务之间的 CS 架构。

小结 #

总上所述,Vibrator 系统服务的启动和使用流程如下:

ROM 应用
system_server 启动:SystemServer#mainSystemServer#run
开始初始化 VibratorstartOtherServices → 创建 VibratorService 实例 → 使用 ServiceManager#addService 注册到系统
链接到 getSystemServiceSystemServiceRegistry 静态注册服务 → 创建 SystemVibrator 并添加到 Map,以便 Context#getSystemService 使用。
SystemVibrator: 构造器从 ServiceMangaer#getService 创建 IVibratorService
开发者调用 Vibrator#vibrate:由继承 VibratorSystemVibrator 调用 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!");
        }

运行测试,没有权限的时候程序崩溃了。

尾声 #

咸鱼博主又闲来无事研究了下系统服务的原理,分享给大家,就这么多~