移除系統使用者的套件

本頁面說明如何識別並移除系統使用者不需要的套件,藉此改善效能。

停用不必要的套件

在 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

將 XML 檔案安裝在裝置的 sysconfig 目錄中 (這個目錄包含用於為裝置建構系統映像檔的 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:singleUser="true" 的 Android 資訊清單。
  2. 如果是 true,請加入許可清單。系統使用者需要的資訊。
  3. 如果是 false,請繼續操作。請先檢查其他條件再移除。

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

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

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

第 2 級,應用程式元件

活動

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

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

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

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

  1. 如果值為「是」,則系統使用者可能需要使用這些資訊。
  2. 如果為「否」,則不要為系統使用者建立許可清單。

舉例來說,Compatibility Test Suite (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 依附於 WebViewFactoryProviderandroid.webkit,由於 com.android.webview 會載入 android.webkit,您必須將 com.android.webview 加入以便系統使用者許可清單。

範例套件逐步操作說明

以下範例說明如何評估套件的 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>