移除系統使用者的套件

本頁面說明如何找出系統使用者不需要的套件,並將其移除,以改善效能。

停用不必要的套件

在 Automotive 中,系統使用者是無頭,也就是說系統使用者不應由人類使用或直接存取。因此,許多應用程式和服務不需要在系統使用者中執行,而且可以停用來改善效能。因此,我們提供一個選項,可為系統使用者 (使用者 0) 移除不必要的應用程式。

本頁將討論兩種使用者:

  • SYSTEM:一律使用 User 0
  • FULL。使用者 (非系統使用者) 是指人類使用者,年齡須滿 10 歲

Android 11

在 Android 11 中,變更 config_userTypePackageWhitelistMode 設定。標記可以合併。在本例中,5 等同於 1 加上 4 (標記 14 的組合)。

檢舉 說明
0 停用許可清單。安裝所有系統套件,但不記錄。
1 強制執行。僅在系統套件列入許可清單時安裝。
2 記錄未加入許可清單的套件。
4 凡是許可清單檔案未提及的套件,都會對所有使用者隱含許可。
8 4 相同,適用於系統使用者。
16 忽略網路旅行社。請勿在 OTA 期間安裝系統套件。

請參考以下常見情境:

  • 如要為完整的許可清單啟用功能,請使用 1 (完全強制執行)
  • 如要為不完整的許可清單啟用功能,5
  • 如要為 SYSTEM 使用者啟用功能,以便進行本機開發,請使用 9 (隱含許可清單)
  • 如要停用某項功能,就好像從未啟用這項功能一樣,16
  • 如要停用功能並復原先前的所有效果,請按下 0

在裝置的 sysconfig 目錄中安裝 XML 檔案 (這個目錄包含用於建構裝置系統映像檔的 makefile (.mk))。為 XML 檔案命名時,請加入在版本中定義套件的所在位置,例如 preinstalled-packages-product-car-CAR_PRODUCT_NAME.xml

<!- this package will be installed for both FULL and SYSTEM user -->
    <install-in-user-type package="com.android.bluetooth"->
        <install-in user-type="FULL" /->
        <install-in user-type="SYSTEM" /->
    </install-in-user-type->

<!- this package will only be installed for both FULL user -->
    <install-in-user-type package="com.android.car.calendar"->
        <install-in user-type="FULL" >
    </install-in-user-type->

Android 9 和 Android 10

如要在 Android 9 和 Android 10 中設定這項功能,請按照下列步驟操作:

  1. 重疊 frameworks/base/core/res/res/values/config.xml 中的 config_systemUserPackagesBlacklistSupported 設定,並將其設為 true。開啟這項功能後,系統使用者和 FULL 使用者都應預設安裝所有套件。
  2. 建立 config.xml 檔案,列出應為系統使用者停用的套件,例如:
    <config>
        <!-- This package will be uninstalled for the system user -->
        <system-user-blacklisted-app package="com.google.car.calendar" />
    </config>
  3. device.mk 中新增一行指令,將檔案複製到裝置的目標資料夾 system/etc/sysconfig/,例如:
    PRODUCT_COPY_FILES += <full path to the config file>:system/etc/sysconfig/<new denylist config file>.xml

驗證結果

如要驗證結果,請執行:

$ adb shell dumpsys user | grep PACKAGE_SUBSTRING
$ adb shell pm list packages --user USER_ID PACKAGE_SUBSTRING
$ adb shell cmd user report-system-user-package-whitelist-problems

樓群

如要判斷是否應在系統使用者中安裝套件,請檢查位於專案來源根目錄中的套件 AndroidManifest.xml 檔案,包括應用程式屬性和應用程式元件 (包括所有活動、服務、廣播接收器和內容供應器)。詳情請參閱「應用程式資訊清單總覽」。

停用套件工作流程

圖 1. 停用套件工作流程。

等級 1,應用程式層級

1. 檢查應用程式 (或應用程式元件) 是否宣告為單例

如果應用程式是單例,系統只會在系統使用者中例項化應用程式。應用程式很可能會是多用戶感知應用程式。如要進一步瞭解多用戶感知應用程式,請參閱「建構多用戶感知應用程式」。

  1. 檢查 Android 資訊清單中的 android:singleUser="true"
  2. 如果是 true,則為許可清單。系統使用者需要的資訊。
  3. 如果是 false,請繼續操作。請先檢查其他條件再移除。

2. 檢查應用程式是否需要受保護的儲存空間存取權

許多系統啟動服務通常會使用裝置加密 (DE) 儲存空間,而非憑證加密 (CE) 儲存空間。此外,直接啟動感知的系統應用程式也需要使用裝置加密儲存空間。如要進一步瞭解直接啟動感知應用程式,請參閱「在系統應用程式中支援直接啟動」。

  1. 檢查 Android 資訊清單中的 android:defaultToDeviceProtectedStorage="true",許多系統啟動服務都需要這個項目。
  2. 如果是 true,則加入許可清單。
  3. 如果是 false,請繼續。

等級 2,應用程式元件

活動

如要進一步瞭解活動,請參閱「活動簡介」。

a. 檢查應用程式是否只包含活動

活動是以使用者介面為導向。由於 Automotive 中的系統使用者是無頭的,因此不應有任何人與系統使用者互動。因此,如果應用程式只包含活動,就很可能與系統使用者無關。

