本文链接 http://wossoneri.github.io/2018/08/30/[Android][Framework]crop-SystemServer-and-SystemFeature/

SystemServer服务裁剪

有些系统,因为应用场景的不同,需要的服务也不一样。比如Android Things,为了应对IOT的应用场景,它就裁剪掉了很多服务。下面介绍一下裁剪服务的方法。

关于服务,要提一下SystemServer,具体介绍见另一篇文章:http://wossoneri.github.io。SystemServer启动了系统的核心服务,除此之外,SystemServer还启动了很多其他服务,具体是在startOtherServices()方法中。我们要裁剪不需要的服务就可以从这里入手。

比如要关闭打印机服务:

可以直接把相关启动服务的代码注释掉:

  1. //mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);

当然这样修改后,以后如果要再打开,还需要再次修改SystemServer,然后编译jar包,push到设备使其生效。

所以建议使用下面的改法:

首先定义boolean变量,从全局属性读取配置,

  1. boolean disablePrinter = SystemProperties.getBoolean("config.disable_printer", false);

然后在启动服务的代码段添加对这个属性的判断:

  1. if (!disablePrinter && mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
  2. mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
  3. }

之后在MakeFile增加自定义的系统权限:

  1. PRODUCT_PROPERTY_OVERRIDES += \
  2. config.disable_printer=true

以后如果要打开这个服务,就把true变成false就可以了。

如果要调试,从修改设备的 /system/build.prop 然后重启即可。非常方便有木有!

如果要修改,删掉out目录下的build.prop,重新编译system(或者直接修改build.prop然后make snod),烧录启动系统之后,运行如下命令进行验证:

  1. service check printer

这样就不会再启动不需要的服务了。

裁剪服务引发的问题

服务不是你不让它Start就完事儿了,系统那么大,总有一些地方会获取服务对象做一些调用处理。比如我们刚裁减掉了打印机服务,然后打开Settings就遇到了crash:

  1. E AndroidRuntime: FATAL EXCEPTION: main
  2. E AndroidRuntime: Process: com.android.settings, PID: 3496
  3. E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.Settings}: java.lang.NullPointerException: Attempt to invoke interface method 'java.util.List android.print.IPrintManager.getPrintServices(int, int)' on a null object reference
  4. E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
  5. E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
  6. E AndroidRuntime: at android.app.ActivityThread.-wrap12(ActivityThread.java)
  7. E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
  8. E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
  9. E AndroidRuntime: at android.os.Looper.loop(Looper.java:154)
  10. E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6119)
  11. E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
  12. E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)
  13. E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)
  14. E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.util.List android.print.IPrintManager.getPrintServices(int, int)' on a null object reference
  15. E AndroidRuntime: at android.print.PrintManager.getPrintServices(PrintManager.java:635)
  16. E AndroidRuntime: at android.print.PrintServicesLoader.onStartLoading(PrintServicesLoader.java:88)
  17. E AndroidRuntime: at android.content.Loader.startLoading(Loader.java:290)
  18. E AndroidRuntime: at android.app.LoaderManagerImpl$LoaderInfo.start(LoaderManager.java:283)
  19. E AndroidRuntime: at android.app.LoaderManagerImpl.installLoader(LoaderManager.java:579)
  20. E AndroidRuntime: at android.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:566)
  21. E AndroidRuntime: at android.app.LoaderManagerImpl.initLoader(LoaderManager.java:619)
  22. E AndroidRuntime: at com.android.settings.search.DynamicIndexableContentMonitor.register(DynamicIndexableContentMonitor.java:136)
  23. E AndroidRuntime: at com.android.settings.SettingsActivity.onStart(SettingsActivity.java:868)
  24. E AndroidRuntime: at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249)
  25. E AndroidRuntime: at android.app.Activity.performStart(Activity.java:6737)
  26. E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2628)
  27. E AndroidRuntime: ... 9 more

