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. POJ_2253 Frogger 【最短路变形】

    一.题目 Frogger 二.分析 题意关键点就是那个青蛙距离.就是所有1到2的点的路径中,每条路径都可以确定一个最大值,这个最大值就是青蛙要跳的青蛙距离,然后要求这个青蛙距离最小值. 其实就是最短路 ...

  2. IDEA如何像ecplise一样添加jar包?

    以前使用ecplise开发代码,现在换成IDEA,有很多操作都不习惯,比如添加jar包.网上可以找到IDEA好几种添加jar包的方法,这里主要介绍在用IDEA开发时如何像ecplise一样添加jar包 ...

  3. 有必要了解的大数据知识(二) Hadoop

    前言 接上文,复习整理大数据相关知识点,这章节从MapReduce开始... MapReduce介绍 MapReduce思想在生活中处处可见.或多或少都曾接触过这种思想.MapReduce的思想核心是 ...

  4. 图解双链表(Java实现)

    原创公众号:bigsai 文章已收录在 全网都在关注的数据结构与算法学习仓库 前言 前面有很详细的讲过线性表(顺序表和链表),当时讲的链表以但链表为主,但实际上在实际应用中双链表的应用多一些就比如Li ...

  5. WPF3D立方体图形展开动画思路

    WPF3D立方体图形展开动画 效果图: 规定: 立方体中心为(000),棱长为2,则(111)(-1-1-1)等1,-1三维组合的八个点为其顶点 坐标系: 补充: WPF 3D 分为中心对称旋转(Ro ...

  6. docker部署nodejs项目应用

    之前笔者弄了一套nestjs项目放在自己服务器上,并用pm2管理进程. 现在要把pm2停止,尝试一下用docker容器,那么首先要安装docker 一.安装docker 由于笔者服务器的系统是cent ...

  7. IOC容器模拟实现

    运用反射机制和自定义注解模拟实现IOC容器,使其具有自动加载.自动装配和根据全限定类名获取Bean的功能. 一. 实现原理 1-1 IOC容器的本质 IOC容器可理解为是一个map,其中的一个entr ...

  8. .Net Core 路由处理

    用户请求接口路由,应用返回处理结果.应用中如何匹配请求的数据呢?为何能如此精确的找到对应的处理方法?今天就谈谈这个路由.路由负责匹配传入的HTTP请求,将这些请求发送到可以执行的终结点.终结点在应用中 ...

  9. SQLserver数据库安装教程

    大家好,这期给大家带来一期SQL server的安装教程 下载SQL Server 2019 Developer 官方网址: https://www.microsoft.com/zh-cn/sql-s ...

  10. (十二)struts2的类型转换

    所有的MVC框架,都属于表现层的解决方案,都需要负责收集用户请求参数,并将请求参数传给应用的控制器组件. 这时问题出现了,所有的请求参数都是字符串类型数据,因此MVC框架必须具备将这些字符串请求参数转 ...