[Android][Framework]裁剪SystemServer服務以及關閉SystemFeature

來源:互聯網
上載者:User

本文連結 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()方法中。我們要裁剪不需要的服務就可以從這裡入手。

比如要關閉印表機服務:

可以直接把相關啟動服務的代碼注釋掉:

//mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);

當然這樣修改後,以後如果要再開啟,還需要再次修改SystemServer,然後編譯jar包,push到裝置使其生效。

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

首先定義boolean變數,從全域屬性讀取配置,

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

然後在啟動服務的程式碼片段添加對這個屬性的判斷:

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

之後在MakeFile增加自訂的系統許可權:

PRODUCT_PROPERTY_OVERRIDES += \    config.disable_printer=true

以後如果要開啟這個服務,就把true變成false就可以了。

如果要調試,從修改裝置的 /system/build.prop 然後重啟即可。非常方便有木有!

如果要修改,刪掉out目錄下的build.prop,重新編譯system(或者直接修改build.prop然後make snod),燒錄啟動系統之後,運行如下命令進行驗證:

service check printer

這樣就不會再啟動不需要的服務了。

裁剪服務引發的問題

服務不是你不讓它Start就完事兒了,系統那麼大,總有一些地方會擷取服務物件做一些調用處理。比如我們剛裁減掉了印表機服務,然後開啟Settings就遇到了crash:

E AndroidRuntime: FATAL EXCEPTION: mainE AndroidRuntime: Process: com.android.settings, PID: 3496E 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 referenceE AndroidRuntime:   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)E AndroidRuntime:   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)E AndroidRuntime:   at android.app.ActivityThread.-wrap12(ActivityThread.java)E AndroidRuntime:   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)E AndroidRuntime:   at android.os.Handler.dispatchMessage(Handler.java:102)E AndroidRuntime:   at android.os.Looper.loop(Looper.java:154)E AndroidRuntime:   at android.app.ActivityThread.main(ActivityThread.java:6119)E AndroidRuntime:   at java.lang.reflect.Method.invoke(Native Method)E AndroidRuntime:   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)E AndroidRuntime:   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)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 referenceE AndroidRuntime:   at android.print.PrintManager.getPrintServices(PrintManager.java:635)E AndroidRuntime:   at android.print.PrintServicesLoader.onStartLoading(PrintServicesLoader.java:88)E AndroidRuntime:   at android.content.Loader.startLoading(Loader.java:290)E AndroidRuntime:   at android.app.LoaderManagerImpl$LoaderInfo.start(LoaderManager.java:283)E AndroidRuntime:   at android.app.LoaderManagerImpl.installLoader(LoaderManager.java:579)E AndroidRuntime:   at android.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:566)E AndroidRuntime:   at android.app.LoaderManagerImpl.initLoader(LoaderManager.java:619)E AndroidRuntime:   at com.android.settings.search.DynamicIndexableContentMonitor.register(DynamicIndexableContentMonitor.java:136)E AndroidRuntime:   at com.android.settings.SettingsActivity.onStart(SettingsActivity.java:868)E AndroidRuntime:   at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249)E AndroidRuntime:   at android.app.Activity.performStart(Activity.java:6737)E AndroidRuntime:   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2628)E AndroidRuntime:   ... 9 more

通過堆棧資訊,可以知道PrintManager.getPrintServices出現了null 指標。這裡也不用看代碼就能猜到,因為我們開機沒有啟動列印服務,所以肯定get不到這個服務的。

然後考慮修改方案,增加非空保護是不是就可以了?Naive!我們的目的是裁剪列印服務,所以我們的修改點並不在這個服務本身,而是刪除所以調用這個服務的地方。

所以堆棧顯示的PrintManager,PrintServicesLoader什麼的統統不要改,我們要看代碼結構,看看是怎麼調用進來的。通過閱讀代碼,瞭解到系統裡有很多Loader類型的對象,其中一個子類就是PrintServicesLoader。然後這些Loader是由LoaderManager管理啟動的。而LoaderManager在DynamicIndexableContentMonitor.java出現過一次初始化操作。

看下DynamicIndexableContentMonitor.java代碼:

public void register(Activity activity, int loaderId) {    ...    boolean hasFeaturePrinting = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING);    ...    if (hasFeaturePrinting) {        activity.getLoaderManager().initLoader(loaderId, null, this);    }    ...

有木有發現一個熟悉的代碼?

對,代碼裡再次出現了一個有關SystemFeature的判斷!上一次出現時SystemServer啟動服務前也做了相同的判斷。

