אינטראקציה עם מרכז הבטיחות

הפניה אוטומטית למרכז הבטיחות

כל אפליקציה יכולה לפתוח את מרכז הבטיחות באמצעות פעולה אחת (android.content.Intent.ACTION_SAFETY_CENTER) (ערך מחרוזת android.intent.action.SAFETY_CENTER).

כדי לפתוח את מרכז הבטיחות, צריך לבצע שיחה מתוך מופע של Activity:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

הפניה אוטומטית לבעיה ספציפית

אפשר גם להפנות לכרטיס אזהרה ספציפי של מרכז הבטיחות באמצעות כוונות רכישה ספציפיות. התוספות האלה לא נועדו לשימוש על ידי צדדים שלישיים. הם חלק מ-SafetyCenterManager, שהוא חלק מ-@SystemApi. רק לאפליקציות מערכת יש גישה לתוספות האלה.

תוספות Intent שמפנות כרטיס אזהרה ספציפי:

  • EXTRA_SAFETY_SOURCE_ID
    • ערך המחרוזת: android.safetycenter.extra.SAFETY_SOURCE_ID
    • סוג המחרוזת: מציין את מזהה מקור הבטיחות של כרטיס אזהרה
    • חובה כדי שההפניה האוטומטית לבעיה תעבוד
  • EXTRA_SAFETY_SOURCE_ISSUE_ID
    • ערך המחרוזת: android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID
    • סוג המחרוזת: מציין את מזהה כרטיס האזהרה
    • חובה כדי שההפניה האוטומטית לבעיה תעבוד
  • EXTRA_SAFETY_SOURCE_USER_HANDLE
    • ערך המחרוזת: android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE
    • סוג UserHandle: מציין UserHandle לאזהרה המשויכת קלפים
    • אופציונלי (ברירת המחדל היא המשתמש הנוכחי)

אפשר להשתמש בקטע הקוד שבהמשך מתוך מופע של Activity כדי לפתוח במסך של מרכז הבטיחות לטיפול בבעיה ספציפית:

UserHandle theUserHandleThisIssueCameFrom = …;

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER)
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID, "TheSafetySourceIdThisIssueCameFrom")
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID, "TheSafetySourceIssueIdToRedirectTo")
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_USER_HANDLE, theUserHandleThisIssueCameFrom);

startActivity(openSafetyCenterIntent);

הפניה אוטומטית לדף משנה ספציפי (החל מ-Android 14)

ב-Android 14 ואילך, הדף של מרכז הבטיחות פוצל לכמה דפי משנה שמייצגים את SafetySourcesGroup השונה (ב ב-Android 13, הערך הזה מוצג כרשומות ניתנות לכיווץ).

אפשר להפנות לדף משנה ספציפי באמצעות תוספת Intent נוספת:

  • EXTRA_SAFETY_SOURCES_GROUP_ID
    • ערך המחרוזת: android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID
    • סוג המחרוזת: מציין את המזהה של SafetySourcesGroup
    • חובה כדי שההפניה אוטומטית לדף המשנה תפעל

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

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER)
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID, "TheSafetySourcesGroupId");

startActivity(openSafetyCenterIntent);

שימוש בממשקי ה-API של המקור במרכז הבטיחות

ממשקי ה-API המקוריים של מרכז הבטיחות זמינים באמצעות SafetyCenterManager (כלומר, @SystemApi). הקוד של פלטפורמת ה-API זמין קוד חיפוש. קוד ההטמעה של ממשקי ה-API זמין בקטע קוד חיפוש Google.

הרשאות

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

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • משמש ל-API של SafetyCenterManager#isSafetyCenterEnabled() (לא שנדרשים למקורות במרכז הבטיחות, הם צריכים רק הרשאה SEND_SAFETY_CENTER_UPDATE)
    • בשימוש על ידי אפליקציות מערכת שבודקות אם מרכז הבטיחות מופעל
    • מוענק רק לאפליקציות מערכת שנמצאות ברשימת ההיתרים
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • משמש עבור ה-API המופעל וה-Security Sources API
    • בשימוש במקורות בטיחות בלבד
    • מוענק רק לאפליקציות מערכת שנמצאות ברשימת ההיתרים

ההרשאות האלה מוגבלות ואפשר לקבל אותן רק על ידי הוספתן אל את הקובץ הרלוונטי, כמו com.android.settings.xml לקובץ לאפליקציה 'הגדרות' ולקובץ AndroidManifest.xml של האפליקציה. צפייה protectionLevel לקבלת מידע נוסף על מודל ההרשאות.