檢查優先順序和特殊權限:

  1. 如果為「是」,系統使用者可能需要使用者名稱。
  2. 如果為「否」,則不要將系統使用者加入許可清單。

舉例來說,相容性測試套件 (CTS) (com.android.cts.priv.ctsshim) 只包含活動,而活動則是用於測試意圖篩選器。不過,由於 CTS 具有較高的權限,因此需要為系統使用者安裝,以便進行測試。

服務

如要進一步瞭解服務,請參閱「服務總覽」。

b. 檢查服務是否已宣告為私人,且無法從其他應用程式存取

如果服務宣告為private,其他套件就不會使用該服務。尋找 android:exported="false"。如果服務宣告為私人,或無法從其他應用程式存取,則無法由其他應用程式繫結。因此,下方的步驟 c 和步驟 d 與此無關。因此,這個元件不會提供更多提示,說明系統使用者是否需要這項服務。

  • 如果是「是」,請檢查下一個元件。
  • 如果是「否」,請繼續檢查這個元件。

c. 檢查系統使用者安裝的應用程式是否可能繫結至這項服務

檢查第 1 級的許可清單套件,並找出這些套件綁定的服務。從此服務中的意圖篩選器和其他套件中的 startService 進行追蹤。

如果這項服務已繫結至系統使用者安裝的應用程式 (例如,com.android.car.companiondevicesupport 已列入許可清單,可在系統使用者中執行),請將服務列入許可清單:

  • 如果答案為「是」,請加入許可清單。
  • 如果是「否」,請繼續檢查這個元件。

d. 檢查服務是否已從其他應用程式繫結,並宣告要在前景執行

尋找 startForeground。也就是說,使用者會在前景與應用程式互動。系統使用者很可能不需要這項服務,因此不需要將其列入許可清單:

  • 如果是「是」,請不要加入許可清單。
  • 如果是「否」,請繼續檢查下一個元件。

e. 檢查服務是否已定義為在系統程序中執行

在 AndroidManifest 檔案中尋找 android:process="system"。如果服務是刻意定義為在系統程序中執行,則會與系統服務在相同程序中執行,並應列入許可清單,以便在系統使用者中執行。在 Android 的記憶體分配設計中,系統服務是最後終止的程序之一,這表示使用此屬性定義的服務具有重要性。如要進一步瞭解 Android 的記憶體配置設計,請參閱「低記憶體殺手」。

  • 如果是「是」,請不要加入許可清單。
  • 如果是「否」,請繼續檢查其他元件。

舉例來說,套件 com.android.networkstack.inprocess 必須列入許可清單,因為它包含 RegularMaintenanceJobService,而 RegularMaintenanceJobService 具有 android:process="system" 標記。

內容供應者

如要進一步瞭解內容供應器,請參閱「內容供應器」。

f. 檢查系統使用者安裝的應用程式是否依賴這個供應器

檢查第 1 級中是否有許可清單中的套件,並查看這些套件依賴哪些供應商。如果應用程式在系統使用者中執行 (例如 com.android.car.companiondevicesupport 已列入允許清單,可在系統使用者中執行),且依賴這個內容供應器,請務必將這個內容供應器列入允許清單。

  1. 如果答案為「是」,請加入許可清單。
  2. 如果是「否」,則不要將其加入許可清單。

舉例來說,如果 com.android.car.EXAMPLE 包含單例供應器 (SystemActionsContentProviderManagedProvisioningActionsContentProvider),則應將其列入系統使用者的許可清單。接著,如果 com.android.car.EXAMPLE 依賴 android.webkitWebViewFactoryProvider,則 com.android.webview 必須列入系統使用者的許可清單,因為它會載入 android.webkit

範例套件逐步操作說明

以下範例說明如何評估套件的 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 1. Search in the entire manifest for singleUser attribute.
No. Move to step 2 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.providers.calendar"
        android:sharedUserId="android.uid.calendar">
    We can ignore the entire permission section
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    ...
    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<!-- 2. Look for defaultToDeviceProtectedStorage in application's attribute.
No. Continue evaluating app components. -->
    <application android:label="@string/calendar_storage"
                 android:allowBackup="false"
                 android:icon="@drawable/app_icon"
                 android:usesCleartextTraffic="false">
<!-- a. Contain only activities?
No. Continue to evaluate components other than activities. -->
        <provider android:name="CalendarProvider2" android:authorities="com.android.calendar"
                <!-- b. Is this component exported?
                Yes. Continue evaluating this component.
                f. App on u0 might depend on this? Search for CalendarProvider2 in dumpsys, shows ContentProviderRecord{b710923 u0 com.android.providers.calendar/.CalendarProvider2}
                Yes. Whitelist for system user. -->
                android:label="@string/provider_label"
                android:multiprocess="false"
                android:exported="true"
                android:readPermission="android.permission.READ_CALENDAR"
                android:writePermission="android.permission.WRITE_CALENDAR" />

<activity android:name="CalendarContentProviderTests" android:label="Calendar Content Provider" android:exported="false"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.UNIT_TEST" /> </intent-filter> </activity> <!-- Not service/content provider. Ignore. --> <receiver android:name="CalendarProviderBroadcastReceiver" android:exported="false"> <intent-filter> <action android:name="com.android.providers.calendar.intent.CalendarProvider2"/> <category android:name="com.android.providers.calendar"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.EVENT_REMINDER"/> <data android:scheme="content" /> </intent-filter> </receiver> <service android:name="CalendarProviderIntentService"/> </application> </manifest>