הסרת חבילות של משתמש המערכת

בדף הזה נסביר איך לשפר את הביצועים על ידי זיהוי והסרה של חבילות שלא נחוצות למשתמש המערכת.

השבתת חבילות מיותרות

ב-Automotive, משתמש המערכת הוא headless, כלומר משתמש המערכת לא מיועד לשימוש על ידי בני אדם או לגישה ישירה שלהם. כתוצאה מכך, אין צורך להריץ אפליקציות ושירותים רבים בתור משתמש המערכת, ואפשר להשבית אותם כדי לשפר את הביצועים. לכן, יש אפשרות להסיר אפליקציות מיותרות ממשתמש המערכת (משתמש 0).

בדף הזה נסביר על שני סוגים של משתמשים:

  • SYSTEM. תמיד משתמש 0
  • FULL. משתמש שמיועד לשימוש על ידי אדם (לא משתמש מערכת), משתמש 10 ואילך

Android 11

ב-Android 11, משנים את ההגדרה של config_userTypePackageWhitelistMode. אפשר לשלב בין דגלים. במקרה כזה, הערך של 5 שווה ל-1 ‎+ 4 (שילוב של הדגלים 1 ו-4).

סימון תיאור
0 השבתת רשימת ההיתרים. התקנה של כל חבילות המערכת, ללא רישום ביומן.
1 אכיפה. התקנה של חבילות מערכת רק אם הן נמצאות ברשימת ההיתרים.
2 רישום ביומן של חבילות שלא נכללות ברשימת ההיתרים.
4 כל חבילה שלא מוזכרת בקובץ רשימת ההיתרים נכללת ברשימת ההיתרים באופן משתמע לכל המשתמשים.
8 זהה ל-4, עבור משתמש המערכת.
16 התעלמות מעדכוני OTA. אין להתקין חבילות מערכת במהלך עדכוני OTA.

כדאי להביא בחשבון את התרחישים הנפוצים הבאים:

  • כדי להפעיל תכונה ברשימת היתרים מלאה, 1 (אכיפה מלאה)
  • כדי להפעיל תכונה ברשימת היתרים חלקית, 5
  • כדי להפעיל תכונה למשתמש SYSTEM כדי להקל על הפיתוח המקומי, 9 (רשימת היתרים משתמעת)
  • כדי להשבית תכונה כאילו היא מעולם לא הופעלה, 16
  • כדי להשבית תכונה ולבטל את כל האפקטים הקודמים, 0