通过堆栈信息,可以知道PrintManager.getPrintServices出现了空指针。这里也不用看代码就能猜到,因为我们开机没有启动打印服务,所以肯定get不到这个服务的。

然后考虑修改方案,增加非空保护是不是就可以了?Naive!我们的目的是裁剪打印服务,所以我们的修改点并不在这个服务本身,而是删除所以调用这个服务的地方。

所以堆栈显示的PrintManager,PrintServicesLoader什么的统统不要改,我们要看代码结构,看看是怎么调用进来的。通过阅读代码,了解到系统里有很多Loader类型的对象,其中一个子类就是PrintServicesLoader。然后这些Loader是由LoaderManager管理启动的。而LoaderManager在DynamicIndexableContentMonitor.java出现过一次初始化操作。

看下DynamicIndexableContentMonitor.java代码:

  1. public void register(Activity activity, int loaderId) {
  2. ...
  3. boolean hasFeaturePrinting = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING);
  4. ...
  5. if (hasFeaturePrinting) {
  6. activity.getLoaderManager().initLoader(loaderId, null, this);
  7. }
  8. ...

有木有发现一个熟悉的代码?

对,代码里再次出现了一个有关SystemFeature的判断!上一次出现时SystemServer启动服务前也做了相同的判断。

所以要裁剪掉打印机服务,我们只需要将FEATURE_PRINTING关闭即可。

通过修改SystemFeature判断后,在SystemServer里面的裁剪代码就可以不再添加了。但是有些服务的裁剪Android并没有添加系统特性的处理,所以还是建议使用我的方法进行裁剪。

SystemFeature加载流程

先看一看FEATURE_PRINTING

PackageManager.java

  1. /**
  2. * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
  3. * The device supports printing.
  4. */
  5. @SdkConstant(SdkConstantType.FEATURE)
  6. public static final String FEATURE_PRINTING = "android.software.print";
  7. /**
  8. * Get a list of features that are available on the
  9. * system.
  10. *
  11. * @return An array of FeatureInfo classes describing the features
  12. * that are available on the system, or null if there are none(!!).
  13. */
  14. public abstract FeatureInfo[] getSystemAvailableFeatures();
  15. /**
  16. * Check whether the given feature name is one of the available features as
  17. * returned by {@link #getSystemAvailableFeatures()}. This tests for the
  18. * presence of <em>any</em> version of the given feature name; use
  19. * {@link #hasSystemFeature(String, int)} to check for a minimum version.
  20. *
  21. * @return Returns true if the devices supports the feature, else false.
  22. */
  23. public abstract boolean hasSystemFeature(String name);
  24. /**
  25. * Check whether the given feature name and version is one of the available
  26. * features as returned by {@link #getSystemAvailableFeatures()}. Since
  27. * features are defined to always be backwards compatible, this returns true
  28. * if the available feature version is greater than or equal to the
  29. * requested version.
  30. *
  31. * @return Returns true if the devices supports the feature, else false.
  32. */
  33. public abstract boolean hasSystemFeature(String name, int version);

都是抽象方法,我们去PMS查找对应的实现

PackageManagerService.java

  1. public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
  2. synchronized (mPackages) {
  3. final ArrayList<FeatureInfo> res = new ArrayList<>(mAvailableFeatures.values());
  4. final FeatureInfo fi = new FeatureInfo();
  5. fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
  6. FeatureInfo.GL_ES_VERSION_UNDEFINED);
  7. res.add(fi);
  8. return new ParceledListSlice<>(res);
  9. }
  10. }
  11. @Override
  12. public boolean hasSystemFeature(String name, int version) {
  13. synchronized (mPackages) {
  14. final FeatureInfo feat = mAvailableFeatures.get(name);
  15. if (feat == null) {
  16. return false;
  17. } else {
  18. return feat.version >= version;
  19. }
  20. }
  21. }

