본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] FileProvider 에 알아보자

by 돼지왕 왕돼지 2021. 2. 16.
반응형

 

-

FileProvider 는 ContentProvider 의 subclass 로 secure 한 file share 를 관장한다.

이를 통하면 file:/// 형태의 uri 대신 content:// 형태의 uri 를 사용하게 된다.

 

 

-

content URI 는 read, write access 를 임시 permission 으로 부여할 수 있다.

content uri 를 가진 Intent 를 통해 타앱에 정보를 보낼 때에도 Intent.setFlags() 를 통해 permission 을 부여할 수 있다.

이 permission 은 전달받는 Activity 가 stack 에 존재할 때 가능하다.

Intent 가 Service 로 전달될 때에는 해당 Service 가 running 하는 동안만 부여된 권한이 유지된다.

 

 

-

file:/// uri 를 사용할 때에는 file system 에서 해당 permission 을 바꾸어야 한다.

이렇게 되면 모든 app 에 permission 을 주는 것과 같고, 이를 원복시킬 때까지 누구든 이 파일에 접근할 수 있기 때문에 보안상 좋은 방법이 아니다.

(그래서 file provider 라는 녀석이 나왔다.)

 

 

 

Defining a FileProvider

 

-

FileProvider 의 기본 기능은 file 에 대해 content URI 를 생성하는 것이다.

이는 FileProvider 를 app 의 manifest 에 xml 형태로 포함시키면 된다.

android:name 에는 androidx.core.content.FileProvider 를 사용하면 된다.

android:authorities 에는 <youtDomain>.fileprovider 의 형태로 주면 된다.

android:exported 는 false 로 준다. public 일 필요가 없다.

android:grantUriPermissions 에는 true 를 준다. 그래야 temp 권한을 획득할 수 있다.

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.mydomain.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            ...
        </provider>
        ...
    </application>
</manifest>

 

 

-

FileProvider 의 기본 기능을 확장하고 싶다면, FileProvider 를 상속하고 android:name 에 fully qualified name 을 적어주면 된다.

 

 

 

Specifying Available Files

 

-

FileProvider 는 미리 설정한 폴더에 있는 file 들에 대해서만 URI 를 생성할 수 있다.

폴더 설정은 storage area 와 path 를 xml 에 적어주는 것으로 가능하다. 이는 <paths> element 를 통해 한다.

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_images" path="images/"/>
    ...
</paths>

name attribute 는 Uri path segment 로 실제 path 를 hide 하는 효과를 지닌다.

path attribute 는 실제 path 를 명시해준다. 이 녀석은 directory 이지 특정 파일이 아니다.

 

위 xml 은 private file 영역(Context.getFilesDir())의 images/ 라는 디렉토리에 한정이 된다.

paths 태그 안의 files-path 는 복수개가 올 수 있다.

 

 

-

<files-path …> 는 Context.getFilesDir() 에 해당되는 영역을 말한다.

<cache-path …> 는 Context.getCacheDir() 에 해당되는 영역을 말한다.

<external-path …> 는 Environment.getExternalStorageDirectory() 에 해당되는 영역을 말한다.

<external-files-path …> 는 Context.getExternalFilesDir(String) 에 해당되는 영역이다.

<external-cache-path …> 는 Context.getExternalCacheDir() 에 해당되는 영역이다.

<external-media-path …> 는 Context.getExternalMediaDirs() 에 해당되는 영역이다. 이건 API 21+ 단말에만 유효하다.

 

 

-

이렇게 정의한 file path 를 FileProvider 의 meta-data 에 넣어준다.

android:name 은 android.support.FILE_PROVIDER_PATHS 로 정의해주고,

android:resource 에는 정의한 xml 파일을 연결해준다.

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

 

 

 

Generating the Content URI for a File

 

-

다른 앱과 content URI 를 통해 file 을 share 하기 위해서는, app 은 content URI 를 생성해야만 한다.

먼저 공유하려는 File 객체를 만든다. 그리고 FileProvider.getUriForFile() 에 File 을 넘기고 Uri 를 받는다. 이 Uri 가 content URI 이다.

이 녀석을 Intent 에 실어서 보내면 된다.

File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);

return 된 uri 는 content://authority/pathName/fileName 의 형태가 된다.

 

-

이 FileProvider 를 통한 contentUri 를 받은 앱에서는 ContentResolver.openFileDescriptor 를 통해 ParcelFileDescriptor 를 받아올 수 있다.

 

 

 

Granting Temporary Permissions to a URI

 

-

getUriForFile() 을 통해 얻어진 contentURI 에 접근 권한을 부여하기 위해서는 다음 중 하나를 만족하면 된다.

 

1. Context.grantUriPermission(package, Uri, mode_flags) 를 호출한다.

