https://stackoverflow.com/questions/62782648/android-11-scoped-storage-permissions
Android 11 Scoped storage permissions
My App use the file paths of images provided by Environment.getExternalStorageDirectory() to create albums of photos, but with Android 11 I won't be able to access directly files. According to the
stackoverflow.com
해당 포스트를 참조했습니다.
23.12.27 수정.
MANAGE_EXTERNAL_STORAGE는 강력한 권한이므로 구글에서 리젝시킵니다.
ㄴ 관련 내용은 하단을 참고해주세요.
이제 Target SDK 33으로 맞춰야 업데이트 및 신규 앱 게시가 가능합니다.
관련 내용 : https://support.google.com/googleplay/android-developer/answer/11926878?hl=ko#zippy=
Google Play 앱의 대상 API 수준 요구사항 - Play Console 고객센터
도움이 되었나요? 어떻게 하면 개선할 수 있을까요? 예아니요
support.google.com
https://developer.android.com/about/versions/13/behavior-changes-all?hl=ko
동작 변경사항: 모든 앱 | Android 개발자 | Android Developers
모든 앱에 영향을 주는 Android 13의 변경사항을 알아봅니다.
developer.android.com
올해 초 구글 스토어 정책이 바뀌어 TargetSDK 가 31 이상이여야 업데이트 및 게시를 할 수 있게 되었다. (하지만 올해 지나면 TargetSDK는 33으로 바뀔 예정)
업데이트 할 앱은 안드로이드 어플에서 자체적으로 기능이 있으면서 유니티가 삽입되어 유니티 실행 버튼을 누르면 유니티가 실행된다.
관련 내용
1. https://developer.android.com/google/play/requirements/target-sdk?hl=ko
Google Play의 대상 API 수준 요구사항 충족하기 | Android Developers
Google Play의 대상 API 수준 요구사항 충족하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. APK를 업로드하는 경우 Google Play의 대상 API 수준 요구사항을 충족
developer.android.com
2. https://support.google.com/googleplay/android-developer/answer/11926878?hl=ko
Google Play 앱의 대상 API 수준 요구사항 - Play Console 고객센터
도움이 되었나요? 어떻게 하면 개선할 수 있을까요? 예아니요
support.google.com
그래서 앱을 업데이트 해야하는데 TargetSDK를 31로 바꿔야했다.
TargetSDK 30에서 31로 이전하면서 발생한 문제에 대해 읊어본다면
1. Using memoryaddresses from more than 16GB of memory
이거는 메모리 참조에 대한 유니티 버그이므로 유니티 버전을 바꿔야 한다.
https://forum.unity.com/threads/android-11-arm64-native-heap-allocator-issues.1047170/
Android 11 ARM64 native heap allocator issues
Hi, In Android 11 release notes, there was a change to native heap alloactor...
forum.unity.com
2. 2023.06.21 - [Develop/Android] - [Android] Build-tool 31.0.0 is missing DX.bat
[Android] Build-tool 31.0.0 is missing DX.bat
안드로이드 SDK 31로 타깃 및 컴파일로 지정하여 빌드 할 때 해당 오류가 발생할 수 있다. Android Studio의 SDK 경로에서 build-tools\31.0.0\ 폴더에서 DX.bat 파일이 없다는 것이다. 해당 폴더에서 D8.bat을 컨
redasol.tistory.com
3. 2023.06.21 - [Develop/Android] - [Android] An exception has occurred in the compiler (1.8.0_202)
[Android] An exception has occurred in the compiler (1.8.0_202)
이전 글에서 이어집니다. 안드로이드 SDK를 30으로 하다가 31로 바꾸고 빌드하려고 하면 JDK 버전 문제를 일으킨다. 31버전부터는 JDK 11을 사용하므로 오라클 JDK 홈페이지에서 다운로드하면 된다. ht
redasol.tistory.com
4. [현재] 유니티를 실행했는데 안드로이드 앱에서 넘겨준 Intent 를 받고나서 반응이 없는 현상 - (해결!) 알고보니 저장소 권한 문제였다, 시스템 로그로는 아무것도 안 나와서 많이 헤멧다.
관련 내용 - https://developer.android.com/training/data-storage/manage-all-files?hl=ko
저장소 기기에 있는 모든 파일 관리 | Android 개발자 | Android Developers
DataStore는 로컬 데이터를 저장하는 최신 방법을 제공합니다. SharedPreferences 대신 DataStore를 사용해야 합니다. 자세한 내용은 DataStore 가이드를 참고하세요. 저장소 기기에 있는 모든 파일 관리 컬렉
developer.android.com
결론부터 말하자면 두 가지를 해야한다.
1. startService 를 ForeGround 로 변경
2. 모든 파일에 대한 접근 허용을 뜻하는 MANAGE_EXTERNAL_STORAGE 권한 추가 - 안드로이드 앱의 매니페스트, 유니티 내의 안드로이드 매니페스트에 추가, 유니티에서 필요로하는 권한은 안드로이드 앱 매니페스트에도 작성이 되어 있어야 한다.
수정 : MANAGE_EXTERNAL_STORAGE는 강력한 권한이므로 구글에서 리젝시킨다.
이 권한을 이용하려면 파일탐색기 어플같이 권한이 꼭 필요한 어플이어야 한다.
android 11이라면 임시로 인텐트에 권한을 부여하는 방법을 사용하거나 : https://redasol.tistory.com/28
[Android, Unity] 안드로이드 앱이 외부 앱에서 자신의 개별 저장소에 임시로 접근할 수 있도록 하기
다음 포스트들을 참조했습니다. https://developer.android.com/training/data-storage?hl=ko 데이터 및 파일 저장소 개요 | Android 개발자 | Android Developers DataStore는 로컬 데이터를 저장하는 최신 방법을 제공합니
redasol.tistory.com
android 13이라면 13에 맞는 저장소 권한을 얻어야 한다.
https://als2019.tistory.com/111
Android 13 - 2편 (android 13 이상을 타겟팅하는 앱)
앱이 Android 13 이상을 타겟팅한다면 이러한 동작을 올바르게 지원하도록 앱을 수정해야 합니다. Privacy 알림 권한이 포그라운드 서비스 모양에 영향을 줌 사용자가 알림 권한을 거부하는 경우 FGS(
als2019.tistory.com
이 포스트의 미디어 권한을 다 선언해야 한다.
즉 만약 android 9부터 13까지 어우르는 앱이라면 권한 선언할 때 사용자의 android OS 버전을 확인하고 해당 버전에 맞는 저장소 권한을 요청하면 된다.
android 12는 삽입된 앱에 대한 저장소 권한에 버그가 있다.
원본 앱에서 삽입된 앱을 실행시킬 때 저장소 권한을 얻어야 삽입된 앱이 실행된다.
이미 권한을 얻은 상태에서는 삽입된 앱이 실행되지 않는 버그가 있으므로 android 12 이용자라면 OS 업데이트 안내를 하거나 앱 설정으로 보내 저장소 권한을 전부 거부로 설정 후 다시 실행해서 저장소 권한을 다시 얻는 과정을 진행해 달라는 안내를 해야한다.
두 번째 방법은 매번 실행시 진행해야하므로 매우매우 사용자 경험이 나쁘다.
1번은 안드로이드 오레오 즉 SDK26부터 해야하므로 startService가 있는 장소에서 다음과 같이 작성한다.
근데 OS11이 되면서 강제되었다.
서비스 이름이 만약 DownloadService 이라면
if (serviceIntent == null) {
serviceIntent = new Intent(context, DownloadService .class);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
만약 bindService를 했다면
Intent intent = new Intent(context, DownloadService .class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
}
else {
context.startService(intent);
}
context.bindService(intent, conn, Context.BIND_AUTO_CREATE);
startForegroundService 로 서비스에 Foreground를 하라고 메세지를 보내고
그 후 서비스에서 Notification을 작성해서 startForeground를 수행하는데 Notification 작성 인자에 PendingIntent를 추가해야한다.
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
그리고 startForeground 로 서비스를 시작한다.
startForeground(NOTIFICATION_ID, notification.setNotification(this, "타이틀", "내용", icon));
이제 MANAGE_EXTERNAL_STORAGE 권한 추가를 보자.
매니페스트에 WRITE_EXTERNAL_STORAGE 권한이 있을텐데 이 권한은 레거시 권한이 되어 OS가 낮은 단말기에서만 발동한다.
따라서 매니페스트 권한은 다음과 같이 작성 및 변경한다.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
그리고 권한 요청을 위한 퍼미션은 다음과 같이 지정한다.

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
permissions = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE,
};
}
else
{
permissions = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BLUETOOTH, // 최신 안드로이드 버전에서 사용가능하도록 블루투스 추가
Manifest.permission.BLUETOOTH_ADMIN,
};
그 후 권한 요청할 때 모든 파일에 대한 접근 허용 을 사용자에게 전달하여 허용을 하도록 하기 위해 권한 요청하는 곳에 다음과 같이 작성한다.