הורדה של SafetyCenterManager

SafetyCenterManager היא מחלקה מסוג @SystemApi שאפשר לגשת אליה מאפליקציות מערכת החל מ-Android 13. השיחה הזו היא דוגמה get SafetyCenterManager:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
  // Must be on T or above to interact with Safety Center.
  return;
}
SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
if (safetyCenterManager == null) {
  // Should not be null on T.
  return;
}

איך בודקים אם מרכז הבטיחות מופעל

השיחה הזו בודקת אם מרכז הבטיחות מופעל. השיחה מחייבת READ_SAFETY_CENTER_STATUS או ההרשאה SEND_SAFETY_CENTER_UPDATE:

boolean isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled();
if (isSafetyCenterEnabled) {
  // …
} else {
  // …
}

שליחת נתונים

נתוני המקור של מרכז הבטיחות עם המאפיין String sourceId הנתון סופקו ל'בטיחות' מרכז עם האובייקט SafetySourceData, שמייצג רשומת ממשק משתמש רשימה של בעיות (כרטיסי אזהרה). הרשומה בממשק המשתמש וכרטיסי האזהרה יכולים לכלול רמות החומרה השונות שצוינו במחלקה SafetySourceData:

  • SEVERITY_LEVEL_UNSPECIFIED
    • לא צוינה מידת חוּמרה
    • צבע: אפור או שקוף (בהתאם ל-SafetySourcesGroup של )
    • משמש לנתונים דינמיים שמופיעים כרשומה סטטית בממשק המשתמש, או להצגה רשומה שלא צוינה
    • אסור להשתמש בכרטיסי אזהרה
  • SEVERITY_LEVEL_INFORMATION
    • מידע בסיסי או הצעה משנית
    • צבע: ירוק
  • SEVERITY_LEVEL_RECOMMENDATION
    • המלצה שהמשתמש צריך לנקוט פעולה לגבי הבעיה הזו, כי היא הם עלולים לסכן אותם
    • צבע: צהוב
  • SEVERITY_LEVEL_CRITICAL_WARNING
    • אזהרה קריטית לכך שהמשתמש חייב לנקוט פעולה בנוגע לבעיה הזו, מהווה סיכון
    • צבע: אדום

SafetySourceData

האובייקט SafetySourceData מורכב מרשומה בממשק המשתמש, מכרטיסי אזהרה משתנים קבועים.

  • מופע אופציונלי של SafetySourceStatus (ערך בממשק משתמש)
  • רשימת SafetySourceIssue מופעים (כרטיסי אזהרה)
  • Bundle תוספות אופציונליות (החל מ-14)
  • משתנים:
    • הרשימה SafetySourceIssue חייבת להיות מורכבת מבעיות עם ערכים ייחודיים מזהים.
    • למכונה של SafetySourceIssue לא יכולה להיות חשיבות גדולה מ- SafetySourceStatus אם יש כזה (אלא אם SafetySourceStatus קיים SEVERITY_LEVEL_UNSPECIFIED, במקרה כזה SEVERITY_LEVEL_INFORMATION ).
    • הדרישות הנוספות שההגדרות של ה-API מטילות צריכות לעמוד בהן, לדוגמה, אם המקור הוא בעיה בלבד, הוא לא יכול לספק מופע אחד (SafetySourceStatus).

SafetySourceStatus

  • כותרת CharSequence נדרשת
  • נדרש סיכום לגבי CharSequence
  • רמת החומרה הנדרשת
  • PendingIntent אופציונלי מכונת הפניה אוטומטית של המשתמש לדף הנכון (ברירת המחדל היא intentAction מההגדרה, אם יש כזה)
  • IconAction אופציונלי (מוצג כסמל צדדי ברשומה) שמורכב מ:
    • סוג הסמל הנדרש, שחייב להיות אחד מהסוגים הבאים:
      • ICON_TYPE_GEAR: מוצג כגלגל שיניים לצד הרשומה בממשק המשתמש
      • ICON_TYPE_INFO: מוצג כסמל מידע לצד הרשומה בממשק המשתמש
    • שדה חובה. PendingIntent כדי להפנות את המשתמשים לדף אחר
  • ערך בוליאני אופציונלי של enabled שמאפשר לסמן את הרשומה בממשק המשתמש בתור מושבת, כך שאי אפשר ללחוץ עליו (ברירת המחדל היא true)
  • משתנים:
    • מכונות PendingIntent חייבות לפתוח מכונה של Activity.
    • אם הרשומה מושבתת, יש לציין אותה SEVERITY_LEVEL_UNSPECIFIED
    • דרישות נוספות שהגדרות ה-API מציבים.

