Android11 Preview가 공개되었고, 변경 내용 중에 Package visibility
에 대해서 알아보려고 합니다.
Package visibility
는 디바이스에 설치된 다른 앱들을 찾거나(query), 실행을 제한하는 변경사항입니다.
사실 Android11에서 QUERY_ALL_PACKAGES
퍼미션을 선언하면, 앱은 위의 제약사항들이 무시할 수 있습니다.
따라서, 이 변경 사항은 필요하지 않은 다른 패키지를 찾는 구현을 지양하라는 구글의 권고사항 정도로 생각이 됩니다.
기존에는 다음과 같은 코드를 사용하면 디바이스에 설치된 다른 패키지를 찾거나 실행할 수 있었습니다.
packageManager.resolveActivity(intent, 0)
packageManager.queryIntentActivities(intent, 0)
packageManager.getInstalledPackages()
하지만 Android11에서는 기본적으로 내 앱이 아닌, 다른 패키지를 찾거나 다른 패키지의 액티비티를 실행할 수 없습니다.
만약 내 앱의 동작에 의존적인 패키지가 있다면 AndroidManifest에 <queries>
태그로 필요한 패키지를 정의해야 합니다.
<queries>
태그는 Android Studio 3.6.1 이상의 버전에서, 최신 gradle plugin 버전에서 사용할 수 있습니다.
<queries> 정의가 필요 없는 경우
다음과 같은 경우에는 이 제약사항이 적용되지 않습니다. 즉, <queries>
를 정의하지 않아도 됩니다.
- 내 앱의 패키지나 Component를 찾거나 실행할 때
- 암시적(Implicit) 인텐트를 사용하여 액티비티를 실행할 때
- Media provider 같은 안드로이드 핵심 기능을 처리하는 시스템 패키지를 사용할 때
- 예를 들어, 어떤 앱이 내앱의 Content provider에 어떤 요청을 했을 때, 내 앱은 요청한 앱을 볼 수 있습니다.
<queries>에 패키지 정의가 필요한 경우
패키지 이름으로 어떤 패키지를 찾거나, 어떤 패키지의 서비스를 사용할 때 <queries>
에 패키지 이름을 정의할 수 있습니다.
다음과 같이 정의하면 "com.codechacha.myapplication"
에 관련된 것을 찾거나 실행할 수 있습니다.
<manifest package="com.codechacha.packagevisibility">
<queries>
<package android:name="com.codechacha.myapplication" />
</queries>
...
</manifest>
예를 들어, 다음 코드는 명시적으로 패키지 이름을 설정한 인텐트에 대해서 실행가능한 액티비티를 찾습니다.
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_LAUNCHER)
intent.setPackage("com.codechacha.myapplication")
val result = packageManager.resolveActivity(intent, 0)
Log.d(TAG, "result: $result")
만약 <queries>
를 정의하지 않았다면, 다음과 같이 null이 리턴됩니다.
MainActivity: result: null
만약 <queries>
에 찾고 싶은 패키지를 정의하였다면, 다음과 같이 설정한 패키지의 Component를 찾을 수 있습니다.
MainActivity: result: ResolveInfo{ba068c4 com.codechacha.myapplication/.MainActivity m=0x108000}
<queries>에 Intent 정의가 필요한 경우
패키지 이름을 모르고, 어떤 인텐트와 관련된 패키지를 사용할 때는 <queries>
에 인텐트를 정의하면 됩니다.
그럼, 이 인텐트와 관련된 패키지를 찾거나 실행할 수 있습니다.
다음과 같이 <queries>
에 인텐트를 등록하면 JPEG
를 지원하는 패키지를 찾거나 실행할 수 있습니다.
<manifest package="com.example.game">
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
...
</manifest>
이번에는 위와 같이 앱에 <queries>
를 등록하고, queryIntentActivities()
로 모든 액티비티를 찾아보았습니다.
val intent = Intent(Intent.ACTION_SEND)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.type = "image/jpeg"
val result = packageManager.queryIntentActivities(intent, 0)
Log.d(TAG, "result: $result")
결과는 다음과 같습니다.
MainActivity: result: [ResolveInfo{ba068c4 com.android.bips/.ImagePrintActivity m=0x608000},
ResolveInfo{ed083ad com.android.bluetooth/.opp.BluetoothOppLauncherActivity m=0x608000},
ResolveInfo{bb4f6e2 com.google.android.apps.docs/.shareitem.UploadMenuActivity m=0x608000},
ResolveInfo{4122a73 com.google.android.apps.maps/com.google.android.maps.MapsActivity m=0x608000},
ResolveInfo{c9d4d30 com.google.android.apps.messaging/.ui.conversationlist.ShareIntentActivity m=0x608000},
ResolveInfo{f9eada9 com.google.android.apps.photos/.uploadtoalbum.UploadContentActivity m=0x608000},
ResolveInfo{92f9f2e com.google.android.gm/.ComposeActivityGmailExternal m=0x608000},
ResolveInfo{75afacf com.codechacha.myapplication/.MainActivity m=0x608000}]
결과에는 <queries>
에 추가한 com.codechacha.myapplication
앱이 포함되어 있고, 그 외의 나머지 앱들은 시스템앱인 것을 볼 수 있습니다.
만약 <queries>
를 정의하지 않았다면 시스템 앱들만 결과로 리턴됩니다.
모든 앱을 찾거나 실행하는 방법
PlayStore와 같은 앱은 디바이스에 설치된 모든 패키지를 찾아, 어떤 앱이 설치되어있는지를 알아야 합니다.
앞으로 어떤 앱들이 설치될 지 모르기 때문에 <queries>
에 정의하는 것이 불가능합니다.
이런 경우 QUERY_ALL_PACKAGES
권한을 추가하면 모든 앱을 찾거나 실행할 수 있습니다.
이 퍼미션은 어떤 앱이든 받을 수 있는 Install permission입니다.
사실, 이 권한을 추가하면 이전 버전과 동일하게 동작합니다.
그렇기 때문에 Package visibility
는 구글의 제약이 아닌, 권고사항 정도로 생각이 됩니다.
하지만, 다음 버전에서 이 퍼미션을 모든 앱이 받지 못하게 할 수도 있습니다. 또는 사용자가 설정에서 특정앱에게만 권한을 주도록 변경할 수도 있습니다.
그렇기 때문에 가능하다면 <queries>
를 정의하여 미래를 대비하는 것이 좋을 것 같습니다.
변경사항을 확인해보는 방법
Android Studio 3.6.1
또는 그 이상 버전에서 구현을 해야 합니다.- targetSdkVersion은 R로 설정해야 합니다.
<queries>
를 정의하지 않았을 때 어떻게 동작하는지 봐보세요.- PackageManager의
getInstalledPackages()
또는queryIntentActivities()
를 호출해보세요. 다른 앱들이 찾을 수 없습니다. - 이제
<queries>
를 정의하고 원하는 패키지를 찾을 수 있는지 확인해보세요.
참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력