Android 11의 저장소 업데이트

Android 11에서는 플랫폼을 더욱 강화하여 외부 저장소의 앱 및 사용자 데이터를 보다 안전하게 보호합니다. 미리보기 출시에서는 지난해 Android Dev Summit에서 발표한 미디어의 원시 파일 경로 액세스 선택, 미디어의 일괄 수정 작업, 저장소 액세스 프레임워크의 업데이트된 UI 등 여러 개선사항이 도입되었습니다.

이 플랫폼에는 개발자가 범위 지정 저장소를 사용하도록 쉽게 전환하기 위한 개선사항이 추가로 도입되었습니다. 앱의 사용 사례에 기반하여 범위 지정 저장소를 사용하도록 앱을 이전하는 방법에 관한 자세한 내용은 이 페이지의 범위 지정 저장소 섹션, Android 저장소 사용 사례 및 권장사항 가이드, Android의 최신 사용자 저장소라는 매체 도움말을 참조하세요.

언제나 그렇듯 다음 버전의 Android를 만드는 데 도움이 되도록 의견을 보내주시기 바랍니다. Issue Tracker를 사용하여 의견을 보내면 됩니다.

Google은 항상 소중한 의견에 귀 기울이고 있습니다. 이 간단한 설문조사를 통해 기능 사용 방식을 알려주세요. 특히 이 기능의 영향을 받은 사용 사례에 관해 알려주세요.

범위 지정 저장소 적용

개발자에게 테스트 시간을 추가로 주기 위해 Android 10(API 수준 29)을 타겟팅하는 앱은 여전히 requestLegacyExternalStorage 속성을 요청할 수 있습니다. 이 플래그를 사용하면 앱은 범위 지정 저장소와 관련된 변경사항(예: 여러 디렉터리 및 여러 미디어 파일 유형에 관한 액세스 권한 부여)을 일시적으로 선택 해제할 수 있습니다. Android 11을 타겟팅하도록 앱을 업데이트하면 시스템에서 requestLegacyExternalStorage 플래그를 무시합니다.

Android 10과 호환성 유지

앱이 Android 10 기기에서 실행될 때 범위 지정 저장소를 선택 해제하는 경우 앱의 매니페스트 파일에서 requestLegacyExternalStoragetrue로 계속 설정하는 것이 좋습니다. 이렇게 하면 Android 10을 실행하는 기기에서 앱이 예상대로 계속 동작할 수 있습니다.

범위 지정 저장소 사용 시 표시되는 디렉터리로 데이터 이전

앱이 레거시 저장소 모델을 사용하고 이전에 Android 10 이하를 타겟팅했다면 범위 지정 저장소 모델이 사용 설정되었을 때 앱이 액세스할 수 없는 디렉터리에 데이터를 저장할 수 있습니다. Android 11을 타겟팅하기 전에 먼저 범위 지정 저장소와 호환되는 디렉터리로 데이터를 이전합니다. 대부분의 상황에서 데이터를 앱별 디렉터리로 이전할 수 있습니다.

이전할 데이터가 있다면 사용자가 Android 11을 타겟팅하는 앱의 새 버전으로 업그레이드할 때 레거시 저장소 모델을 보존할 수 있습니다. 이렇게 하면 사용자가 앱에서 이전에 데이터를 저장했던 디렉터리에 저장된 앱 데이터에 액세스할 수 있는 권한을 유지할 수 있습니다. 업그레이드에 레거시 저장소 모델을 사용하려면 앱의 매니페스트에서 preserveLegacyExternalStorage 속성을 true로 설정합니다.

참고: 대부분의 앱에서는 preserveLegacyExternalStorage를 사용할 필요가 없습니다. 이 플래그는 앱 데이터를 범위 지정 저장소와 호환되는 위치로 이전하며 사용자가 앱을 업데이트할 때 데이터 관련 액세스 권한을 유지하도록 하려는 상황을 위해서만 설계됩니다. 이 플래그를 사용하면 사용자가 앱을 업데이트할 때 레거시 저장소 모델이 계속 사용되기 때문에 범위 지정 저장소가 앱 사용자에게 어떤 영향을 주는지 테스트하기가 더 어려워집니다.

preserveLegacyExternalStorage를 사용한다면 레거시 저장소 모델은 사용자가 앱을 제거할 때까지만 유효합니다. 사용자가 Android 11을 실행하는 기기에 앱을 설치 또는 재설치하면 앱이 preserveLegacyExternalStorage의 값과 상관없이 범위 지정 저장소 모델을 선택 해제할 수 없습니다.

범위 지정 저장소 테스트

앱의 타겟 SDK 버전 및 매니페스트 플래그 값과 관계없이 앱의 범위 지정 저장소를 사용 설정하려면 다음 앱 호환성 플래그를 사용 설정하세요.