SafetySourceIssue

  • נדרש מזהה ייחודי של String
  • כותרת CharSequence נדרשת
  • כותרת משנה CharSequence אופציונלית
  • נדרש סיכום לגבי CharSequence
  • רמת החומרה הנדרשת
  • קטגוריה אופציונלית של בעיות, שחייבת להיות אחת מהאפשרויות הבאות:
    • ISSUE_CATEGORY_DEVICE: הבעיה משפיעה על המכשיר של המשתמש.
    • ISSUE_CATEGORY_ACCOUNT: הבעיה משפיעה על החשבונות של המשתמש.
    • ISSUE_CATEGORY_GENERAL: הבעיה משפיעה על הבטיחות הכללית של המשתמש. (זוהי ברירת המחדל)
    • ISSUE_CATEGORY_DATA (החל מ-Android 14): הבעיה משפיעה על הנתונים של המשתמש.
    • ISSUE_CATEGORY_PASSWORDS (החל מ-Android) 14): הבעיה משפיעה על סיסמאות.
    • ISSUE_CATEGORY_PERSONAL_SAFETY (החל מ-Android) 14): הבעיה משפיעה על סייפטי.
  • רשימה של רכיבי Action שהמשתמש יכול לטפל בבעיה הזו, כל אחד מכונה של Action מורכבת מ:
    • נדרש מזהה ייחודי של String
    • חובה להוסיף תווית CharSequence
    • שדה חובה. PendingIntent להפנות את המשתמש לדף אחר או לעבד את הפעולה ישירות המסך של מרכז הבטיחות
    • ערך בוליאני אופציונלי שמציין אם ניתן לפתור את הבעיה ישירות המסך של מרכז הבטיחות (ברירת המחדל היא false)
    • הודעה אופציונלית CharSequence שתוצג למשתמש כאשר הבעיה נפתרת בהצלחה ישירות ממרכז הבטיחות מסך
  • PendingIntent אופציונלי מתבצעת קריאה כשהמשתמש סוגר את הבעיה (ברירת המחדל היא שאף אחד לא שנקרא)
  • נדרש מזהה אחד (String) של סוג הבעיה. דומה לבעיה אבל לא חייב להיות ייחודי ומשמש לרישום
  • אופציונלי: String עבור מזהה ביטול הכפילויות, זה מאפשר לפרסם את אותו SafetySourceIssue ממקורות שונים ומוצגת רק פעם אחת ממשק משתמש בהנחה שיש להם ערך deduplicationGroup זהה (בהתחלת Android 14). אם לא צוין אחרת, הבעיה אף פעם בוטל כפילויות
  • CharSequence אופציונלי לשם הייחוס. זהו טקסט שמציג המקור של כרטיס האזהרה (החל מ-Android) 14). אם לא צוין אחרת, נשתמש בכותרת של SafetySourcesGroup
  • יכולת פעולה אופציונלית של הבעיה (החל מ-Android 14), שחייב להיות אחד מאלה:
    • ISSUE_ACTIONABILITY_MANUAL: המשתמש צריך לפתור את הבעיה במצב ה-GRU (זוהי ברירת המחדל)
    • ISSUE_ACTIONABILITY_TIP: הבעיה הזו היא טיפ בלבד וייתכן שלא נדרש לה כל קלט של משתמשים.
    • ISSUE_ACTIONABILITY_AUTOMATIC: כבר טיפלנו בבעיה הזו ולא דורשים קלט של משתמשים.
  • התנהגות אופציונלית של ההתראות (ב-Android) 14), שחייב להיות אחד מאלה:
    • NOTIFICATION_BEHAVIOR_UNSPECIFIED: מרכז הבטיחות יקבע אם נדרשת התראה עבור כרטיס האזהרה. (זוהי ברירת המחדל)
    • NOTIFICATION_BEHAVIOR_NEVER: לא פורסמה התראה.
    • NOTIFICATION_BEHAVIOR_DELAYED: התראה מתפרסמת זמן מה אחרי הדיווח הראשון על הבעיה.
    • NOTIFICATION_BEHAVIOR_IMMEDIATELY: ההתראה מתפרסמת מיד הבעיה מדווחת.
  • Notification אופציונלי, כדי להציג התראה בהתאמה אישית עם כרטיס האזהרה (החל מ-Android 14). אם לא צוין אחרת, הערך Notification נגזר מכרטיס האזהרה. מורכב מ:
    • כותרת CharSequence נדרשת
    • נדרש סיכום לגבי CharSequence
    • רשימה של רכיבי Action שהמשתמש יכול להשלים עבור ההתראה הזו
  • משתנים:
    • רשימת המופעים של Action חייבת להיות מורכבת מפעולות עם ייחודיות מזהים
    • רשימת המופעים של Action חייבת להכיל ערך אחד או שניים של Action רכיבים. אם יכולת הפעולה היא לא ISSUE_ACTIONABILITY_MANUAL, מותר לציין אפס Action.
    • ל-OnDismiss PendingIntent אסור לפתוח מופע של Activity
    • דרישות נוספות שקבעו הגדרות ה-API

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