이 임시 접근 권한은 package 를 한정시킬 수 있다.

flag 에는 FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION 둘 중 하나 혹은 둘 다 들어갈 수 있다.

이 permission 은 revokeUriPermission() 이 호출되거나 단말이 reboot 되기 전까지 유효하다.

 

2. Intent 에 setData 를 통해 uri 를 설정하고, Intent.setFlags() 를 통해 권한을 부여한다.

권한에는 FLAG_GRANT_READ_URI_PERMISSION 이나 FLAG_GRANT_WRITE_URI_PERMISSION 둘 중 하나 혹은 둘 다 넣어준다.

Intent 의 전달은 많은 경우 setResult() 를 통해 한다.

Intent 를 통해 전달된 권한 획득은 받는 Activity 가 stack 에 active 로 있는 동안 유효하다.

stack 이 종료되면, permission 은 자동으로 제거된다.

permission grant 된 activity 가 다른 component 를 호출하여 해당 URI 를 넘기는 경우 자동으로 권한이 승계된다.

 

 

 

Serving a Content URI to Another App

 

-

file 에 대한 content URI 를 다른 앱에 제공하는 경우는 여러 가지가 있다.

일반적인 방법은 중 하나는 startActivityForResult() 를 호출하여, FileProvider 로 생성된 URI 를 담은 intent 를 setResult() 를 통해 받는 것이다.

 

 

-

contentURI 를 ClipData 에 넣고, 이를 Intent 를 통해 전달하는 방법도 있다.

Intent.setClipData() API 가 활용되며, 이 경우 여러개의 ClipData 를 Intent 에 넣을 수 있다는 장점이 있다.

Intent.setFlags() 를 호출할 때 모든 content URI 에 권한이 적용된다.

( Intent.setClipData 는 API 16 부터 제공된다. )

 

 

cf)

https://developer.android.com/reference/android/content/Intent.html#setClipData(android.content.ClipData)

 

Intent  |  Android 개발자  |  Android Developers

 

developer.android.com

 

setClipData(ClipData clip)

 

-

ClipData 는 Intent 에 들어가는 정보이며, intent matching 등에는 쓰이지 않는다.

이 녀석은 의미적으로는 extra 와 같다고 보면 된다.

extra 대신 이 녀석을 쓰는 주된 이유는 FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION 이 clip data 에 들어있는 uri 와 호환되기 때문이다.

여러개의 content:// scheme 을 가진 uri 를 공유할 때 유효하다.

 

 

-

만약 ClipData 가 포함하고 있는 정보 자체가 Intent 라면, 그곳에 정의된 grant flag 는 무시된다.

오직 top-level 의 main intent flag  만이 적용된다.

 

 

-

ClipData 안에 있는 MIME type, label, icon 은 Intent 에 의해 직접적으로 사용되지 않는다.

App 판단시 일반적으로 main intent 자체의 MIME type 에 의존하며, ClipData 의 정보를 참조하지 않는다.

일반적인 관례는, ClipData 를 사용하는 경우 MIME type 을 "*/*" 를 사용한다.

 

 

 

cf2)

https://developer.android.com/reference/android/content/ClipData

 

ClipData  |  Android 개발자  |  Android Developers

 

developer.android.com

 

ClipData

 

-

Parcelable 하며, clipboard 의 clipped data 를 나타낸다.

ClipData 는 1개 이상의 item 을 가지고 있는 복잡한 type 이며, 각각은 data item 을 가지고 있다.

user 에게 보여주기 위해 label 도 가지고 있다.

 

 

-

ClipData 는 ClipDescription 이라는 것을 가지고 있는데, 이는 clip 에 대한 meta data 를 가지고 있다.

getDescription, getMimeType(int) 은 반드시 올바른 MIME type 을 전달해야 한다.

제대로 된 MIME type 을 가진 clip 을 만들기 위해 newPlainText(CharSequence, CharSequence), newUri(ContentResolver, CharSequence, Uri), newIntent(ChrarSequence, Intent) 를 사용하는 것이 추천된다.

 

각각의 item 은 다음 3개의 data class 중 하나에 속한다.

simple text CharSequence, single Intent object, Uri

 

 

 

참고자료

 

https://developer.android.com/reference/android/support/v4/content/FileProviderdeveloper.android.com/reference/androidx/core/content/FileProvider

 

FileProvider  |  Android 개발자  |  Android Developers

From class android.content.ContentProvider ContentProviderResult[] applyBatch(ArrayList arg0) void attachInfo(Context arg0, ProviderInfo arg1) int bulkInsert(Uri arg0, ContentValues[] arg1) Bundle call(String arg0, String arg1, Bundle arg2) Uri canonicaliz

developer.android.com

 

 

반응형

댓글