범위 지정 저장소를 사용 중지하고 대신 레거시 저장소 모델을 사용하려면 두 플래그를 모두 설정 해제하세요.

기기 저장용량 관리

Android 11에서 범위 지정 저장소 모델을 사용하는 앱은 자체 앱별 캐시 파일에만 액세스할 수 있습니다. 앱에서 기기 저장용량을 관리해야 한다면 다음을 실행하세요.

  1. ACTION_MANAGE_STORAGE 인텐트 작업을 호출하여 여유 공간을 확인합니다.
  2. 기기에 여유 공간이 충분하지 않다면 앱에서 모든 캐시를 지우는 데 동의하라는 메시지를 사용자에게 표시합니다. 메시지를 표시하려면 ACTION_CLEAR_APP_CACHE 인텐트 작업을 호출합니다.

외부 저장소의 앱별 디렉터리

Android 11에서 앱은 외부 저장소에 자체 앱별 디렉터리를 만들 수 없습니다. 시스템에서 앱에 제공하는 디렉터리에 액세스하려면 getExternalFilesDirs()를 호출합니다.

미디어 파일 액세스

사용자 개인정보 보호를 유지하면서도 더 간편하게 미디어에 액세스하도록 Android 11은 다음 기능을 추가합니다.

일괄 작업 실행

기기 간 일관성과 사용자 편의성 강화를 위해 Android 11은 MediaStore API에 몇 가지 메서드를 추가합니다. 이러한 메서드는 특정 미디어 파일 수정(예: 사진을 직접 편집)에 간소화된 흐름을 사용하려는 앱에 특히 유용합니다.

추가된 메서드는 다음과 같습니다.

createWriteRequest()
앱에 지정된 미디어 파일 그룹에 관한 쓰기 액세스 권한을 부여하도록 사용자에게 요청합니다.
createFavoriteRequest()
지정된 미디어 파일을 기기의 '즐겨찾기' 미디어로 표시하도록 사용자에게 요청합니다. 이 파일에 읽기 액세스할 수 있는 앱은 사용자가 파일을 '즐겨찾기'로 표시했음을 알 수 있습니다.
createTrashRequest()

지정된 미디어 파일을 기기의 휴지통에 넣도록 사용자에게 요청합니다. 휴지통에 있는 항목은 시스템에서 정의한 기간이 지나면 영구적으로 삭제됩니다.

createDeleteRequest()

지정된 미디어 파일을 휴지통에 미리 넣지 않고 영구적으로 삭제하도록 사용자에게 요청합니다.

이러한 메서드 중 하나를 호출하면 시스템에서 PendingIntent 객체가 빌드됩니다. 앱이 이 인텐트를 호출하면 앱에서 지정된 미디어 파일을 업데이트하거나 삭제하도록 동의를 요청하는 대화상자가 사용자에게 표시됩니다.

예를 들어 다음은 createWriteRequest() 호출을 구조화하는 방법입니다.

Kotlin

val urisToModify = /* A collection of content URIs to modify. */
val editPendingIntent = MediaStore.createWriteRequest(contentResolver,
        urisToModify)

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,
    null, 0, 0, 0)

자바

List<Uri> urisToModify = /* A collection of content URIs to modify. */
PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver,
                  urisToModify);

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.getIntentSender(),
    EDIT_REQUEST_CODE, null, 0, 0, 0);

사용자의 응답을 평가하고 계속 진행하거나 사용자가 동의하지 않으면 앱에 권한이 필요한 이유를 사용자에게 설명합니다.

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int,
                 data: Intent?) {
    ...
    when (requestCode) {
        EDIT_REQUEST_CODE ->
            if (resultCode == Activity.RESULT_OK) {
                /* Edit request granted; proceed. */
            } else {
                /* Edit request not granted; explain to the user. */
            }
    }
}

자바

@Override
protected void onActivityResult(int requestCode, int resultCode,
                   @Nullable Intent data) {
    ...
    if (requestCode == EDIT_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            /* Edit request granted; proceed. */
        } else {
            /* Edit request not granted; explain to the user. */
        }
    }
}

createFavoriteRequest(), createTrashRequest(), createDeleteRequest()와 동일한 일반 패턴을 사용할 수 있습니다.

직접 파일 경로 및 네이티브 라이브러리를 사용하여 파일에 액세스

앱이 타사 미디어 라이브러리와 더 원활하게 호환될 수 있도록 Android 11에서는 MediaStore API 이외의 API를 사용하여 공유 저장소의 미디어 파일에 액세스할 수 있습니다. 다음 API 중 하나를 사용하여 미디어 파일에 직접 액세스할 수도 있습니다.

  • File API
  • 네이티브 라이브러리(예: fopen())

