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

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

כל אפליקציה יכולה לפתוח את מרכז הבטיחות באמצעות פעולה אחת (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 type: מציין את הערך 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 זמין בחיפוש קוד.

הרשאות

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

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

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

הורדה של SafetyCenterManager

SafetyCenterManager היא סוג של @SystemApi שאפשר לגשת אליו מאפליקציות מערכת החל מגרסה Android 13. הקריאה הזו מדגימה איך לקבל את 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 (החל מגרסה 14 של Android). אם לא צוין אחרת, הבעיה אף פעם בוטל כפילויות
  • 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
    • נשלחת ככוונה משתמעת שדורשת את ההרשאה READ_SAFETY_CENTER_STATUS

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

הטמעת פעולות פתרון

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

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

אפשר להשתמש בקוד הזה כדי לשלוח ל-Safety Center דיווח על בעיה שהושלמה:

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 בכל המשתמשים והפרופילים. למידע נוסף, ראו פיתוח אפליקציות שמזהות משתמשים מרובים. האובייקט 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 הפנימיים של מרכז הבטיחות
    • הרשאה שמוענקת רק ל-PermissionController ולמעטפת

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

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

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

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

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

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

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

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

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

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

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

מידע על PendingIntent

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

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

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

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

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

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

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

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