מתקינים את קובץ ה-XML בספרייה sysconfig של המכשיר (זוהי אותה ספרייה שמכילה את קובץ ה-makefile‏ (.mk) שמשמש ליצירת קובץ האימג' של המערכת למכשיר). כשנותנים שם לקובץ ה-XML, צריך לכלול את המיקום שבו החבילה מוגדרת ב-build, לדוגמה, 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. מעבירים את קובץ התצורה config_systemUserPackagesBlacklistSupported מ-frameworks/base/core/res/res/values/config.xml ומגדירים אותו כ-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. בדיקה אם האפליקציה (או רכיבי האפליקציה) מוגדרים כ-singleton

אם האפליקציה היא מונוגומיה, המערכת יוצרת את האובייקט של האפליקציה רק במשתמש המערכת. סביר להניח שהאפליקציה נועדה לשימוש של כמה משתמשים. למידע נוסף על אפליקציות שמתאימות לכמה משתמשים, אפשר לעיין במאמר פיתוח אפליקציות שמתאימות לכמה משתמשים.

  1. בודקים את android:singleUser="true" במניפסט של Android.
  2. אם true, רשימת ההיתרים. נדרש למשתמש המערכת.
  3. אם false, ממשיכים. לפני שמסירים, כדאי לבדוק קריטריונים אחרים.

2. בודקים אם האפליקציה דורשת גישה לאחסון המוגן

שירותים רבים של אתחול מערכת מסתמכים לרוב על אחסון מוצפן במכשיר (DE) במקום על אחסון מוצפן של פרטי כניסה (CE). בנוסף, אפליקציות מערכת שמופעלות ישירות בזמן האתחול מסתמכות גם הן על אחסון מוצפן במכשיר. מידע נוסף על אפליקציות שתומכות בהפעלה ישירה זמין במאמר תמיכה בהפעלה ישירה באפליקציות מערכת.

  1. בודקים את המניפסט של Android כדי למצוא את android:defaultToDeviceProtectedStorage="true", שנחוץ למספר רב של שירותי אתחול המערכת.
  2. אם true, רשימת ההיתרים.
  3. אם false, ממשיכים.

רמה 2, רכיבי אפליקציה

פעילויות

מידע נוסף על פעילויות זמין במאמר מבוא לפעילויות.

א. בודקים אם האפליקציה מכילה רק פעילויות

הפעילויות מוכוונות לממשק המשתמש. מכיוון שמשתמש המערכת הוא ללא גוף (headless) ב-Automotive, אסור שאדם יתקשר עם משתמש המערכת. לכן, אם האפליקציה מכילה רק פעילויות, סביר להניח שהיא לא רלוונטית למשתמש המערכת.

בודקים אם יש לכם עדיפות והרשאות מיוחדות:

  1. אם כן, יכול להיות שיהיה צורך במשתמש המערכת.
  2. אם לא, לא מוסיפים את משתמש המערכת לרשימת ההיתרים.

לדוגמה, חבילה לבדיקות תאימות (CTS) (com.android.cts.priv.ctsshim) מכילה רק פעילויות, והפעילויות מוגדרות לבדיקה של מסנני כוונת החיפוש. עם זאת, מכיוון של-CTS יש הרשאות גבוהות, צריך להתקין אותו עבור משתמש המערכת למטרות בדיקה.

שירות

מידע נוסף על שירותים זמין במאמר סקירה כללית על שירותים.

ב. בודקים אם השירות מוגדר כפרטי ואין גישה אליו מאפליקציות אחרות

אם השירות מוגדר כפרטי, חבילות אחרות לא ישתמשו בו. מחפשים את android:exported="false". אם השירות מוגדר כפרטי או שאין גישה אליו מאפליקציות אחרות, אי אפשר לקשר אותו לאפליקציות אחרות. לכן, שלב ג ושלב ד שבהמשך לא רלוונטיים. כתוצאה מכך, הרכיב הזה לא יספק עוד רמזים לגבי הצורך של משתמש המערכת בשירות.

  • אם התשובה היא כן, בודקים את הרכיב הבא.
  • אם התשובה היא לא, ממשיכים לבדוק את הרכיב הזה.

ג. בדיקה אם אפליקציות שמותקנות במשתמש המערכת עשויות לקשר לשירות הזה

בודקים אם יש חבילות ברשימת ההיתרים ברמה 1 ומזהים את השירותים שהן מקושרות אליהם. מעקב אחרי מסנן הכוונה בשירות הזה וב-startService בחבילות אחרות.

אם השירות הזה קשור לאפליקציות שמותקנות במשתמש המערכת (לדוגמה, com.android.car.companiondevicesupport נמצא ברשימת ההיתרים לצורך הפעלה במשתמש המערכת), צריך להוסיף את השירות לרשימת ההיתרים:

  • אם כן, מוסיפים לרשימת ההיתרים.
  • אם התשובה היא לא, ממשיכים לבדוק את הרכיב הזה.

ד. בדיקה אם השירות מקושר מאפליקציות אחרות ומוצהר כפעיל בחזית

מחפשים את startForeground. כלומר, אנשים ייצרו אינטראקציה עם האפליקציה בחזית. סביר להניח שהשירות הזה לא יהיה נדרש למשתמש המערכת, ולכן אין צורך להוסיף אותו לרשימת ההיתרים:

  • אם כן, לא מוסיפים את האתר לרשימת ההיתרים.
  • אם התשובה היא לא, ממשיכים לבדוק את הרכיב הבא.

ה. בדיקה אם השירות מוגדר לפעול בתהליך מערכת

בקובץ AndroidManifest, מחפשים את android:process="system". אם השירות מוגדר בכוונה לפעול בתהליך המערכת, הוא פועל באותו תהליך כמו שירות המערכת, וצריך להוסיף אותו לרשימת ההיתרים כדי שיפעל בתור משתמש המערכת. כחלק מתכנון הקצאת הזיכרון של Android, שירותי המערכת הם חלק מהתהליכים האחרונים שמופסקים, מה שמצביע על מידת הקריטיות של שירותים שמוגדרים עם מאפיין כזה. למידע נוסף על תכנון הקצאת הזיכרון ב-Android, ראו הסרת משימות עם זיכרון נמוך.

  • אם כן, לא מוסיפים את האתר לרשימת ההיתרים.
  • אם התשובה היא לא, ממשיכים לבדוק רכיבים אחרים.

לדוגמה, חבילת com.android.networkstack.inprocess צריכה להיכלל ברשימת ההיתרים כי היא מכילה את RegularMaintenanceJobService, שכוללת את התג android:process="system".

ספק תוכן

מידע נוסף על ספקי תוכן זמין במאמר ספקי תוכן.

ו. בודקים אם האפליקציה שמותקנת במשתמש המערכת תלויה בספק הזה

בודקים אם יש חבילות ברשימת ההיתרים ברמה 1, ובודקים על אילו ספקים הן תלויות. אם אפליקציה פועלת במשתמש המערכת (לדוגמה, com.android.car.companiondevicesupport נמצאת ברשימת ההיתרים לפעול במשתמש המערכת) והיא תלויה בספק התוכן הזה, צריך לוודא שגם ספק התוכן הזה נמצא ברשימת ההיתרים.

  1. אם כן, מוסיפים לרשימת ההיתרים.
  2. אם לא, לא מוסיפים לרשימת ההיתרים.

לדוגמה, אם com.android.car.EXAMPLE מכיל ספקי יחיד (SystemActionsContentProvider ו-ManagedProvisioningActionsContentProvider), צריך להוסיף אותו לרשימת ההיתרים של משתמש המערכת. לאחר מכן, אם com.android.car.EXAMPLE תלויה ב-android.webkit עבור WebViewFactoryProvider, צריך להוסיף את 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>