앱에 저장소 권한이 없으면 직접 파일 경로를 사용하여 앱에 속하는 미디어 파일에 액세스할 수 있습니다. 앱에 READ_EXTERNAL_STORAGE 권한이 있으면 파일이 앱에 속하는지 여부와 관계없이 직접 파일 경로를 사용하여 모든 미디어 파일에 액세스할 수 있습니다.

미디어 파일에 직접 액세스하는 경우 앱의 매니페스트 파일에서 requestLegacyExternalStoragetrue로 설정하여 범위 지정 저장소를 선택 해제하는 것이 좋습니다. 이렇게 하면 Android 10을 실행하는 기기에서 앱이 예상대로 동작합니다.

성능

직접 파일 경로를 사용하여 미디어 파일의 순차적 읽기를 실행하면 성능은 MediaStore API의 성능과 비슷합니다.

그러나 직접 파일 경로를 사용하여 미디어 파일의 임의 읽기 및 쓰기를 실행하면 프로세스 속도가 최대 두 배까지 느려질 수 있습니다. 이러한 상황에서는 대신 MediaStore API를 사용하는 것이 좋습니다.

미디어 저장소에서 사용 가능한 값

기존 미디어 파일에 액세스할 때 로직에서 DATA 열의 값을 사용할 수 있습니다. 이 값에 유효한 파일 경로가 있기 때문입니다. 그러나 파일을 항상 사용할 수 있다고 가정하지 마세요. 발생할 수 있는 모든 파일 기반 I/O 오류를 처리할 준비를 합니다.

한편 미디어 파일을 만들거나 업데이트하려면 DATA 열의 값을 사용하지 마세요. 대신 DISPLAY_NAMERELATIVE_PATH 열의 값을 사용합니다.

다른 앱의 비공개 디렉터리에 액세스

변경 세부정보

변경 이름: APP_DATA_DIRECTORY_ISOLATION

변경 ID: 143937733

전환하는 방법

앱과 Android 11의 호환성을 테스트할 때 다음 ADB 명령어를 사용하여 이 변경사항을 사용 설정하거나 사용 중지할 수 있습니다.

adb shell am compat enable (143937733|APP_DATA_DIRECTORY_ISOLATION) PACKAGE_NAME
adb shell am compat disable (143937733|APP_DATA_DIRECTORY_ISOLATION) PACKAGE_NAME

호환성 프레임워크 및 변경사항 전환에 관한 자세한 내용은 Android 11과 앱의 호환성 테스트를 참조하세요.

Android 11에서 앱은 외부 저장소 내 다른 모든 앱의 전용 앱별 디렉터리의 파일에 더 이상 액세스할 수 없습니다.

문서 액세스 제한

개발자에게 테스트할 시간을 주기 위해 저장소 액세스 프레임워크(SAF)와 관련된 다음 변경사항은 Android 11을 타겟팅하는 앱에만 적용됩니다.

디렉터리에 액세스

더 이상 ACTION_OPEN_DOCUMENT_TREE 인텐트 작업을 사용하여 다음 디렉터리에 관한 액세스 권한을 요청할 수 없습니다.

  • 내부 저장소 볼륨의 루트 디렉터리
  • 기기 제조업체가 신뢰할 수 있다고 생각하는 각 SD 카드 볼륨의 루트 디렉터리(카드가 에뮬레이션되었거나 삭제 가능한지 여부와 관계없음) 신뢰할 수 있는 볼륨은 앱이 대부분의 경우 성공적으로 액세스할 수 있는 볼륨입니다.
  • Download 디렉터리

파일에 액세스

더 이상 ACTION_OPEN_DOCUMENT_TREE 또는 ACTION_OPEN_DOCUMENT 인텐트 작업을 사용하여 다음 디렉터리에서 개별 파일을 선택하도록 사용자에게 요청할 수 없습니다.

  • Android/data/ 디렉터리 및 모든 하위 디렉터리
  • Android/obb/ 디렉터리 및 모든 하위 디렉터리

변경사항 테스트

이 동작 변경사항을 테스트하려면 다음 단계를 따르세요.

  1. ACTION_OPEN_DOCUMENT 작업을 사용하여 인텐트를 호출합니다. Android/data/Android/obb/ 디렉터리가 모두 나타나지 않는지 확인합니다.
  2. 다음 중 한 가지 방법을 사용합니다.
  3. ACTION_OPEN_DOCUMENT_TREE 작업을 사용하여 인텐트를 호출합니다. Download 디렉터리가 표시되고 디렉터리와 연결된 작업 버튼이 비활성화되는지 확인합니다.

권한

Android 11은 저장소 권한과 관련된 다음과 같은 변경사항을 도입합니다.

임의의 버전 타겟팅

첫 번째 대화상자에는 설정의 '허용'이라는 링크가 표시됩니다.
그림 1. 앱에서 범위 지정 저장소를 사용하고 READ_EXTERNAL_STORAGE 권한을 요청할 때 표시되는 대화상자

