[Framework][Android]裁剪SystemServer服务以及关闭SystemFeature

阅读目录

本文链接 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()方法中。我们要裁剪不需要的服务就可以从这里入手。

比如要关闭打印机服务:

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

<span style="color:#000000"><code><span style="color:green">//mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);</span></code></span>

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

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

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

<span style="color:#000000"><code><span style="color:#0000ff">boolean</span> disablePrinter = SystemProperties.getBoolean(<span style="color:#a31515">"config.disable_printer"</span>, <span style="color:#0000ff">false</span>);</code></span>

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

<span style="color:#000000"><code><span style="color:#0000ff">if</span> (!disablePrinter && mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
    mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}</code></span>

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

<span style="color:#000000"><code>PRODUCT_PROPERTY_OVERRIDES += \
    config.disable_printer=<span style="color:#0000ff">true</span></code></span>

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

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

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

<span style="color:#000000"><code>service check printer</code></span>

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

回到顶部

裁剪服务引发的问题

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

<span style="color:#000000"><code>E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.android.settings, PID: 3496
E AndroidRuntime: java.lang.RuntimeException: Unable to <span style="color:#0000ff">start</span> activity ComponentInfo{com.android.settings/com.android.settings.Settings}: java.lang.NullPointerException: Attempt <span style="color:#0000ff">to</span> invoke <span style="color:#0000ff">interface</span> method <span style="color:#a31515">'java.util.List android.print.IPrintManager.getPrintServices(int, int)'</span> <span style="color:#0000ff">on</span> a <span style="color:#a31515">null</span> <span style="color:#0000ff">object</span> <span style="color:#0000ff">reference</span>
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread.-wrap12(ActivityThread.java)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.os.Handler.dispatchMessage(Handler.java:102)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.os.Looper.loop(Looper.java:154)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread.main(ActivityThread.java:6119)
E AndroidRuntime:   <span style="color:#0000ff">at</span> java.lang.reflect.Method.invoke(<span style="color:#0000ff">Native</span> Method)
E AndroidRuntime:   <span style="color:#0000ff">at</span> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)
E AndroidRuntime:   <span style="color:#0000ff">at</span> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)
E AndroidRuntime: Caused <span style="color:#0000ff">by</span>: java.lang.NullPointerException: Attempt <span style="color:#0000ff">to</span> invoke <span style="color:#0000ff">interface</span> method <span style="color:#a31515">'java.util.List android.print.IPrintManager.getPrintServices(int, int)'</span> <span style="color:#0000ff">on</span> a <span style="color:#a31515">null</span> <span style="color:#0000ff">object</span> <span style="color:#0000ff">reference</span>
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.print.PrintManager.getPrintServices(PrintManager.java:635)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.print.PrintServicesLoader.onStartLoading(PrintServicesLoader.java:88)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.content.Loader.startLoading(Loader.java:290)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.LoaderManagerImpl$LoaderInfo.start(LoaderManager.java:283)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.LoaderManagerImpl.installLoader(LoaderManager.java:579)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:566)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.LoaderManagerImpl.initLoader(LoaderManager.java:619)
E AndroidRuntime:   <span style="color:#0000ff">at</span> com.android.settings.search.DynamicIndexableContentMonitor.register(DynamicIndexableContentMonitor.java:136)
E AndroidRuntime:   <span style="color:#0000ff">at</span> com.android.settings.SettingsActivity.onStart(SettingsActivity.java:868)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.Activity.performStart(Activity.java:6737)
E AndroidRuntime:   <span style="color:#0000ff">at</span> android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2628)
E AndroidRuntime:   ... 9 more</code></span>

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

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

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

看下DynamicIndexableContentMonitor.java代码:

