與安全中心互動

重新導向至安全中心

任何應用程式都可以使用 android.content.Intent.ACTION_SAFETY_CENTER 動作 (字串值 android.intent.action.SAFETY_CENTER) 開啟安全中心。

如要開啟安全中心,請在 Activity 例項中撥打電話:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

重新導向至特定問題

您也可以使用特定意圖額外資料,將使用者重新導向至特定 Safety Center 警告資訊卡。這些額外項目並非供第三方使用,因此屬於 SafetyCenterManager 的一部分,而 SafetyCenterManager@SystemApi 的一部分。只有系統應用程式可以存取這些額外項目。

將特定警告資訊卡重新導向的意圖額外項目:

  • EXTRA_SAFETY_SOURCE_ID
    • 字串值:android.safetycenter.extra.SAFETY_SOURCE_ID
    • 字串類型:指定相關警告資訊卡的安全來源 ID
    • 必填,才能將問題轉介給相關人員
  • EXTRA_SAFETY_SOURCE_ISSUE_ID
    • 字串值:android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID
    • 字串類型:指定警告資訊卡 ID
    • 必填,才能將問題轉介給相關人員
  • EXTRA_SAFETY_SOURCE_USER_HANDLE
    • 字串值:android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE
    • UserHandle 類型:為相關警告資訊卡指定 UserHandle
    • 選用 (預設為目前使用者)

您可以在 Activity 例項中使用下列程式碼片段,開啟 Safety Center 畫面並顯示特定問題:

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 中,這會顯示為可摺疊的項目)。

您可以使用這個意圖額外資料,將使用者重新導向至特定子頁面:

  • EXTRA_SAFETY_SOURCES_GROUP_ID
    • 字串值:android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID
    • 字串類型:指定 SafetySourcesGroup 的 ID
    • 如要將使用者重新導向至子頁面,此屬性為必填

您可以在 Activity 例項中使用下列程式碼片段,將 Safety Center 畫面開啟至特定子頁面:

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

startActivity(openSafetyCenterIntent);

使用 Safety Center 來源 API

您可以使用 SafetyCenterManager (即 @SystemApi) 取得 Safety Center 來源 API。API 途徑的程式碼可在 Code Search 中取得。您可以在 Code Search 中找到 API 的實作程式碼。

權限

只有獲准清單中的系統應用程式,才能使用下列權限存取 Safety Center 來源 API。如需更多資訊,請參閱「特權權限許可清單」。

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • 用於 SafetyCenterManager#isSafetyCenterEnabled() API (Safety Center 來源不需要此權限,只需要 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_STATUSSEND_SAFETY_CENTER_UPDATE 權限:

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

提供資料

安全中心來源資料會連同指定的 String sourceId,透過 SafetySourceData 物件提供給安全中心,代表 UI 項目和問題清單 (警告卡片)。UI 項目和警告資訊卡可在 SafetySourceData 類別中指定不同的嚴重性等級:

  • SEVERITY_LEVEL_UNSPECIFIED
    • 未指定嚴重性
    • 顏色:灰色或透明 (視項目的 SafetySourcesGroup 而定)
    • 用於在 UI 中以靜態項目形式顯示動態資料,或顯示未指定的項目
    • 不得用於警告卡片
  • SEVERITY_LEVEL_INFORMATION
    • 基本資訊或較不重要的建議
    • 顏色:綠色
  • SEVERITY_LEVEL_RECOMMENDATION
    • 建議使用者採取行動,因為這個問題可能會讓他們面臨風險
    • 顏色:黃色
  • SEVERITY_LEVEL_CRITICAL_WARNING
    • 嚴重警告,使用者必須採取行動,因為這個問題存在風險
    • 顏色:紅色

SafetySourceData

SafetySourceData 物件由 UI 項目、警告資訊卡和不變量組成。

  • 選填 SafetySourceStatus 例項 (UI 項目)
  • SafetySourceIssue 例項 (警告資訊卡) 清單
  • 選用 Bundle 額外項目 (14 以上版本)
  • 不變量:
    • SafetySourceIssue 清單必須由具有專屬 ID 的問題組成。
    • 如果有 SafetySourceStatusSafetySourceIssue 例項不得比 SafetySourceStatus 重要 (除非 SafetySourceStatusSEVERITY_LEVEL_UNSPECIFIED,在這種情況下允許 SEVERITY_LEVEL_INFORMATION 問題)。
    • 必須符合 API 設定規定的其他要求,例如,如果來源僅為問題,則不得提供 SafetySourceStatus 例項。

SafetySourceStatus

  • 必要的 CharSequence 標題
  • 必填 CharSequence 摘要
  • 必要的嚴重性等級
  • 可選的 PendingIntent 例項,用於將使用者重新導向至正確的頁面 (預設會使用設定中的 intentAction,如果有)
  • 選用的 IconAction (顯示為項目旁邊的圖示),由下列項目組成:
    • 必要圖示類型,必須是下列其中一種類型:
      • ICON_TYPE_GEAR:顯示為 UI 項目旁的齒輪
      • ICON_TYPE_INFO:顯示為 UI 項目旁的資訊圖示
    • 必須使用 PendingIntent 將使用者重新導向至其他網頁
  • 可選的布林值 enabled 值,可將 UI 項目標示為已停用,因此無法點選 (預設為 true)
  • 不變量:
    • PendingIntent 例項必須開啟 Activity 例項。
    • 如果項目已停用,則必須指定為 SEVERITY_LEVEL_UNSPECIFIED
    • API 設定規定的其他要求。