所以要裁剪掉印表機服務,我們只需要將FEATURE_PRINTING關閉即可。

通過修改SystemFeature判斷後,在SystemServer裡面的裁剪代碼就可以不再添加了。但是有些服務的裁剪Android並沒有添加系統特性的處理,所以還是建議使用我的方法進行裁剪。

SystemFeature載入流程

先看一看FEATURE_PRINTING

PackageManager.java

/** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device supports printing. */@SdkConstant(SdkConstantType.FEATURE)public static final String FEATURE_PRINTING = "android.software.print";/** * Get a list of features that are available on the * system. * * @return An array of FeatureInfo classes describing the features * that are available on the system, or null if there are none(!!). */public abstract FeatureInfo[] getSystemAvailableFeatures();/** * Check whether the given feature name is one of the available features as * returned by {@link #getSystemAvailableFeatures()}. This tests for the * presence of <em>any</em> version of the given feature name; use * {@link #hasSystemFeature(String, int)} to check for a minimum version. * * @return Returns true if the devices supports the feature, else false. */public abstract boolean hasSystemFeature(String name);/** * Check whether the given feature name and version is one of the available * features as returned by {@link #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. * * @return Returns true if the devices supports the feature, else false. */public abstract boolean hasSystemFeature(String name, int version);

都是抽象方法,我們去PMS尋找對應的實現

PackageManagerService.java

public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {    synchronized (mPackages) {        final ArrayList<FeatureInfo> res = new ArrayList<>(mAvailableFeatures.values());        final FeatureInfo fi = new FeatureInfo();        fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",                                                    FeatureInfo.GL_ES_VERSION_UNDEFINED);        res.add(fi);        return new ParceledListSlice<>(res);    }}@Overridepublic boolean hasSystemFeature(String name, int version) {    synchronized (mPackages) {        final FeatureInfo feat = mAvailableFeatures.get(name);        if (feat == null) {            return false;        } else {            return feat.version >= version;        }    }}

這裡的邏輯都是通過mAvailableFeatures得到所有的feature,尋找該成員變數的相關代碼

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

瞭解到,首先擷取一個SystemConfig的單例,然後通過getAvailableFeatures方法擷取可用的feature。

SystemConfig.java

// These are the features this devices supports that were read from the// system configuration files.final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();public ArrayMap<String, FeatureInfo> getAvailableFeatures() {    return mAvailableFeatures;}private void addFeature(String name, int version) {    FeatureInfo fi = mAvailableFeatures.get(name);    if (fi == null) {        fi = new FeatureInfo();        fi.name = name;        fi.version = version;        mAvailableFeatures.put(name, fi);    } else {        fi.version = Math.max(fi.version, version);    }}private void removeFeature(String name) {    if (mAvailableFeatures.remove(name) != null) {        Slog.d(TAG, "Removed unavailable feature " + name);    }}

根據mAvailableFeatures的注釋,裝置支援的feature是從設定檔裡讀取出來的。調用讀取設定檔的地方是:

SystemConfig() {    // Read configuration from system    readPermissions(Environment.buildPath(        Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);    // Read configuration from the old permissions dir    readPermissions(Environment.buildPath(        Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);    // Allow ODM to customize system configs around libs, features and apps    int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;    readPermissions(Environment.buildPath(        Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);    readPermissions(Environment.buildPath(        Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);    // Only allow OEM to customize features    readPermissions(Environment.buildPath(        Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);    readPermissions(Environment.buildPath(        Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);}

到此就很明白了,它是讀取了幾個目錄:

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

    <!-- basic system services -->    <feature name="android.software.app_widgets" />    <feature name="android.software.connectionservice" />    <feature name="android.software.voice_recognizers" notLowRam="true" />    <feature name="android.software.backup" />    <feature name="android.software.home_screen" />    <feature name="android.software.input_methods" />    <feature name="android.software.print" />   <------這個就是列印特性

將其注釋掉就可以在手機進行測試了。

但是,我們還需要修改源碼,保證以後編譯系統這個值都是被屏蔽的。

尋找MakeFile,找到如下:

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

這個檔案在源碼中的位置是frameworks/native/data/etc/。找到該源碼檔案,將不要的Feature注釋掉,然後重新編譯源碼,啟動系統,一切正常!印表機相關的服務徹底被屏蔽掉了,系統啟動速度,資源消耗又變小了一點點。嗯,是很小的一點點,我們還可以把VR,紅外線等等很多服務裁剪掉,以適應不同應用情境下的精簡系統。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: