التفاعل مع "مركز الأمان"

إعادة التوجيه إلى "مركز الأمان"

يمكن لأي تطبيق فتح "مركز الأمان" باستخدام الإجراء android.content.Intent.ACTION_SAFETY_CENTER (قيمة السلسلة android.intent.action.SAFETY_CENTER).

لفتح "مركز الأمان"، يمكنك إجراء مكالمة من داخل إحدى مثيلات Activity:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

إعادة التوجيه إلى مشكلة معيّنة

من الممكن أيضًا إعادة التوجيه إلى بطاقة تحذير معيّنة في "مركز الأمان" باستخدام إضافات أهداف معيّنة. هذه الإضافات غير مخصّصة للاستخدام من قِبل جهات خارجية، لذلك تكون جزءًا من SafetyCenterManager، وهي جزء من @SystemApi. لا يمكن سوى لتطبيقات النظام الوصول إلى هذه الإضافات.

عناصر إضافية للنوايا التي تعيد توجيه بطاقة تحذير معيّنة:

  • 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)

في الإصدار 14 من نظام التشغيل Android أو الإصدارات الأحدث، يتم تقسيم صفحة "مركز الأمان" إلى صفحات فرعية متعددة تمثّل SafetySourcesGroup المختلفة (في الإصدار 13 من نظام التشغيل Android، يتم عرض ذلك كإدخالات قابلة للطي).

من الممكن إعادة التوجيه إلى صفحة فرعية معيّنة باستخدام سمة النية الإضافية هذه:

  • 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);

استخدام واجهات برمجة تطبيقات مصدر "مركز الأمان"

تتوفّر واجهات برمجة التطبيقات لمصدر "مركز السلامة" باستخدام SafetyCenterManager (وهو @SystemApi). يتوفّر رمز واجهة برمجة التطبيقات في بحث الرمز. يتوفّر رمز تنفيذ واجهات برمجة التطبيقات في بحث الرمز.

الأذونات

لا يمكن الوصول إلى واجهات برمجة التطبيقات المصدر في "مركز الأمان" إلا من خلال تطبيقات النظام المُدرَجة في القائمة المسموح بها، وذلك باستخدام الأذونات المُدرَجة أدناه. لمزيد من المعلومات، يُرجى الاطّلاع على قائمة السماح بالوصول إلى الأذونات المميّزة.

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • يُستخدَم هذا الإذن لواجهة برمجة التطبيقات SafetyCenterManager#isSafetyCenterEnabled() (لا يُشترط حصوله على مصادر "مركز السلامة"، إذ يكتفي بالحصول على إذن SEND_SAFETY_CENTER_UPDATE)
    • تُستخدَم هذه الإذنات من قِبل تطبيقات النظام التي تتحقّق مما إذا كان "مركز الأمان" مفعّلاً.
    • لا يتم منحه إلا لتطبيقات النظام المُدرَجة في القائمة المسموح بها
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • تُستخدَم لواجهة برمجة التطبيقات المفعَّلة وواجهة برمجة التطبيقات 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 المشاكل).
    • يجب استيفاء المتطلبات الإضافية التي تفرضها إعدادات واجهة برمجة التطبيقات، على سبيل المثال، إذا كان المصدر يتضمّن المشاكل فقط، يجب عدم توفير مثيل SafetySourceStatus.

SafetySourceStatus

  • عنوان CharSequence مطلوب
  • ملخّص CharSequence مطلوب
  • مستوى الخطورة المطلوب
  • عنصر اختياري PendingIntent لإعادة توجيه المستخدم إلى الصفحة الصحيحة (يستخدم الإعداد التلقائي intentAction من الإعدادات، إن توفّرت)
  • العمود الاختياري IconAction (يظهر كرمز جانبي في الإدخال) ويتألف من:
    • نوع الرمز المطلوب، والذي يجب أن يكون أحد الأنواع التالية:
      • ICON_TYPE_GEAR: يظهر كعجلة بجانب إدخال واجهة المستخدم
      • ICON_TYPE_INFO: يتم عرضها كرمز معلومات بجانب إدخال واجهة المستخدم.
    • مطلوب PendingIntent لإعادة توجيه المستخدم إلى صفحة أخرى
  • قيمة منطقية اختيارية enabled تسمح بوضع علامة على إدخال واجهة المستخدم على أنّه مُعطل، وبالتالي لا يمكن النقر عليه (القيمة التلقائية هي true)
  • القيم الثابتة:
    • يجب أن تفتح نُسخ PendingIntent نسخة Activity.
    • إذا كان الإدخال غير مفعّل، يجب تحديده على أنّه SEVERITY_LEVEL_UNSPECIFIED.
    • المتطلبات الإضافية التي تفرضها إعدادات واجهة برمجة التطبيقات

