Android에서는 사용자에게 더욱 안전한 환경을 제공할 수 있도록 사용자 개인정보 보호와 플랫폼 보안강화를 위한 변경 작업을 진행하고 있습니다. Android 11(API 레벨 30) 이상을 타깃하는 앱은 기기에 설치된 앱의 필터링된 목록만 확인할 수 있습니다. 필터링된 목록 이외의 앱에 액세스하려면 앱이 Android 매니페스트의 <queries> 요소를 사용하여 직접 상호 작용하기 위해 필요한 앱을 선언해야 합니다. 이 블로그 게시물에서는 이를 위한 몇 가지 모범 사례를 살펴보겠습니다.
앱 쿼리 및 상호 작용(Querying and interacting with apps):
앱을 쿼리하고 앱과 상호 작용하는 방법은 다양합니다.
<manifest package="com.example.game">
<queries>
<package android:name="com.example.store" />
<package android:name="com.example.services" />
</queries>
...
</manifest>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
<manifest package="com.example.suite.enterprise">
<provider android:authorities="com.example.settings.files" />
상호 작용해야 하는 패키지에 대해서만 쿼리하여 데이터를 최소화(data minimization)하는 것이 좋습니다. QUERY_ALL_PACKAGES 또는 똑같이 넓은 <intent> 요소는 이 수준의 정보가 필요한 앱에서만 사용해야 합니다. 새로운 패키지 가시성 정책에는 기기에 설치된 앱의 전체 목록에 대한 액세스를 제어하는 새로운 QUERY_ALL_PACKAGES 권한에 대한 승인 프로세스가 도입되었습니다.
액티비티 플래그(Activity flags):
대부분의 일반적인 사용 사례에서는 앱이 폭넓은 패키지 가시성을 가질 필요가 전혀 없습니다. 많은 상황에서는 startActivity()를 사용해 이 인텐트를 열 수 있는 앱이 없는 경우에 예외를 처리할 수 있습니다.
try {
val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
addCategory(CATEGORY_BROWSABLE)
}
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Snackbar.make(it,"Activity Not Found",Snackbar.LENGTH_LONG).show()
대상에 대한 가시성 없이도 Activity를 시작할 수 있습니다. 다만, 암시적 인텐트(implicit intent)가 사용됨으로, 어떤 앱이 실행될지, 암시적 인텐트를 처리할 수 있는 Activity가 있는지 사전에 확인할 수 없습니다. 대신, Activity가 시작되지 않으면 예외가 발생합니다. 아래와 같은 플래그를 사용하면 이 부분을 개선할 수 있습니다.
플래그를 사용하는 일반적인 예로 앱이 브라우저의 모양과 느낌을 맞춤 설정하도록 허용하는 맞춤 탭(Custom Tabs)을 사용하는 경우를 생각할 수 있습니다. 특정 웹 콘텐츠를 처리할 수 있는 네이티브 앱이 이미 설치되어 있는 경우에는 해당 앱으로 넘어가지만, 그렇지 않은 경우 앱 내에서 맞춤 탭을 이용해 웹 콘텐츠를 표시하도록 구현할 수 있습니다. 간단히 요약하면 이 플래그는 개발자가 폭넓은 패키지 가시성 없이도, 특정 웹 콘텐츠를 처리할 수 있는 네이티브 앱이 있는지 사전에 확인하고, 이 후, 거기에서 원하는 방식으로 처리할 수 있는지 확인하는데 도움이 됩니다.
FLAG_ACTIVITY_REQUIRE_NON_BROWSER
이 플래그는 브라우저(browser)가 아닌 결과로 확인될 경우에만 인텐트를 실행합니다. 그와 같은 결과가 없는 경우 ActivityNotFoundException이 발생합니다. 이 후, 앱의 맞춤 탭에서 해당 URL을 열 수 있습니다.
// The URL should either launch directly in a non-browser app (if it's
// the default), or in the disambiguation dialog.
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
인텐트에 이 플래그가 포함된 경우 startActivity() 호출로 인해 브라우저 앱을 직접 실행하게 되거나, 브라우저 앱만이 유일한 옵션인 상황에서 사용자가 실행할 앱을 고르는 대화상자(disambiguation dialong)가 표시되는 경우라면, ActivityNotFoundException이 발생합니다. 플래그에 대한 자세한 내용은 사용 사례 기반 패키지 가시성 구성(Configuring package visibility based on use cases)을 참조하세요.
공유 시트 맞춤 설정(Customizing a share sheet):
맞춤 공유 시트 대신 시스템 공유 시트(system share sheet)를 사용하는 것이 좋습니다. 앱 가시성(app visibility)을 사용할 필요 없이 시스템 공유 시트를 맞춤 설정할 수 있습니다. 자세한 내용은 이 문서를 참조하세요.
패키지 가시성 디버깅(Debugging Package Visibility):
손쉽게 매니페스트를 확인하면 포함된 모든 쿼리를 볼 수 있습니다. 이 작업을 수행하려면 안드로이드 스튜디오에서 매니페스트 파일을 열고 병합된 매니페스트(Merged Manifest) 탭을 선택하세요.
또한, 패키지 필터링에 대한 로그 메시지를 사용하여 기본 가시성(default visibility)이 앱에 미치는 영향을 확인할 수도 있습니다.
$ adb shell pm log-visibility -enable YOUR_PACKAGE_NAME
다음 단계
패키지 가시성에 대한 자세한 내용은 다음 자료를 확인해 보세요.
그럼 즐겁게 코딩해 보세요!