5. Android 9
앱의 Private 영역
다른 앱에서 접근불가
내부 데이터 저장에 적합
Context.getFileDir()
> /data/user/0/<package_name>/files
Context.getCacheDir()
> /data/user/0/<package_name>/cache
내부 저장소(Internal Storage)
9. Improving shared storage
Be3er a3ribution
파일관리가 쉽지 않음
특정 앱이 사용하고 있는 파일 추적 어려움
앱의 삭제 이후에도 남아있는 파일들
앱이 언인스톨 될 때에, 파일이 제거되도록
언인스톨 이후에 유지가 필요한 파일들에 대해서는 따로
처리
10. Protect app data
Improving shared storage
다른 앱들이 우리의 앱의 파일들을 자유롭게 볼 수 있었음
안드로이드 보안 권장 사항
> 민감한 정보/실행파일/클래스파일 외부 저장소에 저장
금지
> 입력 유효성 검사를 시행해야 함
Man-in-the-Disk:a new attack surface for android
apps 포스트(CheckPoint)
Q에서는 다른 앱들이 우리 앱 파일을 볼 수 없도록 하여
앱에 관련된 데이터를 보호
14. Android Q
Scoped Storage Mode
MediaStore
> 추가 권한 없이 사용 가능
> 다른 앱이 저장한 콘텐트를 보기 위해서는,
READ_EXTERNAL_STORAGE 권한 필요
MediaStore이외의 파일들은 기본 파일 선택기
(System File Picker) 통해 접근
외부 저장소 (External Storage)
17. /sdcard 영역 접근 불가
앱에서는 더이상 /sdcard 영역에 직접 접근 불가
직접 접근을 시도할 경우, FileNotFoundException이나 EPERM error 가 발생
Environment.getExternalStorageDirectory() (deprecated)
> /sdcard
해당 구역에 직접 파일 쓰기 불가
Beta 3 까지는
/sdcard/Android/sandbox/<package_name>으로 매핑
구글 기본 파일 관리앱으로 접근이 가능
18. 개별 앱 공간 변경
샌드박스 형식으로 격리(isolated)
추가 권한 없이 읽고 쓰기 가능
파일경로로 직접 접근하는 것, 여전히 가능 (getExternalFilesDirs 등)
Context.getExternalFilesDirs()
> /sdcard/Android/data/<package_name>/files
다른 앱으로 파일 절대경로 전달 불가
ContentUri 활용 필요
19. 공용 저장 공간 변경
기존 외부 저장소의 Public Files 공간이 모두 사라짐
MediaStore나 Storage Access Framework를 이용
사진, 동영상, 음악, 다운로드 공간으로 나누어 관리
다운로드
> 기본 파일 탐색기(System File Picker)를 통해 사용자가 명시적으로 선택한 경우 접근
가능
> 자신이 생성한 파일은 제외
20. File location 권한 접근
앱 삭제시
제거
개별 앱 공간 필요없음 getExternalFilsDir() Y
Media Collections
(Photos, videos,
audio)
READ_EXTERNAL_STORAGE
(다른 앱 파일에 접근할때만)
MediaStore
(or SAF)
N
Downloads
(documents and
ebooks)
필요없음
Storage Access
Framework
(system’s file picker)
N
Summary of ?le access
21. Adapt to scoped mode
앱의 Target을 Android Q로 지정하면 활성화
Q에서 새로 설치하는 경우에만 활성화
임시 opt-out 기능 지원
requestLegacyExternalStorage 플래그
내년 Major Platform Release에서는 target SDK level에
관계없이 모든 앱에 적용 될 예정
24. What belongs?
사용자에게 소속된 파일들을 저장하도록 설계
다른 앱에서 사용 가능
앱이 제거된 이후에도 파일 유지
제거 이후 앱과의 연결은 끊어짐
MediaStore 컬렉션들
MediaStore.Audio
MediaStore.Video
MediaStore.Images
26. Downloads
기존 컬렉션에 포함되지 않는 파일들을 저장하기 위
한
새로운 MediaStore.Downloads 컬렉션
내가 저장한 모든 파일은 항상 접근 가능
다른 앱이 저장한 파일를 선택하기 위해서는
ACTION_OPEN_DOCUMENT를 통해
System Picker를 열어 유저의 Interaction이 필요
27. Contributing
MediaStore 컬렉션에 추가권한 없이 파일 추가가 가능
“Storage” Runtime permission은 다른 앱이 추가한 미디어파일을 볼때에 필요
MediaStore 에 저장한 파일들은 앱의 삭제 이후에도 지속
28.
29.
30.
31. InGuencing directories
새로 추가되는 미디어파일들은 타입에 따라 기본 저장위치 배정
예를 들어, image/* 파일은 Pictures 디렉토리가 기본 저장위치
특정 디렉토리로 저장 위치를 설정 : MediaColumns.RELATIVE_PATH
RELATIVE_PATH/DISPLAY_NAME 변경하여 update()를 호출시 변경가능
RELATIVE_PATH의 최상위 Path는 항상 기본 저장위치를 지정해야 함
그렇지 않을 경우 Exception 발생
java.lang.IllegalArgumentException: Primary directory Pictures not
allowed for content://media/external_primary/audio/media; allowed
directories are [Alarms, Music, Notifications, Podcasts, Ringtones]
32. Q부터는, Volume Name을 이용하여 저장할 storage device 선택 가능
기본 shared storage device : VOLUME_EXTERNAL_PRIMARY
MediaStore.getExternalVolumeNames()를 통해 다른 Device 정보 Get
특정 Volume에 query, insert, update, delete를 하기 위해
MediaStore.*.getContentUri() 메서드 전달
InGuencing devices
33. // Publish audio into a specific directory on specific device
val values = ContentValues().apply {
put(MediaStore.Audio.Media.RELATIVE_PATH,
"Music/My Artist/My Album")
put(MediaStore.Audio.Media.DISPLAY_NAME,
"My Song.mp3")
}
val volumeNames = MediaStore.getExternalVolumeNames(context)
// …
val collection = MediaStore.Audio.Media.getContentUri(volumeName)
val item = resolver.insert(collection, values)
34. Discovery
ContentResolver.query()를 통해 파일 찾기
DATA column deprecated
ContentResolver.openFileDescriptor()로 item read/write
Pending 파일은 기본적으로 숨김처리
MediaStore.setIncludePending()을 사용시 보여짐
다른 앱에서 작업중인 Pending item query도 가능하나, read/write는 불가
35.
36. Editing
다른 앱의 미디어파일들 discover/read 가능
수정하고 싶다면, 유저로부터 Write Access 얻기
Write 권한 없이 update(), delete(), openFileDescriptor()를 사용할 경우,
RecoverableSecurityException이 발생
37.
38. 다른 앱이 만든 파일의 수정 및 제거 시도시,
resolver.update() / delete()
Q Beta 4에서 정상동작 하지 않음
(2019-06-12, QPP4.190502.019, Pixel)
Image/Video/Audio 파일
> RecoverableSecurityException 발생
> actionIntent.send() 호출하였으나
파일에 변화없음
39. Metadata
Q에서는 미디어 파일에 내재되어 있는 location metadata를 보호
위도, 경도와 같은 Location 정보에 접근이 필요할 경우,
ACCESS_MEDIA_LOCATION permission을 앱에 추가
MediaStore내의 LATITUDE와 LONGITUDE 필드 deprecated
대신 ExifInterface 사용
42. Android Kitkat 4.4 이상 버전부터 사용가능
(OPEN_DOCUMENT_TREE는 5.0이상)
공통된 시스템 파일 선택기 사용
일관된 파일 탐색 방식
클라우드 스토리지에 바로 접근 가능
Beyond local
43. 클라이언트 앱 작성
OPEN_DOCUMENT, OPEN_DOCUMENT_TREE
> SAF로 사용자가 선택한 파일 혹인 디렉터리를 읽고 쓸 수 있음
> 선택된 디렉터리 및 파일 접근 권한을 영구적으로 유지 가능
CREATE_DOCUMENT
> SFA로 사용자가 새로운 파일을 만들도록 안내
> 생성된 파일은 자신의 파일로 간주되어, 자유롭게 읽고 쓸수 있음
사용자에게 일관된 경험을 제공하기 위해, 선택기 UI가 시스템 앱
(com.android.ldocumentsui)로 제공
44. private static final int READ_REQUEST_CODE = 44;
public void performDirectorySearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, READ_REQUEST_CODE);
}
OPEN_DOCUMENT_TREE
> SAF로 디렉터리 선택, 접근 권한을 영구적으로 유지 가능
45.
46. public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
for (DocumentFile file : pickedDir.listFiles()) {
Log.d(TAG, "Found file " + file.getName() + file.length());
}
DocumentFile newFile =
pickedDir.createFile("text/plain", "My Novel");
OutputStream out =
getContentResolver().openOutputStream(newFile.getUri());
// …
}
}
47. •Android Q privacy change: Scoped storage
•https://developer.android.com/preview/privacy/scoped-storage
•Storage Access Framework
•https://developer.android.com/guide/topics/providers/document-
provider
•개발자를 위한 안드로이드 Q 정리 / 양찬석
•https://brunch.co.kr/magazine/androidq
•What’s new in Shared Storage / Google IO 2019 Session
•https://youtu.be/3EtBw5s9iRY
Reference