这里的逻辑都是通过mAvailableFeatures得到所有的feature,查找该成员变量的相关代码

  1. final ArrayMap<String, FeatureInfo> mAvailableFeatures;
  2. SystemConfig systemConfig = SystemConfig.getInstance();
  3. mGlobalGids = systemConfig.getGlobalGids();
  4. mSystemPermissions = systemConfig.getSystemPermissions();
  5. mAvailableFeatures = systemConfig.getAvailableFeatures();

了解到,首先获取一个SystemConfig的单例,然后通过getAvailableFeatures方法获取可用的feature。

SystemConfig.java

  1. // These are the features this devices supports that were read from the
  2. // system configuration files.
  3. final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
  4. public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
  5. return mAvailableFeatures;
  6. }
  7. private void addFeature(String name, int version) {
  8. FeatureInfo fi = mAvailableFeatures.get(name);
  9. if (fi == null) {
  10. fi = new FeatureInfo();
  11. fi.name = name;
  12. fi.version = version;
  13. mAvailableFeatures.put(name, fi);
  14. } else {
  15. fi.version = Math.max(fi.version, version);
  16. }
  17. }
  18. private void removeFeature(String name) {
  19. if (mAvailableFeatures.remove(name) != null) {
  20. Slog.d(TAG, "Removed unavailable feature " + name);
  21. }
  22. }