SafetyEvent

  • סוג חובה, חייב להיות אחד מהסוגים הבאים:
    • SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: למצב המקור יש השתנה.
    • SAFETY_EVENT_TYPE_REFRESH_REQUESTED: תגובה לרענון/לסריקה מחדש אות ממרכז הבטיחות; השתמשו בטיוטה הזו במקום SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED כדי שמרכז הבטיחות יוכל לבצע פעולות לעקוב אחר בקשת הרענון/הסריקה מחדש.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: פתרנו את הבעיה SafetySourceIssue.Action ישירות מהמסך של מרכז הבטיחות; להשתמש זה במקום SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED מטעמי בטיחות מרכז כדי לעקוב אחרי הבעיה של SafetySourceIssue.Action שטופלה.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: ניסינו לפתור את הבעיה SafetySourceIssue.Action ישירות מהמסך של מרכז הבטיחות, אבל לא ניתן היה לעשות זאת; השתמשו בטיוטה הזו במקום SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED כדי שמרכז הבטיחות יוכל לפעול הטראק SafetySourceIssue.Action נכשל.
    • SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: השפה של המכשיר השתנה, לכן אנחנו מעדכנים את הטקסט של הנתונים שסופקו; זה מורשה להשתמש ב-SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED בשביל זה.
    • SAFETY_EVENT_TYPE_DEVICE_REBOOTED: אנחנו מספקים את הנתונים האלה כחלק של הפעלה ראשונית, כי הנתונים של מרכז הבטיחות לא נשמרים הפעלות מחדש; מותר להשתמש בSAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED לכך.
  • מזהה String אופציונלי למזהה השידור של הרענון.
  • מזהה String אופציונלי למכונה של SafetySourceIssue שמקבלת הבעיה נפתרה.
  • מזהה String אופציונלי למכונה SafetySourceIssue.Action להגיע לפתרון.
  • משתנים:
    • יש לספק את מזהה השידור של הרענון, אם הסוג SAFETY_EVENT_TYPE_REFRESH_REQUESTED
    • יש לציין את המזהים של הבעיות והפעולה, אם הסוג הוא SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED או SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED

בהמשך מוצגת דוגמה לאופן שבו מקור עשוי לספק נתונים למרכז הבטיחות (ב במקרה שהוא כולל רשומה עם כרטיס אזהרה אחד):

PendingIntent redirectToMyScreen =
    PendingIntent.getActivity(
        context, requestCode, redirectToMyScreenIntent, PendingIntent.FLAG_IMMUTABLE);