SafetySourceIssue

  • المعرّف الفريد المطلوب String
  • عنوان CharSequence مطلوب
  • ترجمة CharSequence اختيارية
  • ملخّص CharSequence مطلوب
  • مستوى الخطورة المطلوب
  • فئة المشكلة الاختيارية، والتي يجب أن تكون إحدى الفئات التالية:
    • ISSUE_CATEGORY_DEVICE: تؤثر المشكلة في جهاز المستخدم.
    • ISSUE_CATEGORY_ACCOUNT: تؤثر المشكلة في حسابات المستخدم.
    • ISSUE_CATEGORY_GENERAL: تؤثّر المشكلة في السلامة العامة للمستخدم. هذا هو الخيار التلقائي.
    • ISSUE_CATEGORY_DATA (بدءًا من Android 14): تؤثر المشكلة في بيانات المستخدم.
    • ISSUE_CATEGORY_PASSWORDS (بدءًا من الإصدار 14 من Android): تؤثر المشكلة فيكلمات مرور المستخدم.
    • ISSUE_CATEGORY_PERSONAL_SAFETY (بدءًا من الإصدار 14 من Android): تؤثر المشكلة في أمان المستخدم الشخصي.
  • قائمة بعناصر Action التي يمكن للمستخدم اتّخاذها لحلّ هذه المشكلة، وتتألّف كل مثيل Action من:
    • المعرّف الفريد المطلوب String
    • تصنيف CharSequence مطلوب
    • مطلوب PendingIntent لإعادة توجيه المستخدم إلى صفحة أخرى أو تنفيذ الإجراء مباشرةً من شاشة "مركز الأمان"
    • قيمة منطقية اختيارية لتحديد ما إذا كان يمكن حلّ هذه المشكلة مباشرةً من شاشة "مركز الأمان" (القيمة التلقائية هي false)
    • CharSequence رسالة اختيارية للنجاح، يتم عرضها للمستخدم عند حلّ المشكلة بنجاح مباشرةً من شاشة "مركز السلامة"
  • PendingIntent اختياري، يتم استدعاؤه عندما يرفض المستخدم المشكلة (الإعداد التلقائي هو عدم استدعاء أي إجراء)
  • مطلوب String معرّف نوع المشكلة، وهو مشابه لمعرّف المشكلة ولكن ليس ضروريًا أن يكون فريدًا ويتم استخدامه في التسجيل
  • String اختياري لـ "معرّف إزالة التكرار"، ويسمح هذا الخيار بنشر SafetySourceIssue نفسه من مصادر مختلفة وعرضه مرة واحدة فقط في واجهة المستخدم بافتراض أنّه يحتوي علىdeduplicationGroup نفسه (بدءًا من Android 14). إذا لم يتم تحديده، لن تتم أبدًا إزالة تكرار المشكلة.
  • CharSequence اختياري لعنوان الإسناد، وهو نص يعرض مصدر بطاقة التحذير (بدءًا من Android 14). في حال عدم تحديده، يتم استخدام عنوان SafetySourcesGroup
  • إمكانية معالجة المشكلة (اختيارية، بدءًا من Android 14)، يجب أن تكون أحد الخيارَين التاليَين:
    • ISSUE_ACTIONABILITY_MANUAL: على المستخدم حلّ هذه المشكلة يدويًا. هذا هو الخيار التلقائي.
    • 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.
    • المتطلبات الإضافية المفروضة من خلال إعدادات واجهة برمجة التطبيقات

