이번 포스팅은 Android Build 오류에 대하여 알아보도록 하겠습니다.
< Error >
android.os.FileUriExposedException: file:///storage/emulated/0/Download/viewData.pdf exposed beyond app through ClipData.Item.getUri()
< Solution>
1. Android 7.0 이상부터 파일 공유 시 API 정책 변경
2. file:// URI 의 직접적인 노출이 아니라 content://URI 로 보내고 이에 대해서 임시 Access 권한을 부여하는 방식으로 변경이 되었습니다.
아래는 Google Android OS 7.0 공식 문서 입니다.
(링크 : https://developer.android.com/about/versions/nougat/android-7.0-changes.html#accessibility)
파일 시스템 권한 변경
개인 파일의 보안을 강화하기 위해, Android 7.0 이상을 대상으로 하는 앱의 개인 디렉터리는 액세스가 제한됩니다(0700
). 이 설정은 크기 또는 존재 여부와 같은 개인 파일의 메타데이터 유출을 막아줍니다. 이러한 권한 변경은 여러 가지 부작용이 있습니다.
▶ 소유자가 개인 파일의 파일 권한을 더 이상 완화해서는 안 되며, MODE_WORLD_READABLE
및/또는 MODE_WORLD_WRITEABLE
을 사용하여 권한을 완화하려고 시도하면 SecurityException
이 트리거됩니다.
★ 참고: 아직까지는 이 제한이 완전히 적용되지 않습니다. 앱이 여전히 기본 API 또는 File
API를 사용하여 개인 디렉터리에 대한 권한을 수정할 수도 있습니다. 하지만 개인 디렉터리에 대한 권한은 부득이한 경우가 아니라면 완화하지 않는 것이 좋습니다.
▶ 패키지 도메인 외부에서 file://
URI를 전달하면 수신기가 액세스 불가능한 경로로 남아 있을 수 있습니다. 따라서 file://
URI를 전달하려고 시도하면 FileUriExposedException
이 트리거됩니다. 개인 파일의 내용을 공유하기 위해 권장되는 방법은 FileProvider
를 사용하는 것입니다.
▶ DownloadManager
는 비공개로 저장된 파일을 더 이상 파일 이름별로 공유할 수 없습니다. 레거시 애플리케이션은 COLUMN_LOCAL_FILENAME
에 액세스할 때 액세스가 불가능한 경로가 될 수 있습니다. Android 7.0 이상을 대상으로 하는 앱은 COLUMN_LOCAL_FILENAME
에 액세스할 때 SecurityException
을 트리거합니다. DownloadManager.Request.setDestinationInExternalFilesDir()
또는 DownloadManager.Request.setDestinationInExternalPublicDir()
을 사용하여 다운로드위치를 공용 위치로 설정하는 레거시 애플리케이션은 COLUMN_LOCAL_FILENAME
에 있는 경로에 여전히 액세스할 수 있지만, 이 메서드는 부득이한 경우가 아니라면 사용하지 않는 것이 좋습니다. DownloadManager
에 의해 노출되는 파일에 액세스하는 좋은 방법은 ContentResolver.openFileDescriptor()
를 사용하는 것입니다.
Android 7.0을 대상으로 하는 앱의 경우, Android 프레임워크는 앱 외부에서 file://
URI의 노출을 금지하는 StrictMode
API 정책을 적용합니다. 파일 URI를 포함하는 인텐트가 앱을 떠나면 FileUriExposedException
예외와 함께 앱에 오류가 발생합니다.
content://
URI를 보내고 이 URI에 대해 임시 액세스 권한을 부여해야 합니다. 이 권한을 가장 쉽게 부여하는 방법은 FileProvider
클래스를 사용하는 방법입니다. 권한과 파일 공유에 대한 자세한 내용은 파일 공유를 참조하세요.3. AndroidMenifext.xml를 수정하여 fileprovider를 지정합니다.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:permission="android.permission.MANAGE_DOCUMENTS"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_path" />
</provider>
4. res/xml/file_provider_path.xml 파일을 생성하여 아래와 같이 파일 경로를 지정합니다.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<files-path
name="files"
path="." />
</paths>