SafetySourceData safetySourceData =
    new SafetySourceData.Builder()
        .setStatus(
            new SafetySourceStatus.Builder(
                    "title", "summary", SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                .setPendingIntent(redirectToMyScreen)
                .build())
        .addIssue(
            new SafetySourceIssue.Builder(
                    "MyIssueId",
                    "title",
                    "summary",
                    SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
                    "MyIssueTypeId")
                .setSubtitle("subtitle")
                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
                .addAction(
                    new SafetySourceIssue.Action.Builder(
                            "MyIssueActionId", "label", redirectToMyScreen)
                        .build())
                .build())
        .build();
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
safetyCenterManager.setSafetySourceData("MySourceId", safetySourceData, safetyEvent);

קבלת הנתונים האחרונים שסופקו

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

אפשר להשתמש בקוד הזה כדי לקבל את הנתונים האחרונים שסופקו למרכז הבטיחות:

SafetySourceData lastDataProvided = safetyCenterManager.getSafetySourceData("MySourceId");

דיווח על שגיאה

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

אפשר לספק את השגיאה באמצעות SafetySourceErrorDetails, שמורכב מ- מתוך:

  • SafetySourceErrorDetails: מופע נדרש של SafetyEvent:
// An error has occurred in the background, need to clear the Safety Center data to avoid showing data that may not be valid anymore
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
SafetySourceErrorDetails safetySourceErrorDetails = new SafetySourceErrorDetails(safetyEvent);
safetyCenterManager.reportSafetySourceError("MySourceId", safetySourceErrorDetails);

איך להגיב לבקשה לרענון או לסריקה מחדש

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

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

  • ACTION_REFRESH_SAFETY_SOURCES
    • ערך המחרוזת: android.safetycenter.action.REFRESH_SAFETY_SOURCES
    • מופעל כשנשלחת בקשה ממרכז הבטיחות לרענון הנתונים של מקור הבטיחות של אפליקציה מסוימת
    • כוונה מוגנת שניתן לשלוח רק באמצעות המערכת
    • נשלח לכל מקורות הבטיחות בקובץ התצורה באמצעות Intent ונדרשת ההרשאה SEND_SAFETY_CENTER_UPDATE

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

  • EXTRA_REFRESH_SAFETY_SOURCE_IDS
    • ערך המחרוזת: android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS
    • סוג מערך המחרוזת (String[]), מייצג את מזהי המקור שעבורם צריך לרענן האפליקציה הנתונה
  • EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE

    • ערך מחרוזת: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE
    • סוג מספר שלם, מייצג את סוג הבקשה @IntDef
    • חייב להיות אחד מהבאים:
      • EXTRA_REFRESH_REQUEST_TYPE_GET_DATA: מבקש מהמקור לספק נתונים במהירות יחסית, בדרך כלל כשהמשתמש פותח את הדף
      • EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA: מבקשת את המקור לספק נתונים עדכניים ככל האפשר, בדרך כלל כשהמשתמש לוחץ על לחצן הסריקה מחדש
  • EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID

    • ערך מחרוזת: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID
    • סוג המחרוזת, מייצג מזהה ייחודי של הרענון המבוקש

כדי לקבל אות ממרכז הבטיחות, צריך להטמיע תג BroadcastReceiver מכונה. השידור נשלח עם BroadcastOptions מיוחד שמאפשר מקלט כדי להפעיל שירות שפועל בחזית.

התגובה של BroadcastReceiver לבקשה לרענון:

public final class SafetySourceReceiver extends BroadcastReceiver {
  // All the safety sources owned by this application.
  private static final String[] ALL_SAFETY_SOURCES = new String[] {"MySourceId1", "…"};
  @Override
  public void onReceive(Context context, Intent intent) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
      // Must be on T or above to interact with Safety Center.
      return;
    }
    String action = intent.getAction();
    if (!SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES.equals(action)) {
      return;
    }
    String refreshBroadcastId =
        intent.getStringExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID);
    if (refreshBroadcastId == null) {
      // Should always be provided.
      return;
    }
    String[] sourceIds =
        intent.getStringArrayExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS);
    if (sourceIds == null) {
      sourceIds = ALL_SAFETY_SOURCES;
    }
    int requestType =
        intent.getIntExtra(
            SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE,
            SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA);
    SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
    if (safetyCenterManager == null) {
      // Should not be null on T.
      return;
    }
    if (!safetyCenterManager.isSafetyCenterEnabled()) {
      // Preferably, no Safety Source code should be run if Safety Center is disabled.
      return;
    }
    SafetyEvent refreshSafetyEvent =
        new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
            .setRefreshBroadcastId(refreshBroadcastId)
            .build();
    for (String sourceId : sourceIds) {
      SafetySourceData safetySourceData = getSafetySourceDataFor(sourceId, requestType);
      // Set the data (or report an error with reportSafetySourceError, if something went wrong).
      safetyCenterManager.setSafetySourceData(sourceId, safetySourceData, refreshSafetyEvent);
    }
  }
  private SafetySourceData getSafetySourceDataFor(String sourceId, int requestType) {
    switch (requestType) {
      case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA:
        return getRefreshSafetySourceDataFor(sourceId);
      case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA:
        return getRescanSafetySourceDataFor(sourceId);
      default:
    }
    return getRefreshSafetySourceDataFor(sourceId);
  }
  // Data to provide when the user opens the page or on specific events.
  private SafetySourceData getRefreshSafetySourceDataFor(String sourceId) {
    // Get data for the source, if it's a fast operation it could potentially be executed in the
    // receiver directly.
    // Otherwise, it must start some kind of foreground service or expedited job.
    return null;
  }
  // Data to provide when the user pressed the rescan button.
  private SafetySourceData getRescanSafetySourceDataFor(String sourceId) {
    // Could be implemented the same way as getRefreshSafetySourceDataFor, depending on the source's
    // need.
    // Otherwise, could potentially perform a longer task.
    // In which case, it must start some kind of foreground service or expedited job.
    return null;
  }
}