يتم تقديم البيانات إلى "مركز الأمان" عند حدوث أحداث معيّنة، لذا من الضروري تحديد السبب الذي أدّى إلى تقديم المصدر ل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، يمكنك الإبلاغ عن الخطأ في Safety Center، ما يؤدي إلى تغيير الإدخال إلى اللون الرمادي، وحذف البيانات المخزّنة مؤقتًا، وعرض رسالة مثل تعذّر التحقّق من الإعداد. يمكنك أيضًا الإبلاغ عن خطأ إذا تعذّر حلّ مثيل 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
    • يتم تشغيله عندما يُرسِل "مركز الأمان" طلبًا لإعادة تحميل بيانات مصدر الأمان لتطبيق معيّن.
    • نية محمية لا يمكن إرسالها إلا من خلال النظام
    • يتم إرسالها إلى جميع مصادر الأمان في ملف الإعدادات كهدف صريح ويتطلب إذن 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>

من الأفضل تنفيذ مصدر Safety Center بطريقة تؤدي إلى استدعاء SafetyCenterManager عند تغيير بياناته. لأسباب تتعلّق بحالة النظام، ننصح بالردّ على إشارة إعادة الفحص فقط (عندما ينقر المستخدم على زر الفحص)، وليس عندما يفتح المستخدم "مركز الأمان". إذا كانت هذه الوظيفة مطلوبة، يجب ضبط الحقل refreshOnPageOpenAllowed="true" في ملف الإعدادات لكي يتلقّى المصدر البث الذي يتم إرساله في هذه الحالات.

الاستجابة لـ "مركز السلامة" عند تفعيله أو إيقافه

يمكنك الاستجابة عند تفعيل "مركز السلامة" أو إيقافه باستخدام إجراء النية التالي:

  • ACTION_SAFETY_CENTER_ENABLED_CHANGED
    • قيمة السلسلة: android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED
    • يتم تشغيله عند تفعيل "مركز السلامة" أو إيقافه أثناء تشغيل الجهاز.
    • لا يتم استدعاؤه عند بدء التشغيل (استخدِم ACTION_BOOT_COMPLETED لإجراء ذلك)
    • نية محمية لا يمكن إرسالها إلا من خلال النظام
    • يتم إرسالها إلى جميع مصادر الأمان في ملف الإعدادات كهدف صريح، ويتطلب ذلك الحصول على إذن SEND_SAFETY_CENTER_UPDATE.
    • تم إرساله كطلب ضمني يتطلب إذن READ_SAFETY_CENTER_STATUS

يُعدّ إجراء النية هذا مفيدًا لتفعيل الميزات ذات الصلة بمركز الأمان أو إيقافها على الجهاز.

تنفيذ إجراءات الحل

إجراء الحل هو مثيل 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 يومًا ما لم يُغلِقها المستخدم عدة مرات.

يجب ألا يحتاج المصدر إلى سلوكيات إضافية إلا في الحالات التالية:

  • يحاول المصدر تنفيذ هذا السلوك بشكل مختلف، على سبيل المثال، عدم إعادة عرض المشكلة مطلقًا.
  • يحاول المصدر استخدام هذا الإجراء كمكالمة استدعاء، على سبيل المثال، لتسجيل المعلومات.

تقديم بيانات لعدة مستخدمين/ملفات شخصية

يمكن استخدام واجهة برمجة التطبيقات 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).

واجهات برمجة التطبيقات الاختبارية والداخلية

واجهات برمجة التطبيقات الداخلية وواجهات برمجة التطبيقات الاختبارية مخصّصة للاستخدام الداخلي، لذا لم يتم وصفها بالتفصيل في هذا الدليل. ومع ذلك، قد نوفّر في المستقبل بعض واجهات برمجة التطبيقات الداخلية للسماح لمصنّعي المعدّات الأصلية بإنشاء واجهة المستخدم الخاصة بهم، وسنعدّل هذا الدليل لتقديم إرشادات حول كيفية استخدامها.

الأذونات

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • تُستخدَم لواجهتَي برمجة التطبيقات الداخليتَين لمركز الأمان.
    • لا يتم منحه إلا لـ PermissionController وshell

تطبيق "الإعدادات"

إعادة التوجيه إلى "مركز الأمان"

يتم الوصول إلى "مركز الأمان" تلقائيًا من خلال تطبيق "الإعدادات" من خلال إدخال الأمان والخصوصية الجديد. إذا كنت تستخدم تطبيق "الإعدادات" مختلفًا أو إذا عدّلت تطبيق "الإعدادات"، قد تحتاج إلى تخصيص كيفية الوصول إلى "مركز الأمان".

