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?的更多相关文章

  1. iOS System Services

    System Services is a singleton class to gather all available information about a device. Over 75 met ...

  2. RH133读书 笔记(4) - Lab 4 System Services

    Lab 4 System Services Goal: Develop skills using system administration tools and setting up and admi ...

  3. 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 ...

  4. (C# Window Service) Verify that you have sufficient privileges to start system services

    写了个Windows Service, 用Wix 写了个Installer,编译通过,生成了msi 安装文件,但是安装的时候总是提示: Product: KingPro Service -- Erro ...

  5. 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 ...

  6. 异常:已引发: "设置 connectionId 时引发了异常。" (System.Xaml.XamlObjectWriterException) 引发了一个 System.Xaml.XamlObjectWriterException: "

    项目中,引用一个富文本编辑器,SmithHtmlEditor,进入页面的时候异常. 在View和ViewModel所在的类库引用. 还需要在Main中引用.

  7. 异常:已捕获: "Error creating context 'spring.root': 未将对象引用设置到对象的实例。" (System.Configuration.ConfigurationErrorsException) 捕获到一个 System.Configuration.ConfigurationErrorsException: "Error creating context 'sp

    查看所指定name的context是否注册成功,以后用此容器来获取其中的object. 常见的使用方式: Application_Start中使用ContextRegistry.GetContext( ...

  8. 写一个system.data.entity的simpledatarepo,实现crudq这些功能,不需要引入entityframework,直接可以使用,用到objectset

    note:you can delete reference of entityframework when using this classes.it`s just a simple repohelp ...

  9. 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盘 ...

随机推荐

  1. RabbitMQ 入门 (Go) - 3. 模拟传感器,生成数据并发布

    现在,我们需要模拟传感器,生成数据,并发布到 RabbitMQ. 建立传感器项目 在 GOPATH src 下建立文件夹 sensors,使用 go mod init 初始化,并创建 main.go. ...

  2. Tkinter系列教程01—引言和安装Tk—Python GUI编程

    目录 Tkinter教程系列01--引言和安装Tk 引言 什么是Tkinter 安装 Tk 为 Windows 安装 Tk 验证是否安装正确 为 GNU/Linux 安装 Tk 使用 Linux 的包 ...

  3. 第3 章 : Kubernetes 核心概念

    Kubernetes 核心概念 本文整理自 CNCF 和阿里巴巴联合举办的云原生技术公开课的课时 3:Kubernetes 核心概念.本次课程中,阿里巴巴资深技术专家.CNCF 9个 TCO 之一 李 ...

  4. BUAA_OS lab2 难点梳理

    BUAA_OS lab2 难点梳理 实验重点 所列出的实验重点为笔者在进行lab2过程中认为需要深刻理解的部分. 进行内存访问的流程 熟悉mips内存映射布局,即理解mmu.h内图 二级页表的理解和实 ...

  5. 利用卷影拷贝服务提取ntds.dit

    0x01 前言 通常情况下,即使拥有管理员权限,也无法读取域控制器中的C:\Windows\NTDS\ntds.dit文件.那么什么是ntds.dit呢? ntds.dit文件是一个数据库,用于存储A ...

  6. Flutter 状态管理- 使用 MobX

    文 / Paul Halliday, developer.school 创始人 众所周知,状态管理是每个软件项目都需要持续迭代更新的方向.它并不是一个「一次性」的工作, 而需要不断确保你遵循的最佳实践 ...

  7. JAVAEE_Servlet_24_HttpSession实现原理

    关于JavaWeb中的HttpSession (一) * Session表示会话,不止存在于JavaWeb之中,只要是Web开发都会存在这种机制 * Session包:javax.servlet.ht ...

  8. ASP.NET Core扩展库之Http请求模拟

    如今,完全独立的业务应用几乎不存在,不管是在企业内部微服务之间的调用,还是与外部第三方服务的调用,Http的API交互是常见的场景,这些实际情况给我们的开发带来了比较大的挑战,一是第三方服务可能会牵制 ...

  9. 也谈如何写一个Webserver(-)

    关于如何写一个Webserver,很多大咖都发表过类似的文章.趁着这个五一假期,我也来凑个份子. 我写Webserver的原因,还得从如何将http协议传送的消息解析说起.当时,我只是想了解一下htt ...

  10. FastAPI + Vue 前后端分离 接口自动化测试工具 apiAutoTestWeb

    apiAutoTestWeb使用说明 apiAutoTestWeb是为apiAutoTest的可视化版本,其采用前后端分离(FastAPI + Vue2)方式实现 具体使用: Python3 + Fa ...