אותו מופע של BroadcastReceiver בדוגמה שלמעלה מוצהר ב- AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="…">
    <application>
    <!-- … -->
        <receiver android:name=".SafetySourceReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
            </intent-filter>
        </receiver>
    <!-- … -->
    </application>
</manifest>

באופן אידיאלי, מטמיעים מקור של מרכז הבטיחות באופן SafetyCenterManager כשהנתונים משתנים. מטעמי תקינות המערכת, מומלץ להגיב רק לאות הסריקה מחדש (כשהמשתמש מקיש על הסריקה ולא כשהמשתמש פותח את מרכז הבטיחות. אם הפונקציונליות הזו חובה, השדה refreshOnPageOpenAllowed="true" בקובץ התצורה חייבים להגדיר כדי שהמקור יקבל את השידור שמועבר במקרים האלה.

שליחת תגובה למרכז הבטיחות כשהיא מופעלת או מושבתת

אפשר להגיב כאשר מרכז הבטיחות מופעל או מושבת באמצעות פעולת Intent:

  • ACTION_SAFETY_CENTER_ENABLED_CHANGED
    • ערך מחרוזת: android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED
    • מופעל כשמרכז הבטיחות מופעל או מושבת בזמן ש המכשיר פועל
    • לא בוצעה קריאה בזמן האתחול (יש להשתמש ACTION_BOOT_COMPLETED לביצוע הפעולה הזו)
    • כוונה מוגנת שניתן לשלוח רק באמצעות המערכת
    • נשלח לכל מקורות הבטיחות בקובץ התצורה באמצעות Intent, נדרשת ההרשאה SEND_SAFETY_CENTER_UPDATE
    • נשלחה כאובייקט Intent מרומז שמחייב את המאפיין READ_SAFETY_CENTER_STATUS הרשאה

פעולת Intent הזו מועילה כדי להפעיל או להשבית תכונות שקשורות אל מרכז הבטיחות במכשיר.

יישום פעולות לפתרון בעיות

פעולה לפתרון היא מופע SafetySourceIssue.Action שמשתמש יכול לפתור את הבעיה ישירות מהמסך של מרכז הבטיחות. המשתמש מקיש על לחצן פעולה והמופע PendingIntent ב-SafetySourceIssue.Action שנשלח על ידי מקור הבטיחות מופעל, שפותר את הבעיה ברקע תודיע למרכז הבטיחות בסיום התהליך.

כדי להטמיע פעולות לפתרון בעיות, המקור של מרכז הבטיחות יכול להשתמש בשירות אם הפעולה צפויה להימשך זמן מה (PendingIntent.getService) או מקלט שידורים (PendingIntent.getBroadcast).

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

Intent resolveIssueBroadcastIntent =
    new Intent("my.package.name.MY_RESOLVING_ACTION").setClass(ResolveActionReceiver.class);
PendingIntent resolveIssue =
    PendingIntent.getBroadcast(
        context, requestCode, resolveIssueBroadcastIntent, PendingIntent.FLAG_IMMUTABLE);
SafetySourceData safetySourceData =
    new SafetySourceData.Builder()
        .setStatus(
            new SafetySourceStatus.Builder(
                    "title", "summary", SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                .setPendingIntent(redirectToMyScreen)
                .build())
        .addIssue(
            new SafetySourceIssue.Builder(
                    "MyIssueId",
                    "title",
                    "summary",
                    SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
                    "MyIssueTypeId")
                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
                .addAction(
                    new SafetySourceIssue.Action.Builder(
                            "MyIssueActionId", "label", resolveIssue)
                        .setWillResolve(true)
                        .build())
                .build())
        .build();
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
safetyCenterManager.setSafetySourceData("MySourceId", safetySourceData, safetyEvent);

BroadcastReceiver פותרים את הפעולה:

public final class ResolveActionReceiver extends BroadcastReceiver {
  private static final String MY_RESOLVING_ACTION = "my.package.name.MY_RESOLVING_ACTION";
  @Override
  public void onReceive(Context context, Intent intent) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
      // Must be on T or above to interact with Safety Center.
      return;
    }
    String action = intent.getAction();
    if (!MY_RESOLVING_ACTION.equals(action)) {
      return;
    }
    SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
    if (safetyCenterManager == null) {
      // Should not be null on T.
      return;
    }
    if (!safetyCenterManager.isSafetyCenterEnabled()) {
      // Preferably, no Safety Source code should be run if Safety Center is disabled.
      return;
    }
    resolveTheIssue();
    SafetyEvent resolveActionSafetyEvent =
        new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
            .setSafetySourceIssueId("MyIssueId")
            .setSafetySourceIssueActionId("MyIssueActionId")
            .build();
    SafetySourceData dataWithoutTheIssue = …;
    // Set the data (or report an error with reportSafetySourceError and
    // SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED, if something went wrong).
    safetyCenterManager.setSafetySourceData("MySourceId", dataWithoutTheIssue, resolveActionSafetyEvent);
  }

  private void resolveTheIssue() {
    // Resolves the issue for the user. Given this a BroadcastReceiver, this should be a fast action.
    // Otherwise, a foreground service and PendingIntent.getService should be used instead (or a job
    // could be scheduled here, too).
  }
}

