발단
Google PlayStore 의 최근 요구 사항에 따라 ( https://developer.android.com/distribute/best-practices/develop/target-sdk?hl=ko ) 최근 App targetSdkVersion 을 30으로 올리는 작업을 진행했습니다.
2021년 8월부터 신규 앱은 다음 요건을 충족해야 합니다.
...
* API 수준 30(Android 11) 이상을 타겟팅하고 동작 변경사항에 맞게 조정합니다.
...
진행하다 보니, 평소에 잘 동작하던 queryIntentActivities 가 제대로 동작하지 않았습니다.
개요
PackageManager.queryIntentActivities 는 보통 암시적 Intent ,(이를 테면, URL 을 가지고 Web Browser 로 넘기기전에 적당한 Intent ), 를 실행하는 App 을 선택하기 위해, 주로 사용하는 API 였습니다.
안드로이드 예전 버전에서 startActivity 를 실행했을 때, 생길 수 있는 선택창을 없애기 위한 방법 중 하나였습니다.
이를 테면, 브라우저 앱이 크롬, 파이어폭스, 제조사 브라우저 등 여러개 설치되어 있다고 할 때,
URL - ACTION_VIEW Intent 로 startActivity 를 실행하기 전, 선택가능한 Activity 중 하나를 코드상에서 선택하게 함으로,
사용자에게 어떤 브라우저를 사용할 것인지 확인을 받는 동선을 없애버려서, 좀 더 편하게 사용하게 하려는 의도이기도 합니다.
혹은 startActivity 를 사용할 때, ActivityNotFoundException 을 회피하기 위해 사용하기도 했습니다.
아래 코드 처럼 try-catch 로 처리하거나, Intent.resolveActivity 를 사용하는 방법도 있긴하지만,
queryIntentActivities() 결과 값 크기로 구분하기도 했습니다.
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
}
이 API 를 잘 이용해서, action = MAIN, category = LAUNCHER 로 인텐트 필터로 사용하면, 앱 관리자 같은 앱을 만들 수도 있습니다.
좀 더 깊숙히
하지만, targetSdkVersion = 30 부터는 이런 동작이 쉽지 않게 되었습니다.
예를 들어, 아래와 같이 jpeg image 를 보내는 intent 를 만들었다고 가정하면,...
fun Context.queryIntentActivitiesTest() {
val jpegIntent = Intent(Intent.ACTION_SEND).apply {
this.setDataAndType(Uri.parse("file://a"), "image/jpeg")
}
val query = this.packageManager.queryIntentActivities(jpegIntent, 0)
val system = query.filter { it.activityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
val other = query.filter { it.activityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
Log.d("Test", "jpegIntent queryIntentActivities = total, SYSTEM, NON-SYSTEM " +
"${query.size}, ${system.size}, ${other.size}"
)
}
이 결과는 아래 표와 같이 나옵니다.
Total | System | Other | |
targetSdkVersion = 29 | 42 | 14 | 28 |
targetSdkVersion = 30 | 15 | 7 | 8 |
즉, targetSdkVersion 을 변경한 것으로 queryIntentActivities 로 반환된 결과 값의 차이가 생기고,
이는 실행할 수 있는 Activity 의 갯수가 줄어든다는 것입니다.
물론, 단순히 startActivity 만 사용하는 코드에서는 전혀 문제가 없지만, 위에서 언급했던 것처럼, 특정 App 이 직접 실행되도록 하는 코드를 작성할 경우에는 targetSdkVersion 만 변경한 것으로도 이전과 다른 동작을 하게 될 수가 있습니다.
구글이 targetSdkVersion = 30 에서 이런 제한을 둔 것은 보안상의 이유라고 보여집니다.
위에서도 언급했지만, queryIntentActivities() 를 잘 이용하면, <앱 관리자> 유형의 앱을 개발할 수 있습니다.
즉, 이 API 를 이용하면, 사용자 단말에 설치되어 있는 앱 목록을 알 수 있다는 것이고, 이를 다른 용도로 앱이 이용할 가능성이 있기 때문에, API 이용에 제한을 두기 시작했다는 것으로 볼 수 있습니다.
해결방법
만약, targetSdkVersion = 29 일 때와 동일하게 동작하게 하려면,
아래와 같이 AndroidManifest.xml 파일에 아래와 같이 <queries></queries> 를 추가해주어야 합니다.
<manifest>
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
<application>
</application>
</manifest>
혹은 아래와 같은 permission 을 추가해 주어야 합니다. 하지만, 이 방법은 권장?, 추천?하지 않습니다.
<manifest>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
</application>
</manifest>
왜냐하면, <앱관리자> 같은 특정한 성격의 앱이 아니고서는 플레이스토어 심사를 통과하지 못 할 가능성이 높기 때문입니다.
( https://support.google.com/googleplay/android-developer/answer/10158779?hl=ko )
위 문서에는 아래와 같이 언급되어 있습니다.
앱이 QUERY_ALL_PACKAGES 권한의 허용되는 용도에 관한 요구사항을 충족할 경우, Play Console의 권한 선언 양식을 사용해 이 권한 및 위험성이 높은 기타 모든 권한을 선언해야 합니다.
정책 요구사항을 충족하지 못하거나 권한 선언 양식을 제출하지 않으면 앱이 Google Play에서 삭제될 수 있습니다.
중요: 앱에서 제한된 권한을 사용하는 방식을 변경하려면 정확하게 업데이트된 정보로 요청을 수정해야 합니다. 이러한 권한을 사기성 및 선언되지 않은 용도로 사용하면 앱이 정지되거나 개발자 계정이 해지될 수 있습니다.
참고한 문서
1. Google Play의 타겟 API 수준 요구사항 충족하기
- https://developer.android.com/distribute/best-practices/develop/target-sdk?hl=ko
2. 패키지 공개 상태 관리
- https://developer.android.com/training/basics/intents/package-visibility
3. 폭넓은 패키지(앱) 가시성 (QUERY_ALL_PACKAGES) 권한 사용
- https://support.google.com/googleplay/android-developer/answer/10158779?hl=ko
#Android,#PackageManager,#targetSdkVersion=30,#queryIntentActivities
'Android' 카테고리의 다른 글
Firebase Authentication ‘Code:10, message:10’ 에러 해결 (0) | 2023.04.04 |
---|---|
Gson - Android Proguard 문제 (0) | 2020.11.12 |
Android Studio - Database Inspector (0) | 2020.11.04 |
Android AlertDialog Style 변경 (0) | 2020.08.05 |
Android Custom Lint (0) | 2020.07.28 |