システム ユーザーのパッケージの削除

このページでは、SYSTEM ユーザーにとって不要なパッケージを特定して削除することで、パフォーマンスを改善する方法について説明します。

不要なパッケージを無効にする

Automotive では、システム ユーザーはヘッドレスです。つまり、システム ユーザーは、人間が使用したり直接アクセスすることを意図したものではありません。そのため、多くのアプリやサービスはシステム ユーザーで実行する必要はなく、無効にすることでパフォーマンスを向上させることができます。そのため、システム ユーザー(ユーザー 0)にとって不要なアプリを削除するオプションが用意されています。

このページでは、次の 2 種類のユーザーについて説明します。

  • システム。常にユーザー 0
  • FULL。人間による使用が想定されているユーザー(システム ユーザー以外のユーザー)、ユーザー 10 以上

Android 11

Android 11 では、構成 config_userTypePackageWhitelistMode を変更します。フラグは組み合わせることができます。この場合、514(フラグ 14 の組み合わせ)に相当します。

フラグ 説明
0 許可リストを無効にします。すべてのシステム パッケージをインストールします(記録はされません)。
1 適用します。許可リストに登録されているシステム パッケージのみインストールします。
2 許可リストに登録されていないパッケージを記録します。
4 許可リストファイルに記載されていないパッケージを、すべてのユーザーを対象とした許可リストに暗黙的に登録します。
8 4 と同じです(システム ユーザーが対象)。
16 OTA を無視します。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. そのアプリに保護されたストレージ アクセスが必要かどうかを確認する

システム ブート サービスは、多くの場合、認証情報暗号化(CE)ストレージの代わりにデバイス暗号化(DE)ストレージを使用します。また、ダイレクト ブートに対応したシステムアプリも、デバイス暗号化ストレージを使用します。ダイレクト ブート対応アプリについて詳しくは、システムアプリでのダイレクト ブートのサポートをご覧ください。

  1. Android マニフェストで、多数のシステム ブート サービスで必要となる android:defaultToDeviceProtectedStorage="true" を確認します。
  2. true の場合は、許可リストに登録します。
  3. false の場合は、続行します。

レベル 2、アプリ コンポーネント

アクティビティ

アクティビティの詳細については、アクティビティの概要をご覧ください。

a. そのアプリに含まれているのがアクティビティのみであるかどうかを確認する

アクティビティはユーザー インターフェース指向です。Automotive では、システム ユーザーはヘッドレスであるため、人間はシステム ユーザーを操作する必要がありません。そのため、アプリにアクティビティのみが含まれている場合、そのアプリはシステム ユーザーとの関連しない可能性が高いと考えられます。

優先度と特別な権限を確認します。

  1. アクティビティのみである場合、システム ユーザーで必要になる可能性があります。
  2. アクティビティのみでない場合、システム ユーザーの許可リストに含めません。

たとえば、互換性テストスイート(CTS)(com.android.cts.priv.ctsshim)にはアクティビティのみが含まれ、アクティビティはインテント フィルタをテストするように定義されています。ただし、CTS は権限が高いため、テスト目的でシステム ユーザー用にインストールする必要があります。

サービス

サービスの詳細については、サービスの概要をご覧ください。

b. サービスが非公開であると宣言され、他のアプリからアクセスできないかどうかを確認する

サービスを非公開として宣言されている場合、他のパッケージはそのサービスを使用できません。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 には、android:process="system" タグが付与された RegularMaintenanceJobService が含まれるため、許可リストに含める必要があります。

コンテンツ プロバイダ

コンテンツ プロバイダの詳細については、コンテンツ プロバイダをご覧ください。

f. システム ユーザーにインストールされたアプリがこのプロバイダに依存しているかどうかを確認する

レベル 1 で許可リストに含まれたパッケージを確認して、依存するプロバイダを確認します。システム ユーザーで実行されているアプリ(例: com.android.car.companiondevicesupport がシステム ユーザーで実行されるように許可リストに含まれている)が、このコンテンツ プロバイダに依存している場合は、このコンテンツ プロバイダも許可リストに含めてください。

  1. 依存している場合は、許可リストに登録します。
  2. 依存していない場合は、許可リストに登録しません。

たとえば、com.android.car.EXAMPLE にシングルトン プロバイダ(SystemActionsContentProviderManagedProvisioningActionsContentProvider)が含まれている場合、システム ユーザーの許可リストに含める必要があります。次に、com.android.car.EXAMPLEWebViewFactoryProviderandroid.webkit に依存している場合は、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>