אותו מופע של BroadcastReceiver בדוגמה שלמעלה מוצהר ב- AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="…">
    <application>
    <!-- … -->
        <receiver android:name=".ResolveActionReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="my.package.name.MY_RESOLVING_ACTION"/>
            </intent-filter>
        </receiver>
    <!-- … -->
    </application>
</manifest>

איך מגיבים לדחיית בעיות

אפשר לציין מופע PendingIntent שיופעל כאשר המופע של SafetySourceIssue נסגר. מרכז הבטיחות מטפל בבעיות האלה פירוקים:

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

המקור לא אמור להזדקק להתנהגויות נוספות, אלא אם:

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

מתן נתונים למספר משתמשים/פרופילים

ניתן להשתמש ב-API SafetyCenterManager בכל המשתמשים והפרופילים. לקבלת מידע נוסף מידע נוסף זמין במאמר יצירת גרסה מרובת משתמשים (Multi-user-Aware) אפליקציות. Context אובייקט שמספק את SafetyCenterManager משויך לאובייקט UserHandle כך שהמכונה SafetyCenterManager שמוחזרת מקיימת אינטראקציה עם מרכז הבטיחות של מכונה UserHandle. כברירת מחדל, Context הוא שמשויכים למשתמש הפעיל, אבל אפשר ליצור מופע של משתמש אחר, אם האפליקציה מכילה את INTERACT_ACROSS_USERS וגם הרשאות INTERACT_ACROSS_USERS_FULL. בדוגמה הזו מוצגת ביצוע שיחה בין משתמשים/פרופילים:

Context userContext = context.createContextAsUser(userHandle, 0);
SafetyCenterManager userSafetyCenterManager = userContext.getSystemService(SafetyCenterManager.class);
if (userSafetyCenterManager == null) {
  // Should not be null on T.
  return;
}
// Calls to userSafetyCenterManager will provide data for the given userHandle

לכל משתמש במכשיר יכולים להיות כמה פרופילים מנוהלים. מרכז הבטיחות מספקת נתונים שונים לכל משתמש, אבל ממזגת את הנתונים של כל החשבונות המנוהלים פרופילים שמשויכים למשתמש מסוים.

כשמוגדר profile="all_profiles" למקור בקובץ התצורה, זה מה שקורה:

  • יש רשומת ממשק משתמש עבור המשתמש (הורה של הפרופיל) וכל המידע המשויך פרופילים מנוהלים (משתמשים ב-titleForWork מכונות).
  • אות הרענון או הסריקה מחדש נשלח להורה של הפרופיל ולכל פרופילים מנוהלים משויכים. המקלט המשויך מופעל בכל פרופיל ויכול לספק את הנתונים המשויכים ישירות SafetyCenterManager בלי לבצע קריאה למספר פרופילים, אלא אם של האפליקציה או שהאפליקציה singleUser.

  • המקור צפוי לספק נתונים לגבי המשתמש וכל השירותים המנוהלים שלו פרופילים. הנתונים של כל רשומה בממשק המשתמש עשויים להיות שונים, בהתאם ל פרופיל.

