《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

@Overridepublic 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

先上图:

@startumltitle \n如何实现一个 System Services?\nskinparam backgroundColor #EEEBDCskinparam handwritten truehide empty descriptionstate 1.编写AIDL文件1.编写AIDL文件 --> 2.Context定义变量1.编写AIDL文件:IWuXiaolongManager.aidl2.Context定义变量 --> 3.编写零碎服务类2.Context定义变量:String WUXIAOLONG_SERVICE = "wuxiaolong"note left of 2.Context定义变量 : 执行make update-api,\n更新接口3.编写零碎服务类 --> 4.注册零碎服务类3.编写零碎服务类:WuXiaolongManagerService.java4.注册零碎服务类--> 5.编写Manager类note right of 4.注册零碎服务类  波及SELinux权限end note5.编写Manager类 --> 6.注册Managernote right of 5.编写Manager类  1.写成单例  2.@Nullable注解end note5.编写Manager类:WuXiaolongManager.java6.注册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.txtindex 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.txtindex 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=104-08 15:41:38.802 12717 12758 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: PowerManagerService04-08 15:41:38.802 12717 12758 E AndroidRuntime: java.lang.IllegalStateException: android.os.ServiceManager$ServiceNotFoundException: No service published for: wuxiaolong04-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: wuxiaolong04-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 shellsetenforce 0 (长期禁用掉SELinux)getenforce  (失去后果为Permissive)

长期禁用掉 SELinux,性能就失常了,对于 SELinux 这里不说了,前面有机会写篇 SELinux 文章。

最初 Log 打印如下:

Line 832: 04-08 16:08:55.290 17649 17690 D wxl     : SystemServiceRegistry registerServiceLine 833: 04-08 16:08:55.290 17649 17690 D wxl     : WuXiaolongManager getInstanceLine 835: 04-08 16:08:55.292 17649 17690 D wxl     : WuXiaolongManager getNameLine 836: 04-08 16:08:55.293 17649 17690 D wxl     : Name=WuXiaolong..

手写个 System Service 实际过后没那么简略,光 SELinux 权限够折腾半天了,这篇文章先就酱紫吧。