如何实现一个 System Services?
《Android 系统开发做什么?》写到 Android System Services 是专注于特定功能的模块化组件,应用框架 API 所提供的功能可与系统服务通信,以访问底层硬件。Android System Services 是如何写的?来以 DisplayManagerService 为例,具体来看看。
System Service 是如何写的?
应用调用
 DisplayManager dm = getSystemService(DisplayManager.class);
 dm.setTemporaryBrightness(0.0f);
 Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
看下 getSystemService 方法,在 Context 类里。
Context#getSystemService
public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
    // Because subclasses may override getSystemService(String) we cannot
    // perform a lookup by class alone.  We must first map the class to its
    // service name then invoke the string-based method.
    String serviceName = getSystemServiceName(serviceClass);
    return serviceName != null ? (T)getSystemService(serviceName) : null;
}
public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
ContextImpl#getSystemService
@Override
public String getSystemServiceName(Class<?> serviceClass) {
    return SystemServiceRegistry.getSystemServiceName(serviceClass);
}
继续跟 SystemServiceRegistry.getSystemServiceName。
SystemServiceRegistry#getSystemServiceName
public static String getSystemServiceName(Class<?> serviceClass) {
    if (serviceClass == null) {
        return null;
    }
    final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass);
    if (sEnableServiceNotFoundWtf && serviceName == null) {
        // This should be a caller bug.
        Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName());
    }
    return serviceName;
}
什么时候 registerService 的?
public final class SystemServiceRegistry {
    static {
        registerService(Context.DISPLAY_SERVICE, DisplayManager.class,
        new CachedServiceFetcher<DisplayManager>() {
            @Override
            public DisplayManager createService(ContextImpl ctx) {
                return new DisplayManager(ctx.getOuterContext());
            }
        });
    }
}
private static <T> void registerService(@NonNull String serviceName,
        @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
结合上面的分析代码可以知道 getSystemService(DisplayManager.class)得到的是一个 DisplayManager 的实例。
接下来看 dm.setTemporaryBrightness 方法。
DisplayManager#setTemporaryBrightness
public void setTemporaryBrightness(float brightness) {
    mGlobal.setTemporaryBrightness(brightness);
}
mGlobal 是 DisplayManagerGlobal 对象。
DisplayManagerGlobal#setTemporaryBrightness
private final IDisplayManager mDm;
private DisplayManagerGlobal(IDisplayManager dm) {
    mDm = dm;
}
public static DisplayManagerGlobal getInstance() {
    synchronized (DisplayManagerGlobal.class) {
        if (sInstance == null) {
            IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
            if (b != null) {
                sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
            }
        }
        return sInstance;
    }
}
public void setTemporaryBrightness(float brightness) {
    try {
        mDm.setTemporaryBrightness(brightness);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
mDm 是 IDisplayManager 对象,初始化在IDisplayManager.Stub.asInterface(ServiceManager.getService(Context.DISPLAY_SERVICE)),看到 IDisplayManager 是一个 aidl 文件:frameworks/base/core/java/android/hardware/display/IDisplayManager.aidl,AIDL (Android Interface Definition Language) 是 Android 中的接口定义文件,为系统提供了一种简单跨进程通信方法,先不管 AIDL。
IDisplayManager
IDisplayManager 定义了包括 setTemporaryBrightness 的几个接口。
interface IDisplayManager {
    //……
    void registerCallback(in IDisplayManagerCallback callback);
    // Requires CONFIGURE_WIFI_DISPLAY permission.
    // The process must have previously registered a callback.
    void startWifiDisplayScan();
    // Requires CONFIGURE_WIFI_DISPLAY permission.
    void stopWifiDisplayScan();
    // Requires CONFIGURE_WIFI_DISPLAY permission.
    void connectWifiDisplay(String address);
    // No permissions required.
    void disconnectWifiDisplay();
    // Temporarily sets the display brightness.
    void setTemporaryBrightness(float brightness);
    //……
}
IDisplayManager 只是接口,需要找下哪里实现了它,搜索是在 BinderService,BinderService 是 DisplayManagerService 内部类。
final class BinderService extends IDisplayManager.Stub {
    @Override // Binder call
    public void setTemporaryBrightness(float brightness) {
        mContext.enforceCallingOrSelfPermission(
                Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
                "Permission required to set the display's brightness");
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mSyncRoot) {
                mDisplayPowerController.setTemporaryBrightness(brightness);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
}
mDisplayPowerController.setTemporaryBrightness(brightness)后面经过一系列调用会到 LightsService#setLight_native,通过 JNI 调用到 native 层,调用底层进行背光调节,关于背光调节后面文章再细讲。
SystemServer
DisplayManagerService 是继承了 SystemService,DisplayManagerService 是怎么注册为系统服务的呢?在 SystemServer 里面:
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    t.traceBegin("StartDisplayManager");
    //开启DisplayManagerService
    mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
    t.traceEnd();
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    //通知服务系统启动完成
    t.traceBegin("MakeDisplayManagerServiceReady");
    try {
        // TODO: use boot phase and communicate these flags some other way
        mDisplayManagerService.systemReady(safeMode, mOnlyCore);
    } catch (Throwable e) {
        reportWtf("making Display Manager Service ready", e);
    }
    t.traceEnd();
}
看完 DisplayManagerService 是怎么写的,不妨模仿写个。
所谓看着代码,感觉还是挺简单的,实际操作起来,各种编译报错……
如何写个 System Service
先上图:

@startuml
title \n如何实现一个 System Services?\n
skinparam backgroundColor #EEEBDC
skinparam handwritten true
hide empty description
state 1.编写AIDL文件
1.编写AIDL文件 --> 2.Context定义变量
1.编写AIDL文件:IWuXiaolongManager.aidl
2.Context定义变量 --> 3.编写系统服务类
2.Context定义变量:String WUXIAOLONG_SERVICE = "wuxiaolong"
note left of 2.Context定义变量 : 执行make update-api,\n更新接口
3.编写系统服务类 --> 4.注册系统服务类
3.编写系统服务类:WuXiaolongManagerService.java
4.注册系统服务类--> 5.编写Manager类
note right of 4.注册系统服务类
  涉及SELinux权限
end note
5.编写Manager类 --> 6.注册Manager
note right of 5.编写Manager类
  1.写成单例
  2.@Nullable注解
end note
5.编写Manager类:WuXiaolongManager.java
6.注册Manager --> 7.应用调用
@enduml
1.编写 AIDL 文件
新建 frameworks/base/core/java/android/hardware/wuxiaolong/IWuXiaolongManager.aidl,内容如下:
package android.hardware.wuxiaolong;
/** @hide */
interface IWuXiaolongManager {
    String getName();
}
2.Context 定义变量
在 Context 里定义一个代表 wuxiaolong 服务的字符串
frameworks/base/core/java/android/content/Context.java
public static final String WUXIAOLONG_SERVICE = "wuxiaolong";
3.编写系统服务
frameworks/base/services/core/java/com/android/server/wuxiaolong/WuXiaolongManagerService.java
package com.android.server.wuxiaolong;
import android.content.Context;
import android.hardware.wuxiaolong.IWuXiaolongManager;
public class WuXiaolongManagerService extends IWuXiaolongManager.Stub {
    private final Context mContext;
    public WuXiaolongManagerService(Context context) {
        super();
        mContext = context;
    }
    @Override
    public String getName() {
        String name = "WuXiaolong..";
        return name;
    }
}
4.注册系统服务
frameworks/base/services/java/com/android/server/SystemServer.java
import com.android.server.wuxiaolong.WuXiaolongManagerService;
private void startOtherServices() {
    // 部分代码省略...
    try {
        android.util.Log.d("wxl","SystemServer WuXiaolongManagerService");
        ServiceManager.addService(Context.WUXIAOLONG_SERVICE, new WuXiaolongManagerService(context));
    } catch (Throwable e) {
        reportWtf("starting WuXiaolongManagerService", e);
    }
    // 部分代码省略...
}
5.编写 Manager 类
frameworks/base/core/java/android/hardware/wuxiaolong/WuXiaolongManager.java
package android.hardware.wuxiaolong;
import android.os.IBinder;
import android.os.ServiceManager;
import android.hardware.wuxiaolong.IWuXiaolongManager;
import android.content.Context;
import android.os.RemoteException;
import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.Nullable;
import android.os.ServiceManager.ServiceNotFoundException;
import android.annotation.SystemService;
@SystemService(Context.WUXIAOLONG_SERVICE)
public class WuXiaolongManager {
    private static WuXiaolongManager sInstance;
    private final IWuXiaolongManager mService;
    private Context mContext;
    /**
     * @hide
     */
    public WuXiaolongManager(IWuXiaolongManager iWuXiaolongManager) {
        mService = iWuXiaolongManager;
    }
    /**
     * Gets an instance of the WuXiaolong manager.
     *
     * @return The WuXiaolong manager instance.
     * @hide
     */
    @UnsupportedAppUsage
    public static WuXiaolongManager getInstance() {
        android.util.Log.d("wxl", "WuXiaolongManager getInstance");
        synchronized (WuXiaolongManager.class) {
            if (sInstance == null) {
                try {
                    IBinder b = ServiceManager.getServiceOrThrow(Context.WUXIAOLONG_SERVICE);
                    sInstance = new WuXiaolongManager(IWuXiaolongManager.Stub
                            .asInterface(ServiceManager.getServiceOrThrow(Context.WUXIAOLONG_SERVICE)));
                } catch (ServiceNotFoundException e) {
                    throw new IllegalStateException(e);
                }
            }
            return sInstance;
        }
    }
    @Nullable
    public String getName() {
        android.util.Log.d("wxl", "WuXiaolongManager getName");
        try {
            return mService.getName();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
6.注册 Manager
frameworks/base/core/java/android/app/SystemServiceRegistry.java
import android.hardware.wuxiaolong.WuXiaolongManager;
static {
    registerService(Context.WUXIAOLONG_SERVICE, WuXiaolongManager.class,
            new CachedServiceFetcher<WuXiaolongManager>() {
                @Override
                public WuXiaolongManager createService(ContextImpl ctx)
                        throws ServiceNotFoundException {
                    android.util.Log.d("wxl","SystemServiceRegistry registerService");
                    return WuXiaolongManager.getInstance();
                }});
}
7.应用调用
WuXiaolongManager mWuXiaolongManager = (WuXiaolongManager)mContext.getSystemService(Context.WUXIAOLONG_SERVICE);
android.util.Log.d("wxl","Name="+ mWuXiaolongManager.getName());
8.解决报错
编译报错
- 报错 1:
******************************
You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)
      to the new methods, etc. shown in the above diff.
   2. You can update current.txt and/or removed.txt by executing the following command:
         make api-stubs-docs-non-updatable-update-current-api
      To submit the revised current.txt to the main Android repository,
      you will need approval.
******************************
需要执行 make update-api,更新接口,会多出来:
frameworks/base/api/current.txt
diff --git a/api/current.txt b/api/current.txt
index 6b1a96c..0779378 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -10256,6 +10256,7 @@ package android.content {
     field public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
     field public static final String WIFI_SERVICE = "wifi";
     field public static final String WINDOW_SERVICE = "window";
+    field public static final String WUXIAOLONG_SERVICE = "wuxiaolong";
   }
   public class ContextWrapper extends android.content.Context {
@@ -18318,6 +18319,14 @@ package android.hardware.usb {
 }
+package android.hardware.wuxiaolong {
+
+  public class WuXiaolongManager {
+    method @Nullable public String getName();
+  }
+
+}
+
 package android.icu.lang {
frameworks/base/non-updatable-api/current.txt
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index adf1bb5..e738c02 100755
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -10256,6 +10256,7 @@ package android.content {
     field public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
     field public static final String WIFI_SERVICE = "wifi";
     field public static final String WINDOW_SERVICE = "window";
+    field public static final String WUXIAOLONG_SERVICE = "wuxiaolong";
   }
   public class ContextWrapper extends android.content.Context {
@@ -18318,6 +18319,14 @@ package android.hardware.usb {
 }
+package android.hardware.wuxiaolong {
+
+  public class WuXiaolongManager {
+    method @Nullable public String getName();
+  }
+
+}
+
 package android.icu.lang {
- 报错 2:
[0mManagers must always be obtained from Context; no direct constructors [ManagerConstructor]
编写 Manager 类需写成单例。
- 报错 3:
Missing nullability on method `getName` return [MissingNullability]
getName 方法加上@Nullable注解。
运行报错
04-08 15:41:38.798   297   297 E SELinux : avc:  denied  { find } for pid=12717 uid=1000 name=wuxiaolong scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
04-08 15:41:38.802 12717 12758 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: PowerManagerService
04-08 15:41:38.802 12717 12758 E AndroidRuntime: java.lang.IllegalStateException: android.os.ServiceManager$ServiceNotFoundException: No service published for: wuxiaolong
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.hardware.wuxiaolong.WuXiaolongManager.getInstance(WuXiaolongManager.java:47)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.app.SystemServiceRegistry$27.createService(SystemServiceRegistry.java:497)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.app.SystemServiceRegistry$27.createService(SystemServiceRegistry.java:493)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:1760)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:1440)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.app.ContextImpl.getSystemService(ContextImpl.java:1921)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at com.android.server.display.DisplayPowerController.updatePowerState(DisplayPowerController.java:1191)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at com.android.server.display.DisplayPowerController.access$700(DisplayPowerController.java:92)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at com.android.server.display.DisplayPowerController$DisplayControllerHandler.handleMessage(DisplayPowerController.java:2074)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:223)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.os.HandlerThread.run(HandlerThread.java:67)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at com.android.server.ServiceThread.run(ServiceThread.java:44)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: Caused by: android.os.ServiceManager$ServiceNotFoundException: No service published for: wuxiaolong
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.os.ServiceManager.getServiceOrThrow(ServiceManager.java:153)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	at android.hardware.wuxiaolong.WuXiaolongManager.getInstance(WuXiaolongManager.java:40)
04-08 15:41:38.802 12717 12758 E AndroidRuntime: 	... 12 more
这里是缺少 SELinux 权限,可执行:
adb shell
setenforce 0 (临时禁用掉SELinux)
getenforce  (得到结果为Permissive)
临时禁用掉 SELinux,功能就正常了,关于 SELinux 这里不说了,后面有机会写篇 SELinux 文章。
最后 Log 打印如下:
Line 832: 04-08 16:08:55.290 17649 17690 D wxl     : SystemServiceRegistry registerService
Line 833: 04-08 16:08:55.290 17649 17690 D wxl     : WuXiaolongManager getInstance
Line 835: 04-08 16:08:55.292 17649 17690 D wxl     : WuXiaolongManager getName
Line 836: 04-08 16:08:55.293 17649 17690 D wxl     : Name=WuXiaolong..
手写个 System Service 实践过后没那么简单,光 SELinux 权限够折腾半天了,这篇文章先就酱紫吧。
如何实现一个 System Services?的更多相关文章
- iOS System Services
		System Services is a singleton class to gather all available information about a device. Over 75 met ... 
- RH133读书 笔记(4) - Lab 4 System Services
		Lab 4 System Services Goal: Develop skills using system administration tools and setting up and admi ... 
- service citrix xcenserver health check service (xenserver healthcheck) failed to start verfy that you have sufficient privileges to srart system services
		citrix XcenServer版本:7.2 citrix Xcencenter版本:7.2 安装citrix Xcencenter的时候报错: service citrix xcenserver ... 
- (C# Window Service) Verify that you have sufficient privileges to start system services
		写了个Windows Service, 用Wix 写了个Installer,编译通过,生成了msi 安装文件,但是安装的时候总是提示: Product: KingPro Service -- Erro ... 
- Win10 Service'MongoDB Server' failed to start. Verify that you have sufficient privileges to start system services【简记】
		最近工作中有需要用到 MongoDB数据库,以前用的3.*的版本,这次用的是较新4.0.6的版本,然后去官网下载安装. 安装到一半,就弹出如下提示,说是"MongoDB Server&quo ... 
- 异常:已引发: "设置 connectionId 时引发了异常。" (System.Xaml.XamlObjectWriterException) 引发了一个 System.Xaml.XamlObjectWriterException: "
		项目中,引用一个富文本编辑器,SmithHtmlEditor,进入页面的时候异常. 在View和ViewModel所在的类库引用. 还需要在Main中引用. 
- 异常:已捕获: "Error creating context 'spring.root': 未将对象引用设置到对象的实例。" (System.Configuration.ConfigurationErrorsException) 捕获到一个 System.Configuration.ConfigurationErrorsException: "Error creating context 'sp
		查看所指定name的context是否注册成功,以后用此容器来获取其中的object. 常见的使用方式: Application_Start中使用ContextRegistry.GetContext( ... 
- 写一个system.data.entity的simpledatarepo,实现crudq这些功能,不需要引入entityframework,直接可以使用,用到objectset
		note:you can delete reference of entityframework when using this classes.it`s just a simple repohelp ... 
- mongodb安装及安装MongoDB报错Verify that you have sufficient privileges to start system services解决方法
		1.点击安装包mongodb-win32-x86_64-2012plus-4.2.2-signed进行安装 2.点击next 3.接受协议,点击next 4.点击自定义安装 选择安装路径,建议默认C盘 ... 
随机推荐
- Linux 三剑客之 awk 实战详解教程
			我们知道 Linux 三剑客,它们分别是:grep.sed.awk.在前边已经讲过 grep 和 sed,没看过的同学可以直接点击阅读,今天要分享的是更为强大的 awk. sed 可以实现非交互式的字 ... 
- 《逆向工程核心原理》——IAThook
			hook逻辑写入dll中,注入dll. #include "pch.h" #include <tchar.h> #include "windows.h&quo ... 
- Python爬虫系列之爬取美团美食板块商家数据(一)
			主要思路 目的: 根据输入的城市名,爬取该城市美团美食板块所有商家的数据.数据包括: 店名.评分.评论数量.均价.地址, 并将这些数据存入Excel中. 最后尝试对爬取到的数据做一个简单的分析. 克服 ... 
- Centos7安装以及设置Redis详细步骤
			一.Redis安装: 1.指定文件夹下下载redis安装包: [root@bogon ~]# mkdir /usr/local/soft/redis [root@bogon ~]# cd /usr/l ... 
- INTERSPEECH2020 语音情感分析论文之我见
			摘要:本文为大家带来InterSpeech2020 语音情感分析25篇论文中的其中8篇的总结. 本文分享自华为云社区<INTERSPEECH2020 语音情感分析论文总结一>,原文作者:T ... 
- 导出目录的JS代码,与目录的三级标题测试
			二级标题 三级标题 三级标题 三级标题 三级标题 三级标题 二级标题 三级标题 三级标题 三级标题 三级标题 三级标题 这里是现在页尾目录功能的代码源码: <!-- 目录索引列表生成 --> ... 
- 专家动态页面的实现——php基于CI框架的学习(二)
			以下是本次学习的页面 打开相关文件,整个定义了一个Expert类 class Expert extends CI_Controller{} 在Expert类里定义了几个参数以及说明其使用了哪些mode ... 
- matplotlib安装问题解决
			p.p1 { margin: 0; font: 11px Menlo; color: rgba(0, 0, 0, 1) } span.s1 { font-variant-ligatures: no-c ... 
- (原创)IconFont(矢量图标字体)在Winform中的应用
			一.前言 很多时候,使用矢量图形可以带来非常美观的界面效果,比如SVG的使用.但是Winform原生是不支持显示SVG图像的,所以退而求其次,可以使用IconFont来实现相似的矢量效果. 先来个图解 ... 
- 【CTF】图片隐写术 · 修复被修改尺寸的PNG图片
			前言 今天我们想来介绍一下关于图片隐写相关处理,以及修复被修改尺寸的PNG图片. 关于PNG图片的相关处理,是CTF Misc图片隐写术中极为基础的一项操作,笔者这里是想要提一些做题过程中发现的小技巧 ... 