בדיקה

אפשר לגשת אל ShadowSafetyCenterManager ולהשתמש בו במבחן Robolectric.

private static final String MY_SOURCE_ID = "MySourceId";

private final MyClass myClass = …;
private final SafetyCenterManager safetyCenterManager = getApplicationContext().getSystemService(SafetyCenterManager.class);

@Test
public void whenRefreshingData_providesDataToSafetyCenterForMySourceId() {
    shadowOf(safetyCenterManager).setSafetyCenterEnabled(true);
    setupDataForMyClass(…);

    myClass.refreshData();

    SafetySourceData expectedSafetySourceData = …;
    assertThat(safetyCenterManager.getSafetySourceData(MY_SOURCE_ID)).isEqualTo(expectedSafetySourceData);
    SafetyEvent expectedSafetyEvent = …;
    assertThat(shadowOf(safetyCenterManager).getLastSafetyEvent(MY_SOURCE_ID)).isEqualTo(expectedSafetyEvent);
}

אפשר לכתוב עוד בדיקות 'קצה-לקצה' (E2E), אבל זה לא קשור לנושא הזה. מותאמת אישית. למידע נוסף על כתיבת בדיקות E2E האלה, ניתן לעיין במאמר בדיקות CTS (CtsSafetyCenterTestCases)

ממשקי API לבדיקה ופנימיים

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

הרשאות

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • משמש לממשקי ה-API הפנימיים של מרכז הבטיחות
    • מוענק רק ל-PermissionsController ולמעטפת

אפליקציית ההגדרות

הפניה אוטומטית למרכז הבטיחות

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

כשמרכז הבטיחות מופעל:

  • הערך הישן של Privacy (פרטיות) מוסתר בקוד
  • רשומת אבטחה מדור קודם מוסתרת על ידי קוד
  • תכונות חדשות אבטחה פרטיות נוספה רשומה קוד
  • תכונות חדשות אבטחה הערך 'פרטיות' מפנה אוטומטית לקוד של מרכז הבטיחות.
  • android.settings.PRIVACY_SETTINGS ו-android.settings.SECURITY_SETTINGS הפעולות של Intent מופנות לפתיחת מרכז הבטיחות (קוד: אבטחה, פרטיות)

דפים מתקדמים של אבטחה ופרטיות

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

מקורות בטיחות

מרכז הבטיחות משתלב עם קבוצה ספציפית של מקורות בטיחות שמסופקים על ידי אפליקציית ההגדרות:

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

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

מידע על PendingIntent

אם אתם מסתמכים על השילוב הקיים של מרכז הבטיחות של אפליקציית ההגדרות ב-Android בגרסה 14 ואילך, הבאג שמתואר בהמשך תוקן. במקרה הזה אין צורך לקרוא את הקטע הזה.

אם בטוחים שהבאג לא קיים, מגדירים משאב בוליאני של XML ערך הגדרה באפליקציית ההגדרות config_isSafetyCenterLockScreenPendingIntentFixed אל true כדי להשבית את דרך לעקוף את הבעיה במרכז הבטיחות.

פתרון עקיף של PendingIntent

הבאג הזה נגרם על ידי 'הגדרות' באמצעות תוספות של מופע Intent כדי לקבוע מקטע לפתיחה. כי Intent#equals לא לוקח את המופע Intent את התוספות הבאות בחשבון, המופע PendingIntent של הסמל של תפריט גלגל השיניים נחשבים שוויוניים ומנווטים לאותו ממשק משתמש (למרות שהם שרוצים לעבור לממשק משתמש אחר). הבעיה הזו תוקנה במהדורה QPR של הבחנה בין המופעים של PendingIntent לפי קוד בקשה. לחלופין, כדי להבחין ביניהם, אפשר להשתמש בפונקציה Intent#setId.

מקורות בטיחות פנימיים

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

אלה בעיקר אותות בנושא פרטיות, לדוגמה:

  • נגישות
  • ביטול אוטומטי של אפליקציות שלא בשימוש
  • גישה למיקום
  • מאזין להתראות
  • פרטים על המדיניות של מקום העבודה