عند تفعيل "مركز السلامة":

  • رمز إخفاء إدخال الخصوصية القديم
  • إدخال الأمان القديم مخفي الرمز
  • تمت إضافة إدخال جديد للأمان والخصوصية رمز
  • إعادة توجيه الإدخال الجديد الأمان والخصوصية إلى رمز مركز الأمان
  • android.settings.PRIVACY_SETTINGS وandroid.settings.SECURITY_SETTINGS: تتم إعادة توجيه إجراءات النية إلى فتح "مركز الأمان" (الرمز: security، privacy)

صفحات الأمان والخصوصية المتقدّمة

يحتوي تطبيق "الإعدادات" على إعدادات إضافية ضمن عنوانَي المزيد من إعدادات الأمان والمزيد من إعدادات الخصوصية، وهما متاحان من "مركز الأمان":

مصادر السلامة

يتم دمج "مركز الأمان" مع مجموعة محدّدة من مصادر الأمان التي يوفّرها تطبيق "الإعدادات":

  • يتحقق مصدر أمان شاشة القفل من ضبط شاشة القفل باستخدام رمز مرور (أو ميزة أمان أخرى) لضمان الحفاظ على أمان معلومات المستخدم الخاصة وعدم السماح بالوصول الخارجي إليها.
  • يظهر مصدر أمان المقاييس الحيوية (مخفي تلقائيًا) للدمج مع أدوات استشعار بصمة الإصبع أو الوجه.

يمكن الوصول إلى رمز المصدر لمصادر "مركز الأمان" هذه من خلال Android code search. إذا لم يتم تعديل تطبيق "الإعدادات" (أي عدم إجراء تغييرات على اسم الحزمة أو رمز المصدر أو رمز المصدر الذي يتعامل مع شاشة القفل والسمات الحيوية)، من المفترض أن يعمل هذا الدمج بشكلٍ تلقائي. بخلاف ذلك، قد يلزم إجراء بعض التعديلات، مثل تغيير ملف الإعدادات لتغيير اسم حزمة تطبيق "الإعدادات" والمصادر التي يتم دمجها مع "مركز الأمان"، بالإضافة إلى عملية الدمج. لمزيد من المعلومات، يُرجى الاطّلاع على تعديلملف الإعداد وإعداداتتكامل.

لمحة عن PendingIntent

إذا كنت تعتمد على دمج "مركز الأمان" الحالي في تطبيق "الإعدادات" على الإصدار 14 من Android أو الإصدارات الأحدث، تم إصلاح الخطأ الموضّح أدناه. لا يلزم قراءة هذا القسم في هذه الحالة.

عندما تكون متأكدًا من عدم توفّر الخطأ، اضبط قيمة إعدادات موارد XML المنطقية في تطبيق "الإعدادات" على config_isSafetyCenterLockScreenPendingIntentFixed إلى true لإيقاف الحلّ البديل ضمن "مركز الأمان".

حلّ بديل لرمز PendingIntent

تحدث هذه المشكلة بسبب استخدام "الإعدادات" لإضافات مثيل Intent لتحديد القطعة التي سيتم فتحها. بما أنّ Intent#equals لا يأخذ في الاعتبار الإضافات الخاصة بمثيل Intent، يتم اعتبار مثيل PendingIntent الخاص بأيقونة قائمة التروس ومحاولة التنقّل في العنصر متساويين وينقلان إلى واجهة المستخدم نفسها (على الرغم من أنّه من المفترض أن ينقلا إلى واجهة مستخدم مختلفة). تم إصلاح هذه المشكلة في إصدار QPR من خلال التمييز بين نُسخ PendingIntent حسب رمز الطلب. بدلاً من ذلك، يمكن التمييز بين هذه القيم باستخدام Intent#setId.

مصادر السلامة الداخلية

بعض مصادر "مركز الأمان" داخلية ويتم تنفيذها في تطبيق النظام PermissionController داخل وحدة PermissionController. تتصرّف هذه المصادر مثل مصادر "مركز الأمان" العادية ولا تتلقّى أي معالجة خاصة. يتوفّر رمز هذه المصادر من خلال بحث رمز Android.

وتشمل هذه الإشارات بشكل أساسي إشارات الخصوصية، على سبيل المثال:

  • تسهيل الاستخدام
  • الإلغاء التلقائي للأذونات الممنوحة للتطبيقات غير المستخدَمة
  • الوصول إلى الموقع الجغرافي
  • برنامج تلقّي الإشعارات الصوتية
  • معلومات سياسة العمل