《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 許可權夠折騰半天了,這篇文章先就醬紫吧。