根据mAvailableFeatures的注释,设备支持的feature是从配置文件里读取出来的。调用读取配置文件的地方是:

  1. SystemConfig() {
  2. // Read configuration from system
  3. readPermissions(Environment.buildPath(
  4. Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
  5. // Read configuration from the old permissions dir
  6. readPermissions(Environment.buildPath(
  7. Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
  8. // Allow ODM to customize system configs around libs, features and apps
  9. int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
  10. readPermissions(Environment.buildPath(
  11. Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
  12. readPermissions(Environment.buildPath(
  13. Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
  14. // Only allow OEM to customize features
  15. readPermissions(Environment.buildPath(
  16. Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
  17. readPermissions(Environment.buildPath(
  18. Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
  19. }

到此就很明白了,它是读取了几个目录:

  • /system/etc/permission
  • /system/etc/sysconfig
  • /oem/etc/permission
  • /oem/etc/sysconfig
  • /odm/etc/permission
  • /odm/etc/sysconfig

然后遍历xml文件,进行解析处理。SystemFeature就是解析的Feature标签。

最后再总结一下加载流程:

屏蔽SystemFeature

知道原理就好做了,在系统扫描的几个目录中使用grep命令查找控制打印机的字串,找到:

/system/etc/permission/handheld_core_hardware.xml

  1. <!-- basic system services -->
  2. <feature name="android.software.app_widgets" />
  3. <feature name="android.software.connectionservice" />
  4. <feature name="android.software.voice_recognizers" notLowRam="true" />
  5. <feature name="android.software.backup" />
  6. <feature name="android.software.home_screen" />
  7. <feature name="android.software.input_methods" />
  8. <feature name="android.software.print" /> <------这个就是打印特性

将其注释掉就可以在手机进行测试了。

但是,我们还需要修改源码,保证以后编译系统这个值都是被屏蔽的。

查找MakeFile,找到如下:

  1. PRODUCT_COPY_FILES := \ frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml

这个文件在源码中的位置是frameworks/native/data/etc/。找到该源码文件,将不要的Feature注释掉,然后重新编译源码,启动系统,一切正常!打印机相关的服务彻底被屏蔽掉了,系统启动速度,资源消耗又变小了一点点。嗯,是很小的一点点,我们还可以把VR,红外线等等很多服务裁剪掉,以适应不同应用场景下的精简系统。

[Android][Framework]裁剪SystemServer服务以及关闭SystemFeature的更多相关文章

  1. Android中使用HTTP服务

    在Android中,除了使用java.net包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作.Android SDK附带了Apache的HttpClient API.Apache Ht ...

  2. [Android][Framework] 添加系统服务

    新博客地址 http://wossoneri.github.io/2018/09/15/[Android][Framework]create-system-service/ 做系统开发,有时候需要自己 ...

  3. android启动之SystemServer启动

    SystemServer是Android系统的核心,APK应用中可以直接交互的大部分系统服务都在该进程中执行,常见的比方WindowManagerServer(Wms).ActivityManager ...

  4. Android FrameWork 学习之Android 系统源码调试

    这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...

  5. 前阿里技术总监手打:452页Android Framework 精编内核解析

    众所周知,Android是一个基于Linux实现的操作系统.但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别. 所以Andro ...

  6. [译]:Xamarin.Android平台功能——位置服务

    返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...

  7. ArcGIS Runtime for Android 使用异步GP服务绘制等值线

    关于基于Android上ArcGIS Server GP服务的调用,已经有前辈给出了很好的例子: http://blog.csdn.net/esrichinacd/article/details/92 ...

  8. Android开发-API指南-服务

    Service 英文原文:http://developer.android.com/guide/components/services.html 采集(更新)日期:2014-12-23 原博客:htt ...

  9. 【转】Android:Bluetooth 的打开和关闭--不错

    原文网址:http://www.ifeegoo.com/android-turn-on-and-turn-off-bluetooth.html 摘要:Android 中打开和关闭 Bluetooth ...

随机推荐

  1. 1.Git起步-Git的三种状态以及三种工作区域、CVCS与DVCS的区别、Git基本工作流程

    1.Git基础 版本控制系统是一种用于记录一个或多个文件内容变化,以便将来查阅恢复特定版本修订情况的系统. Git是一种分布式版本控制系统(Distributed Version Control Sy ...

  2. 解决关于 vue项目中 点击按钮路由多了个问号

    问题描述: 在vue项目开发过程中,点击按钮结果页面刷新了一遍 后来发现路径变成了 localhost:8080/?#/login 原因: 这里是 form 表单,点击了button 按钮,触发了他的 ...

  3. logstash收集TCP端口日志

    logstash收集TCP端口日志官方地址:https://www.elastic.co/guide/en/logstash-versioned-plugins/current/index.html ...

  4. Android--Loaders

    前言 Loaders,装载机,适用于Android3.0以及更高的版本,它提供了一套在UI的主线程中异步加载数据的框架.使用Loaders可以非常简单的在Activity或者Fragment中异步加载 ...

  5. 聊聊JVM(二)说说GC的一些常见概念

    转自CSDN 上一篇总结GC的基础算法,各种GC收集器的基本原理,还是比较粗粒度的概念.这篇会整理一些GC的常见概念,理解了这些概念,相信对GC有更加深入的理解 1. 什么时候会触发Minor GC? ...

  6. 小程序开发--移动端分辨率与rpx

    首先说一个很有意思的问题:一块720p的屏幕和1080p的屏幕那个大? 这个问题很有代表性,如果手机竖着放,720p=720px*1280px,而1080p=1080px*1920px;那么在宽度上, ...

  7. ⑤早起的鸟儿有虫吃-JSTL核心标签库[收藏]

    介绍 JSTL 全名为Java Server Pages Standard Tag Library(JSP Standard Tag Library),它的中文名称为JSP 标准标签函数库. Web  ...

  8. [转&精]IO_STACK_LOCATION与IRP的一点笔记

    IO_STACK_LOCATION和IRP算是驱动中两个很基础的东西,为了理解这两个东西,找了一点资料. 1. IRP可以看成是Win32窗口程序中的消息(Message),DEVICE_OBJECT ...

  9. 阿里云CentOS安装配置Python3.7及pip3

    一.安装Python3.7 安装依赖包 yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqli ...

  10. 各个系统下ping IP+端口的方法

    前言 做开发的人员,可能都会遇到网络的一些问题.这时一般都需要测试指定的网络是否能正常访问.通常在windows系统下,在cmd里直接用命令ping指定的IP或者域名就可以快速的知道这个地址是否是可以 ...