다음 변경사항은 앱의 타겟 SDK 버전에 관계없이 Android 11에서 적용됩니다.

  • 저장소 런타임 권한이 파일 및 미디어로 이름이 바뀝니다.
  • 범위 지정 저장소를 선택 해제하지 않은 앱이 READ_EXTERNAL_STORAGE 권한을 요청하는 경우 Android 10과 다른 대화상자가 사용자에게 표시됩니다. 이 대화상자는 그림 1과 같이 앱에서 사진과 미디어에 액세스할 수 있는 권한을 요청하고 있다는 것을 나타냅니다.

    사용자는 시스템 설정에서 READ_EXTERNAL_STORAGE 권한이 있는 앱을 확인할 수 있습니다. 설정 > 개인정보 보호 > 권한 관리자 > 파일 및 미디어 페이지에서 권한이 있는 각 앱이 모든 파일에 허용 아래에 나열됩니다.

    참고: 앱이 Android 11을 타겟팅하면 '모든 파일'의 이 액세스 권한은 읽기 전용입니다. 이 앱을 사용하여 공유 저장공간의 모든 파일을 읽고 쓰려면 모든 파일 액세스 권한이 있어야 합니다.

Android 11 타겟팅

앱이 Android 11을 타겟팅하는 경우 WRITE_EXTERNAL_STORAGE 권한 및 WRITE_MEDIA_STORAGE 독점 권한은 더 이상 추가 액세스를 제공하지 않습니다.

Android 10(API 수준 29) 이상을 실행하는 기기에서 앱은 저장소 관련 권한을 요청하지 않고도 MediaStore.Downloads와 같은 잘 정의된 미디어 컬렉션에 기여할 수 있습니다. 앱에서 미디어 파일로 작업할 때 필요한 권한만 요청하는 방법을 자세히 알아보세요.

모든 파일 액세스

공유 저장소 액세스가 필요한 대다수 앱은 Storage Access Framework 또는 MediaStore API와 같은 범위 지정 저장소 권장사항을 준수할 수 있습니다. 그러나 일부 앱에는 기기 파일에 광범위한 액세스가 필요하지만 개인정보를 안전하게 보호하는 저장소 권장사항을 사용하여 효율적으로 작업을 실행할 수 없는 핵심 사용 사례가 있습니다.

예를 들어 바이러스 백신 앱의 기본 사용 사례에서는 여러 디렉터리에서 많은 파일을 정기적으로 검사해야 할 수 있습니다. 이 검사에서 시스템 파일 선택기를 사용하여 디렉터리를 선택하는 데 사용자 상호작용이 반복적으로 필요하다면 사용자 환경이 저하될 수 있습니다. 파일 관리자 앱, 백업 및 복원 앱, 문서를 만들고 수정하는 생산성 앱과 같은 다른 사용 사례에도 이와 유사한 상황이 발생할 수 있습니다.

앱은 다음을 실행하여 사용자에게 모든 파일 액세스라는 특수 앱 액세스를 요청할 수 있습니다.

  1. 매니페스트에서 MANAGE_EXTERNAL_STORAGE 권한을 선언합니다.
  2. ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION 인텐트 작업을 사용하여 앱에 모든 파일 관리 액세스 허용 옵션을 사용 설정할 수 있는 시스템 설정 페이지로 사용자를 안내합니다.

앱에 MANAGE_EXTERNAL_STORAGE 권한이 부여되었는지 확인하려면 Environment.isExternalStorageManager()를 호출합니다.

MANAGE_EXTERNAL_STORAGE 권한은 다음을 부여합니다.

  • 공유 저장소 내 모든 파일 읽기 및 쓰기 액세스

  • MediaStore.Files 테이블 콘텐츠 액세스

  • 이동 중(OTG) USB 드라이브와 SD 카드 모두의 루트 디렉터리 액세스

  • 모든 내부 저장소 디렉터리⁠쓰기 액세스. 단, /Android/data/, /sdcard/Android 및 대부분의 /sdcard/Android 하위 디렉터리는 제외됩니다. 이 쓰기 액세스에는 파일 경로 액세스가 포함됩니다.

    이 권한이 부여된 앱은 다른 앱에 속하는 앱별 디렉터리에 여전히 액세스할 수 없습니다. 이러한 디렉터리는 저장소 볼륨에서 Android/data/의 하위 디렉터리로 표시되기 때문입니다.

앱에 MANAGE_EXTERNAL_STORAGE 권한이 있으면 MediaStore API나 파일 경로를 사용하여 이러한 추가 파일과 디렉터리에 액세스할 수 있습니다. 그러나 저장소 액세스 프레임워크를 사용할 때 MANAGE_EXTERNAL_STORAGE 권한이 없으면 파일이나 디렉터리에만 액세스할 수 있습니다.