안전 센터로 리디렉션
모든 앱에서 android.content.Intent.ACTION_SAFETY_CENTER
작업(문자열 값 android.intent.action.SAFETY_CENTER
)을 사용하여 안전 센터를 열 수 있습니다.
안전 센터를 열려면 Activity
인스턴스 내에서 다음과 같이 호출하세요.
Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);
startActivity(openSafetyCenterIntent);
특정 문제로 리디렉션
특정 인텐트 추가 항목을 사용하여 특정 안전 센터 경고 카드로 리디렉션할 수도 있습니다. 이러한 추가 항목은 서드 파티에서 사용하면 안 되므로 @SystemApi
의 일부인 SafetyCenterManager
의 일부입니다. 시스템 앱만 이러한 추가 항목에 액세스할 수 있습니다.
특정 경고 카드를 리디렉션하는 인텐트 추가 항목은 다음과 같습니다.
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 사용
안전 센터 소스 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
는 Android 13부터 시스템 앱에서 액세스할 수 있는 @SystemApi
클래스입니다. 이 호출은 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
가 있는 안전 센터 소스 데이터는 UI 항목과 문제 목록(경고 카드)을 나타내는 SafetySourceData
객체를 사용하여 안전 센터에 제공됩니다. 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
목록은 고유 식별자가 있는 문제로 구성되어야 합니다.SafetySourceIssue
인스턴스는SafetySourceStatus
(있는 경우)보다 중요도가 높아서는 안 됩니다. 단,SafetySourceStatus
가SEVERITY_LEVEL_UNSPECIFIED
인 경우는 예외입니다. 여기서는SEVERITY_LEVEL_INFORMATION
문제가 허용됩니다.- API 구성으로 부과되는 추가 요구사항을 충족해야 합니다. 예를 들어 소스가 문제 전용인 경우
SafetySourceStatus
인스턴스를 제공하면 안 됩니다.
SafetySourceStatus
- 필수
CharSequence
제목 - 필수
CharSequence
요약 - 필수 심각도 수준
- 사용자를 올바른 페이지로 리디렉션하는 선택적
PendingIntent
인스턴스(기본값은 구성에서intentAction
을 사용함(있는 경우)) - 선택적
IconAction
(항목에서 측면 아이콘으로 표시됨)은 다음으로 구성됩니다.- 필수 아이콘 유형. 다음 유형 중 하나여야 합니다.
ICON_TYPE_GEAR
: UI 항목 옆에 톱니바퀴로 표시됩니다.ICON_TYPE_INFO
: UI 항목 옆에 정보 아이콘으로 표시됩니다.
- 사용자를 다른 페이지로 리디렉션하는 필수
PendingIntent
- 필수 아이콘 유형. 다음 유형 중 하나여야 합니다.
- UI 항목을 사용 중지됨으로 표시하여 클릭할 수 없도록 하는 선택적 불리언
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
문제 유형 식별자. 이는 문제 식별자와 비슷하나 고유하지 않아도 되며 로깅에 사용됩니다. - 중복 삭제 ID에 관한 선택적
String
. 이를 통해 다양한 소스에서 동일한SafetySourceIssue
를 게시하고, 동일한deduplicationGroup
을 보유한다고 가정하여 UI에 한 번만 표시할 수 있습니다(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
인스턴스를 열면 안 됩니다. - 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
을 해결하려고 했지만 실패했습니다. 실패한SafetySourceIssue.Action
을 안전 센터에서 추적할 수 있도록SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
대신 사용합니다.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
식별자 - 해결되는
SafetySourceIssue
인스턴스의 선택적String
식별자 - 해결되는
SafetySourceIssue.Action
인스턴스의 선택적String
식별자 - 불변:
- 유형이
SAFETY_EVENT_TYPE_REFRESH_REQUESTED
이면 새로고침 브로드캐스트 ID를 제공해야 합니다. - 유형이
SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED
또는SAFETY_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에 무언가를 표시하거나, 비용이 많이 드는 작업을 실행하기 전에 데이터를 업데이트해야 하는지 확인하거나, 일부를 변경하여 또는 새 SafetyEvent
인스턴스와 함께 안전 센터에 동일한 SafetySourceData
인스턴스를 제공할 수 있습니다. 테스트에도 유용합니다.
다음 코드를 사용하여 안전 센터에 제공된 마지막 데이터를 가져오세요.
SafetySourceData lastDataProvided =
safetyCenterManager.getSafetySourceData("MySourceId");
오류 신고
SafetySourceData
데이터를 수집할 수 없다면 안전 센터에 오류를 신고할 수 있습니다. 그러면 항목이 회색으로 변경되고 캐시된 데이터가 삭제되며 설정을 확인할 수 없음과 같은 메시지가 표시됩니다. 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
- 문자열 유형으로, 요청된 새로고침의 고유 식별자를 나타냅니다.
- 문자열 값:
안전 센터에서 신호를 받으려면 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"
필드를 설정해야 합니다.
사용 설정 또는 사용 중지 시 안전 센터에 응답
안전 센터 사용 설정 또는 사용 중지 시 다음 인텐트 작업을 사용하여 응답할 수 있습니다.
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
) 또는 broadcast receiver(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>
문제 닫기에 응답
SafetySourceIssue
인스턴스가 닫힐 때 트리거될 수 있는 PendingIntent
인스턴스를 지정할 수 있습니다. 안전 센터는 이러한 문제 닫기를 다음과 같이 처리합니다.
- 소스에서 문제를 푸시하면 사용자는 닫기 버튼(경고 카드의 X 버튼)을 탭하여 안전 센터 화면에서 문제를 닫을 수 있습니다.
- 사용자가 문제를 닫으면 문제가 계속돼도 UI에 다시 표시되지 않습니다.
- 디스크에서 지속적 닫기는 기기 재부팅 중에 유지됩니다.
- 안전 센터 소스에서 문제 제공을 중지했다가 나중에 문제를 다시 제공하면 문제가 다시 표시됩니다. 이는 사용자가 경고를 확인한 후 닫고 문제를 완화하는 조치를 취했지만 비슷한 문제를 일으키는 작업을 다시 실행하는 상황을 허용하기 위한 것입니다. 이 시점에 경고 카드가 다시 표시되어야 합니다.
- 노란색 및 빨간색 경고 카드는 180일마다 다시 표시됩니다. 단, 사용자가 여러 번 닫은 경우는 예외입니다.
다음과 같은 경우를 제외하고 소스에는 추가 동작이 필요하지 않습니다.
- 소스에서 이 동작을 다르게 구현하려고 합니다(예: 문제를 다시 표시하지 않음).
- 소스에서 이를 콜백으로 사용하려고 합니다(예: 정보를 기록하기 위해).
여러 사용자/프로필 데이터 제공
SafetyCenterManager
API는 사용자 및 프로필에서 사용할 수 있습니다. 자세한 내용은 여러 사용자 인식 앱 빌드를 참고하세요. SafetyCenterManager
를 제공하는 Context
객체는 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
인스턴스 사용)을 위한 UI 항목이 있습니다. 새로고침 또는 재검색 신호가 프로필 상위 요소 및 연결된 모든 관리 프로필을 위해 전송됩니다. 연결된 수신기가 각 프로필에 대해 시작되고 수신기나 앱이
singleUser
가 아닌 한 교차 프로필 호출을 실행하지 않고도 연결된 데이터를 직접SafetyCenterManager
에 제공할 수 있습니다.소스가 사용자 및 모든 관리 프로필의 데이터를 제공해야 합니다. 각 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
- 내부 안전 센터 API에 사용됩니다.
- PermissionController 및 셸에만 부여됩니다.
설정 앱
안전 센터 리디렉션
기본적으로 안전 센터에는 새로운 보안 및 개인 정보 보호 항목이 있는 설정 앱을 통해 액세스합니다. 다른 설정 앱을 사용하거나 설정 앱을 수정한 경우 안전 센터에 액세스하는 방법을 맞춤설정해야 할 수 있습니다.
안전 센터가 사용 설정된 경우:
- 기존 개인 정보 보호 항목은 숨겨진 코드입니다.
- 기존 보안 항목은 숨겨진 코드입니다.
- 새로운 보안 및 개인 정보 보호 항목은 추가된 코드입니다.
- 새로운 보안 및 개인 정보 보호 항목은 안전 센터 코드로 리디렉션됩니다.
android.settings.PRIVACY_SETTINGS
및android.settings.SECURITY_SETTINGS
인텐트 작업은 안전 센터를 열도록 리디렉션됩니다(코드: security, privacy).
고급 보안 및 개인 정보 보호 페이지
설정 앱에는 기타 보안 설정 및 기타 개인 정보 보호 설정 제목 아래에 추가 설정이 포함되어 있으며 안전 센터에서 사용할 수 있습니다.
고급 보안 코드
고급 개인 정보 보호 코드
Android 14부터 고급 보안 및 고급 개인 정보 보호 설정 페이지가
"com.android.settings.MORE_SECURITY_PRIVACY_SETTINGS"
인텐트 작업이 있는 하나의 '기타 보안 및 개인 정보 보호' 페이지 아래에 병합됩니다.
안전 소스
안전 센터는 설정 앱에서 제공한 특정 안전 소스 세트와 통합됩니다.
- 잠금 화면 안전 소스는 잠금 화면이 비밀번호(또는 다른 보안)로 설정되어 사용자의 개인 정보가 외부 액세스로부터 안전하게 보호되는지 확인합니다.
- 생체 인식 안전 소스(기본적으로 숨겨짐)는 지문이나 얼굴 센서와 통합되도록 표시됩니다.
이러한 안전 센터 소스의 소스 코드에는 Android 코드 검색을 통해 액세스할 수 있습니다. 설정 앱이 수정되지 않은 경우(패키지 이름이나 소스 코드, 잠금 화면과 생체 인식을 처리하는 소스 코드가 변경되지 않음) 이 통합은 즉시 작동합니다. 그 외의 경우 통합뿐 아니라 설정 앱의 패키지 이름과 안전 센터와 통합하는 소스를 변경하기 위해 구성 파일을 변경하는 등 일부 수정이 필요할 수 있습니다. 자세한 내용은 구성 파일 업데이트 및 통합 설정을 참고하세요.
PendingIntent 정보
Android 14 이상에서 기존 설정 앱 안전 센터 통합을 사용한다면 아래 설명된 버그가 수정되었습니다. 이 경우 이 섹션은 읽지 않아도 됩니다.
버그가 없다고 확신하는 경우 설정 앱 config_isSafetyCenterLockScreenPendingIntentFixed
의 XML 불리언 리소스 구성 값을 true
로 설정하여 안전 센터 내 해결 방법을 사용 중지합니다.
PendingIntent 해결 방법
이 버그는 Intent
인스턴스 추가 항목을 사용하여 열 프래그먼트를 결정하는 설정으로 인해 발생합니다. Intent#equals
는 Intent
인스턴스 추가 항목을 고려하지 않으므로 톱니바퀴 메뉴 아이콘의 PendingIntent
인스턴스와 해당 항목이 동일하게 간주되어 동일한 UI로 이동합니다(다른 UI로 이동하도록 되어 있어도 마찬가지임). 이 문제는 요청 코드별로 PendingIntent
인스턴스를 구별하여 QPR 출시에서 수정되었습니다. 또는 Intent#setId
를 사용하여 구별할 수 있습니다.
내부 안전 소스
일부 안전 센터 소스는 내부용이며 PermissionController 모듈 내 PermissionController 시스템 앱에서 구현됩니다. 이러한 소스는 일반 안전 센터 소스처럼 동작하고 특별하게 처리되지 않습니다. 이러한 소스의 코드는 Android 코드 검색을 통해 사용할 수 있습니다.
이는 주로 개인 정보 보호 신호입니다. 예를 들면 다음과 같습니다.
- 접근성
- 사용되지 않는 앱 자동 취소
- 위치 정보 액세스
- 알림 리스너
- 업무 정책 정보