boolean MANAGE_EXTERNAL_STORAGE_b = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !MANAGE_EXTERNAL_STORAGE_b) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",getApplicationContext().getPackageName())));
startActivityForResult(intent, MULTIPLE_PERMISSIONS);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, MULTIPLE_PERMISSIONS);
}
if(Environment.isExternalStorageManager())
{
MANAGE_EXTERNAL_STORAGE_b = true;
}
}
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R && !MANAGE_EXTERNAL_STORAGE_b)
{
MANAGE_EXTERNAL_STORAGE_b = true;
}
boolean 변수인 MANAGE_EXTERNAL_STORAGE_b 는 전역 변수로 선언했다.
MANAGE_EXTERNAL_STORAGE 권한을 요청하면 다음과 같은 화면으로 이동하고 권한을 요청한 앱을 표시하고 사용자가 직접 허용하도록 작동한다. ( 빨간 박스가 권한을 요청한 앱 )

사용자가 앱을 선택하고 뒤로가기 버튼을 눌러 앱으로 돌아오면 Environment.isExternalStorageManager() 로 해당 권한이 허용됐는지 확인할 수 있다.
MANAGE_EXTERNAL_STORAGE_b 변수는 이 권한이 없어도 되는 단말기를 위해 추가하여 관리하기 위함이다.
이렇게 하면 SDK 31을 타겟으로 빌드하면 저장소를 접근해서 읽고 쓰기를 할 수 있게 된다.
'Develop > Android' 카테고리의 다른 글
[Android] context.getresources().getidentifier() 가 뭔가요? (0) | 2023.10.27 |
---|---|
[Android] 인앱 결제 시스템 만들기 (PBL v6, 비소비성 결제) (1) | 2023.10.25 |
[Android] 안드로이드 알림창 만들고 특정 문장에 색 넣기 (0) | 2023.09.19 |
[Android, Unity] 안드로이드 앱이 외부 앱에서 자신의 개별 저장소에 임시로 접근할 수 있도록 하기 (FileProvider, 임시 권한 주기) (0) | 2023.07.05 |
[Android] REQUEST_INSTALL_PACKAGES 권한 요청하기 (0) | 2023.06.23 |