與安全中心互動

重新導向至安全中心

任何應用程式都可以使用 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 執行個體中使用下列程式碼片段,開啟特定問題的安全中心畫面:

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 執行個體中,可使用下列程式碼片段,開啟特定子頁面的安全中心畫面:

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

startActivity(openSafetyCenterIntent);

使用安全中心來源 API

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

權限

只有許可清單中的系統應用程式,才能使用下列權限存取 Safety Center 來源 API。詳情請參閱將特殊權限加入許可清單一節。

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • 用於 SafetyCenterManager#isSafetyCenterEnabled() API (不需要安全中心來源,只需要 SEND_SAFETY_CENTER_UPDATE 權限即可)
    • 用於檢查安全中心的系統應用程式是否已啟用
    • 僅授予許可清單中的系統應用程式
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • 用於已啟用的 API 和 Safety Source 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 的問題組成。
    • SafetySourceIssue 執行個體 (如有) 不得超過 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 可將使用者重新導向至其他頁面,或直接從安全中心畫面處理動作
    • 選填的布林值,用於指定是否能直接透過 SafetyCenter 畫面解決問題 (預設值為 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 設定設下的額外規定

系統會根據特定事件提供資料給安全中心,因此必須指定導致來源透過 SafetyEvent 例項提供 SafetySourceData 的原因。

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。如要追蹤解決的 SafetySourceIssue.Action,請使用這個方式,而不要使用 SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
    • 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 進行這項操作。
  • 重新整理廣播 ID 的選用 String ID。
  • 針對要解析的 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);

取得上次提供的資料

您可以取得針對應用程式擁有來源的最後一個資料,並提供給安全中心。您可以在自己的 UI 中顯示特定內容、檢查資料是否需要更新再執行高成本作業,或在執行高成本的作業前,將相同的 SafetySourceData 執行個體提供給安全中心,或藉由一些變更或新的 SafetyEvent 執行個體提供。也非常適合用於測試。

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

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

回報錯誤

如果無法收集 SafetySourceData 資料,可以向安全中心回報錯誤,讓安全中心將項目變更為灰色、清除快取資料,並提供類似「Unable check settings」(無法檢查設定) 的訊息。如果 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
    • 當安全中心傳送特定應用程式的安全來源資料要求時,就會觸發這個事件
    • 只有系統可以傳送的受保護意圖
    • 以明確意圖的形式傳送至設定檔中的所有安全性來源,且需要 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" 欄位,來源才能在這些情況下接收傳遞的廣播訊息。

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

您可以在啟用或停用安全中心後,使用下列意圖動作回應:

  • 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 按鈕),即可在 Safety Center 畫面上關閉問題。
  • 使用者關閉問題後,如果問題持續發生,該問題就不會再出現在 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,而我們也會更新本指南,提供如何使用這些 API 的指南。

權限

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

「設定」應用程式

安全中心重新導向

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

啟用安全中心後:

  • 舊版隱私權項目隱藏code
  • 舊版「Security」(安全性) 項目隱藏程式碼
  • 新增「安全性與隱私權」項目並輸入程式碼
  • 新的安全性與隱私權項目重新導向至安全中心程式碼
  • 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 程式碼搜尋取得。

這些主要是隱私權信號,例如:

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