<span style="color:#000000"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">register</span>(Activity activity, <span style="color:#0000ff">int</span> loaderId) {
    ...
    <span style="color:#0000ff">boolean</span> hasFeaturePrinting = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING);
    ...
    <span style="color:#0000ff">if</span> (hasFeaturePrinting) {
        activity.getLoaderManager().initLoader(loaderId, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">this</span>);
    }
    ...</code></span>

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

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

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

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

回到顶部

SystemFeature加载流程

先看一看FEATURE_PRINTING

PackageManager.java

<span style="color:#000000"><code><span style="color:green">/**
 * Feature for {<span style="color:gray">@link</span> #getSystemAvailableFeatures} and {<span style="color:gray">@link</span> #hasSystemFeature}:
 * The device supports printing.
 */</span>
<span style="color:#2b91af">@SdkConstant</span>(SdkConstantType.FEATURE)
<span style="color:#0000ff">public</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">final</span> String FEATURE_PRINTING = <span style="color:#a31515">"android.software.print"</span>;

<span style="color:green">/**
 * Get a list of features that are available on the
 * system.
 *
 * <span style="color:gray">@return</span> An array of FeatureInfo classes describing the features
 * that are available on the system, or null if there are none(!!).
 */</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">abstract</span> FeatureInfo[] getSystemAvailableFeatures();

<span style="color:green">/**
 * Check whether the given feature name is one of the available features as
 * returned by {<span style="color:gray">@link</span> #getSystemAvailableFeatures()}. This tests for the
 * presence of <em>any</em> version of the given feature name; use
 * {<span style="color:gray">@link</span> #hasSystemFeature(String, int)} to check for a minimum version.
 *
 * <span style="color:gray">@return</span> Returns true if the devices supports the feature, else false.
 */</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">abstract</span> <span style="color:#0000ff">boolean</span> <span style="color:#a31515">hasSystemFeature</span>(String name);

<span style="color:green">/**
 * Check whether the given feature name and version is one of the available
 * features as returned by {<span style="color:gray">@link</span> #getSystemAvailableFeatures()}. Since
 * features are defined to always be backwards compatible, this returns true
 * if the available feature version is greater than or equal to the
 * requested version.
 *
 * <span style="color:gray">@return</span> Returns true if the devices supports the feature, else false.
 */</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">abstract</span> <span style="color:#0000ff">boolean</span> <span style="color:#a31515">hasSystemFeature</span>(String name, <span style="color:#0000ff">int</span> version);</code></span>

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

PackageManagerService.java

<span style="color:#000000"><code><span style="color:#0000ff">public</span> <span style="color:#2b91af">@NonNull</span> ParceledListSlice<FeatureInfo> <span style="color:#a31515">getSystemAvailableFeatures</span>() {
    <span style="color:#0000ff">synchronized</span> (mPackages) {
        <span style="color:#0000ff">final</span> ArrayList<FeatureInfo> res = <span style="color:#0000ff">new</span> ArrayList<>(mAvailableFeatures.values());

        <span style="color:#0000ff">final</span> FeatureInfo fi = <span style="color:#0000ff">new</span> FeatureInfo();
        fi.reqGlEsVersion = SystemProperties.getInt(<span style="color:#a31515">"ro.opengles.version"</span>,
                                                    FeatureInfo.GL_ES_VERSION_UNDEFINED);
        res.add(fi);

        <span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> ParceledListSlice<>(res);
    }
}

<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">boolean</span> <span style="color:#a31515">hasSystemFeature</span>(String name, <span style="color:#0000ff">int</span> version) {
    <span style="color:#0000ff">synchronized</span> (mPackages) {
        <span style="color:#0000ff">final</span> FeatureInfo feat = mAvailableFeatures.get(name);
        <span style="color:#0000ff">if</span> (feat == <span style="color:#0000ff">null</span>) {
            <span style="color:#0000ff">return</span> <span style="color:#0000ff">false</span>;
        } <span style="color:#0000ff">else</span> {
            <span style="color:#0000ff">return</span> feat.version >= version;
        }
    }
}</code></span>

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

<span style="color:#000000"><code><span style="color:#0000ff">final</span> ArrayMap<String, FeatureInfo> mAvailableFeatures;

SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();</code></span>

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

SystemConfig.java

<span style="color:#000000"><code><span style="color:green">// These are the features this devices supports that were read from the</span>
<span style="color:green">// system configuration files.</span>
<span style="color:#0000ff">final</span> ArrayMap<String, FeatureInfo> mAvailableFeatures = <span style="color:#0000ff">new</span> ArrayMap<>();

<span style="color:#0000ff">public</span> ArrayMap<String, FeatureInfo> <span style="color:#a31515">getAvailableFeatures</span>() {
    <span style="color:#0000ff">return</span> mAvailableFeatures;
}

<span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">addFeature</span>(String name, <span style="color:#0000ff">int</span> version) {
    FeatureInfo fi = mAvailableFeatures.get(name);
    <span style="color:#0000ff">if</span> (fi == <span style="color:#0000ff">null</span>) {
        fi = <span style="color:#0000ff">new</span> FeatureInfo();
        fi.name = name;
        fi.version = version;
        mAvailableFeatures.put(name, fi);
    } <span style="color:#0000ff">else</span> {
        fi.version = Math.max(fi.version, version);
    }
}

<span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">removeFeature</span>(String name) {
    <span style="color:#0000ff">if</span> (mAvailableFeatures.remove(name) != <span style="color:#0000ff">null</span>) {
        Slog.d(TAG, <span style="color:#a31515">"Removed unavailable feature "</span> + name);
    }
}</code></span>

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

