Android11 - Package visibility 변경사항 소개

JS · 05 Apr 2020

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>를 정의하고 원하는 패키지를 찾을 수 있는지 확인해보세요.

참고