SafetySourceIssue

  • 必填的專屬 String ID
  • 必要的 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 ID
    • 必要的 CharSequence 標籤
    • 需要 PendingIntent 才能將使用者重新導向至其他頁面,或直接在安全中心畫面中處理操作
    • 選用布林值,用於指定是否可直接透過 Safety Center 畫面解決此問題 (預設值為 false)
    • 選用的 CharSequence 成功訊息,會在使用者直接透過安全中心畫面成功解決問題時顯示
  • 使用者關閉問題時呼叫的選用 PendingIntent (預設為不呼叫)
  • 必填 String 問題類型 ID;這類似於問題 ID,但不必是唯一值,且用於記錄
  • 可選的 String 用於重複排除 ID,這可讓您從不同來源發布相同的 SafetySourceIssue,並在 UI 中只顯示一次 (假設這些來源具有相同的 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 例項清單必須由具有不重複 ID 的動作組成
    • Action 例項清單必須包含一或兩個 Action 元素。如果可操作性不是 ISSUE_ACTIONABILITY_MANUAL,則可使用零個 Action
    • OnDismiss PendingIntent 不得開啟 Activity 例項
    • API 設定規定的其他要求

系統會在特定事件發生時將資料提供給 Safety Center,因此必須指定導致來源向 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 Center 資料不會在重新啟動時保留;因此,可以使用 SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED 來提供這項資料。
  • 更新廣播 ID 的選用 String 識別碼。
  • 針對要解析的 SafetySourceIssue 例項,提供選用的 String 識別碼。
  • 針對要解析的 SafetySourceIssue.Action 例項,提供選用的 String 識別碼。
  • 不變量:
    • 如果類型為 SAFETY_EVENT_TYPE_REFRESH_REQUESTED,則必須提供重新整理廣播 ID
    • 如果類型為 SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDEDSAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED,則必須提供問題和動作 ID

以下範例說明來源可能如何向安全中心提供資料 (在本例中,它提供的是包含單一警告卡片的項目):

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

取得上次提供的資料

您可以取得應用程式擁有的來源向 Safety Center 提供的最新資料。您可以利用這項資料在自己的 UI 中顯示內容,以便檢查是否需要在執行耗時作業前更新資料,或是將相同的 SafetySourceData 例項連同某些變更或新的 SafetyEvent 例項提供給 Safety Center。這在測試時也相當實用。

使用以下程式碼,取得 Safety Center 收到的最新資料:

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

回報錯誤

如果您無法收集 SafetySourceData 資料,可以將錯誤回報給 Safety Center,系統會將項目變更為灰色、清除快取資料,並提供類似「無法檢查設定」的訊息。如果 SafetySourceIssue.Action 例項無法解析,您也可以回報錯誤。在這種情況下,系統不會清除快取資料,也不會變更 UI 項目,但會向使用者顯示訊息,告知發生錯誤。

您可以使用 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
    • 當 Safety Center 傳送要求,以便為特定應用程式重新整理安全性來源資料時觸發
    • 只有系統可以傳送的受保護意圖
    • 以明確意圖的形式傳送至設定檔中的所有安全性來源,且需要 SEND_SAFETY_CENTER_UPDATE 權限

本節目提供下列額外內容:

  • EXTRA_REFRESH_SAFETY_SOURCE_IDS
    • 字串值:android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS
    • 字串陣列類型 (String[]),代表要為指定應用程式重新整理的來源 ID
  • 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
    • 字串類型,代表要求重新整理作業的專屬 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 的方式實作,以便在資料變更時呼叫 SafetyCenterManager。基於系統健康狀態的考量,建議您只回應重新掃描信號 (使用者輕觸掃描按鈕時),而不要回應使用者開啟安全中心時的事件。如果需要這項功能,請務必設定設定檔中的 refreshOnPageOpenAllowed="true" 欄位,讓來源在這些情況下接收廣播。

在啟用或停用安全中心時回應

您可以使用這個意圖動作,回應 Safety Center 啟用或停用的情況:

  • 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 例項,使用者可以直接從安全中心畫面解決。使用者輕觸動作按鈕,安全來源傳送的 SafetySourceIssue.Action 上的 PendingIntent 例項就會觸發,在背景解決問題,並在完成時通知安全中心。

如要實作解決動作,如果預期作業需要一些時間 (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 例項關閉時觸發。安全中心會處理下列問題的駁回:

  • 如果來源推送問題,使用者只要輕觸「Dismiss」按鈕 (警告資訊卡上的 X 按鈕),即可在安全中心畫面上關閉問題。
  • 使用者關閉問題時,如果問題持續存在,系統就不會再次在 UI 中顯示該問題。
  • 裝置重新啟動時,磁碟上的永久解除會保留。
  • 如果 Safety Center 來源停止提供問題,但在日後又再次提供問題,則該問題會再次顯示。這麼做是為了避免使用者看到警告、關閉警告,然後採取應可緩解問題的行動,但使用者又再次採取某些動作,導致類似問題。此時,警告資訊卡片應會再次顯示。
  • 除非使用者多次關閉,否則黃色和紅色警告卡會每 180 天重新顯示一次。

除非符合下列情況,否則來源不需要額外行為:

  • 來源會嘗試以不同方式實作這項行為,例如永不重現問題。
  • 來源會嘗試將此做為回呼,例如記錄資訊。

提供多位使用者/個人資料的資料

SafetyCenterManager API 可跨使用者和設定檔使用。詳情請參閱「建構支援多用戶的應用程式」。提供 SafetyCenterManagerContext 物件與 UserHandle 例項相關聯,因此傳回的 SafetyCenterManager 例項會與該 UserHandle 例項的 Safety Center 互動。根據預設,Context 會與執行的使用者建立關聯,但如果應用程式擁有 INTERACT_ACROSS_USERSINTERACT_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 例項) 都有 UI 項目。
  • 系統會針對上層發布商簡介和所有相關的受管理商家檔案傳送重新整理或重新掃描訊號。系統會為每個設定檔啟動相關聯的接收器,並可直接將相關聯的資料提供給 SafetyCenterManager,無須跨設定檔呼叫 (除非接收器或應用程式為 singleUser)。

  • 來源應為使用者和所有受管理的設定檔提供資料。每個 UI 項目的資料可能因設定檔而異。

測試

您可以存取 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,讓原始設備製造商 (OEM) 自行建構 UI,屆時我們會更新本指南,提供相關使用說明。

權限

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • 用於內部 Safety Center API
    • 僅授予 PermissionController 和殼層

「設定」應用程式

安全中心重新導向

根據預設,您可以透過「設定」應用程式存取安全中心,並使用新的「安全性與隱私權」項目。如果您使用其他「設定」應用程式,或已修改「設定」應用程式,可能需要自訂存取安全中心的方式。

啟用安全中心後:

  • 隱藏舊版隱私權項目的程式碼
  • 舊版安全性項目隱藏code
  • 新增「安全性和隱私權」項目 code
  • 新的「安全性和隱私權」項目會重新導向至安全中心code
  • android.settings.PRIVACY_SETTINGSandroid.settings.SECURITY_SETTINGS 意圖動作會重新導向至安全中心 (code: securityprivacy)

進階安全性和隱私權頁面

「設定」應用程式包含「更多安全性設定」和「更多隱私權設定」標題下的其他設定,可透過安全中心存取:

安全性來源

Safety Center 會整合「設定」應用程式提供的特定安全性來源:

  • 鎖定畫面安全來源會驗證鎖定畫面是否已設定密碼 (或其他安全措施),確保使用者的私人資訊不會遭到外部存取。
  • 生物特徵辨識安全來源 (預設為隱藏) 會顯示,以便整合指紋或臉部辨識感應器。

您可以透過 Android 程式碼搜尋,取得這些安全中心來源的原始碼。如果未修改「設定」應用程式 (未變更套件名稱、原始碼或處理螢幕鎖定和生物辨識功能的原始碼),則這項整合功能應可立即使用。否則,您可能需要進行一些修改,例如變更設定檔,以變更設定應用程式的套件名稱,以及與安全中心整合的來源,以及整合作業。詳情請參閱「更新設定檔」和整合設定

關於 PendingIntent

如果您使用 Android 14 以上版本中現有的「設定」應用程式 Safety Center 整合功能,下方所述的錯誤已修正。在這種情況下,您不必閱讀本節內容。

確定沒有錯誤後,請將「設定」應用程式 config_isSafetyCenterLockScreenPendingIntentFixed 中的 XML 布林值資源設定值設為 true,以關閉安全中心的解決方法。

PendingIntent 解決方法

這項錯誤是因為設定使用 Intent 例項額外項目來判斷要開啟哪個 Fragment。由於 Intent#equals 不會考量 Intent 例項額外項目,因此齒輪圖示和項目的 PendingIntent 例項會視為相同,並導向相同的 UI (即使它們原本要導向不同的 UI)。這個問題已在 QPR 版本中修正,方法是依據要求代碼區分 PendingIntent 例項。或者,您也可以使用 Intent#setId 進行區分。

內部安全性來源

部分安全中心來源是內部來源,會在 PermissionController 模組中的 PermissionController 系統應用程式中實作。這些來源的運作方式與一般安全中心來源相同,不會受到特殊處理。您可以透過 Android 程式碼搜尋取得這些來源的程式碼。

這些信號主要與隱私權相關,例如:

  • 無障礙設定
  • 自動撤銷未使用的應用程式
  • 位置資訊存取權
  • 通知接聽器
  • 工作政策資訊