<span style="color:#000000"><code>SystemConfig() {
    <span style="color:green">// Read configuration from system</span>
    readPermissions(Environment.buildPath(
        Environment.getRootDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"sysconfig"</span>), ALLOW_ALL);
    <span style="color:green">// Read configuration from the old permissions dir</span>
    readPermissions(Environment.buildPath(
        Environment.getRootDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"permissions"</span>), ALLOW_ALL);
    <span style="color:green">// Allow ODM to customize system configs around libs, features and apps</span>
    <span style="color:#0000ff">int</span> odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
    readPermissions(Environment.buildPath(
        Environment.getOdmDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"sysconfig"</span>), odmPermissionFlag);
    readPermissions(Environment.buildPath(
        Environment.getOdmDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"permissions"</span>), odmPermissionFlag);
    <span style="color:green">// Only allow OEM to customize features</span>
    readPermissions(Environment.buildPath(
        Environment.getOemDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"sysconfig"</span>), ALLOW_FEATURES);
    readPermissions(Environment.buildPath(
        Environment.getOemDirectory(), <span style="color:#a31515">"etc"</span>, <span style="color:#a31515">"permissions"</span>), ALLOW_FEATURES);
}</code></span>

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

  • /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

<span style="color:#000000"><code>    <span style="color:green"><!-- basic system services --></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.app_widgets"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.connectionservice"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.voice_recognizers"</span> <span style="color:red">notLowRam</span>=<span style="color:#a31515">"true"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.backup"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.home_screen"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.input_methods"</span> /></span>
    <span style="color:#0000ff"><<span style="color:#0000ff">feature</span> <span style="color:red">name</span>=<span style="color:#a31515">"android.software.print"</span> /></span>   <span style="color:#0000ff"><<span style="color:#0000ff">------这个就是打印特性</span></span></code></span>

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

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

查找MakeFile,找到如下:

<span style="color:#000000"><code>PRODUCT_COPY_FILES := \ frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml</code></span>

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

http://www.cgpwyj.cn/
http://news.cgpwyj.cn/
http://item.cgpwyj.cn/
http://www.peacemind.com.cn/
http://news.peacemind.com.cn/
http://item.peacemind.com.cn/
http://www.tasknet.com.cn/
http://news.tasknet.com.cn/
http://item.tasknet.com.cn/
http://www.ownbar.cn/
http://news.ownbar.cn/
http://item.ownbar.cn
http://www.shtarchao.net.cn/
http://news.shtarchao.net.cn/
http://item.shtarchao.net.cn/
http://www.metroworld.com.cn/
http://news.metroworld.com.cn/
http://www.cngodo.cn/
http://news.cngodo.cn/
http://item.cngodo.cn/
http://www.gzrdbp.cn/
http://news.gzrdbp.cn/
http://item.gzrdbp.cn/
http://www.dnapt.cn/
http://news.dnapt.cn/
http://item.dnapt.cn/
http://www.ncxlk.cn/
http://news.ncxlk.cn/
http://item.ncxlk.cn/
http://www.zgxxyp.cn/
http://news.zgxxyp.cn/
http://item.zgxxyp.cn/
http://www.sjjdvr.cn/
http://news.sjjdvr.cn/
http://item.sjjdvr.cn/
http://www.sujinkeji.cn/
http://news.sujinkeji.cn/
http://item.sujinkeji.cn/
http://www.zsjxbd.cn/
http://news.zsjxbd.cn/
http://item.zsjxbd.cn/
http://www.yesgas.cn/
http://news.yesgas.cn/
http://item.yesgas.cn/
http://www.quickpass.sh.cn/
http://news.quickpass.sh.cn/
http://item.quickpass.sh.cn/
http://www.jspcrm.cn/
http://news.jspcrm.cn/
http://item.jspcrm.cn/
http://www.yjdwpt.cn/
http://news.yjdwpt.cn/
http://item.yjdwpt.cn/
http://www.henanwulian.cn/
http://news.henanwulian.cn/
http://item.henanwulian.cn/
http://www.hhrshh.cn/
http://news.hhrshh.cn/
http://item.hhrshh.cn/
http://www.gpgold.cn/
http://news.gpgold.cn/
http://item.gpgold.cn/
http://www.jingzhuiyou.cn/
http://news.jingzhuiyou.cn/
http://item.jingzhuiyou.cn/ 

猜你喜欢

转载自blog.csdn.net/qq_38462321/article/details/82253127