Android - 설치된 앱 리스트 가져오기

JS · 13 Sep 2020

Android에서 PackageManager를 이용하면 디바이스에 설치된 앱 리스트를 가져올 수 있습니다. PackageManager는 디바이스에 설치된 패키지를 관리하며, 패키지에 대한 정보를 찾을 수 있는 Query API를 제공합니다.

다음 API를 사용하면 디바이스에 설치된 Package 리스트를 얻을 수 있습니다.

  • PackageManager.getInstalledPackages()
  • PackageManager.getInstalledApplications()

PackageManager.getInstalledPackages()

getInstalledPackages()는 설치된 앱들의 정보를 PackageInfo 리스트로 리턴합니다. 인자로 flags를 전달합니다. 특별한 플래그를 설정하고 싶지 않다면 0을 전달하면 됩니다. 그럼 기본적으로 설치된 앱 리스트들이 리턴됩니다.

val packageManager = context.packageManager
val packages: List<PackageInfo> = packageManager.getInstalledPackages(0)
for (info: PackageInfo in packages) {
    Log.d("Test", "packageName: ${info.packageName}"
            + ", versionName: ${info.versionName}"
            + ", lastUpdateTime: ${info.lastUpdateTime}"
            + ", targetSdk: ${info.applicationInfo.targetSdkVersion}"
            + ", minSdk: ${info.applicationInfo.minSdkVersion}"
            + ", sourceDir: ${info.applicationInfo.sourceDir}"
            + ", uid: ${info.applicationInfo.uid}"
            + ", label: ${info.applicationInfo.loadLabel(packageManager)}")
}

로그를 확인해보면 다음과 같이 설치된 앱들의 정보들이 출력됩니다.

09-13 15:56:22.717 13291 13323 D Test    : packageName: com.google.android.youtube, versionName: 14.19.57, lastUpdateTime: 1569045019000, targetSdk: 28, minSdk: 21, sourceDir: /system/product/app/YouTube/YouTube.apk, uid: 10132, label: YouTube
....

시스템 앱 리스트만 리턴

설치된 시스템 앱 리스트만 얻고 싶다면, 플래그 인자로 MATCH_SYSTEM_ONLY를 전달하면 됩니다.

val packages: List<PackageInfo> = packageManager.getInstalledPackages(
        PackageManager.MATCH_SYSTEM_ONLY)

삭제된 앱 리스트도 함께 리턴

안드로이드는 멀티 유저 시스템을 지원합니다. 어떤 앱이 현재 실행 중인 User에는 설치되어있지 않고, 다른 User에 설치되어있을 수 있습니다. 이런 경우, 이 앱은 현재 User에는 설치되어있지 않은 상태이기 때문에 getInstalledPackages(0)의 결과로 리턴되지 않습니다.

현재 User에 삭제된 앱들의 정보도 모두 얻고 싶다면, 플래그 인자로 MATCH_UNINSTALLED_PACKAGES를 인자로 전달하면 됩니다.

val packages: List<PackageInfo> = packageManager.getInstalledPackages(
        PackageManager.MATCH_UNINSTALLED_PACKAGES)

비활성화된 앱 리스트도 함께 리턴

어떤 앱은 설치되어있지만 비활성화되어있을 수 있습니다. 비활성화(disabled)되면 실행이 불가능한 상태로, Launcher에도 아이콘이 보이지 않는 상태입니다. 이런 앱들의 정보도 모두 얻고 싶다면, 플래그 인자로 MATCH_DISABLED_COMPONENTS를 전달하면 됩니다.

val packages: List<PackageInfo> = packageManager.getInstalledPackages(
        PackageManager.MATCH_DISABLED_COMPONENTS)

User Locked 시점에 설치된 모든 앱 리스트 가져오기

Android 7.0부터 Direct Boot가 적용되었습니다. Direct Boot가 적용된 디바이스의 경우, 만약 부팅 후 사용자가 Lock을 해제하지 않으면 User는 Locked 상태가 됩니다. 그리고 User Locked 상태일 때는 DIRECT_BOOT_AWARE으로 설정되지 않은 앱은 실행이 불가능한 상태가 됩니다.

이 시점에 getInstalledPackages(0)을 호출하면 DIRECT_BOOT_AWARE으로 설정된 앱 리스트만 리턴됩니다. 만약 설치된 모든 앱 리스트를 얻고 싶다면 PackageManager.MATCH_DIRECT_BOOT_UNAWARE 플래그를 인자로 전달해야 합니다.

하지만 다음과 같이, 하나의 플래그만 전달하기 보다 AWARE와 UNAWARE 두개의 플래그를 모두 전달하도록 구현하시는 것이 좋습니다. (Direct boot에 영향을 받지 않고 모든 패키지를 찾고 싶다는 의도를 보여줄 수 있습니다.)

val flags = PackageManager.MATCH_DIRECT_BOOT_AWARE or PackageManager.MATCH_DIRECT_BOOT_UNAWARE
val packages: List<PackageInfo> = packageManager.getInstalledPackages(flags)

PackageManager.getInstalledApplications()

getInstalledPackages() API는 PackageInfo 리스트를 리턴합니다. PackageInfo는 ApplicationInfo를 갖고 있습니다.

하지만 ApplicationInfo만 필요한 경우, getInstalledApplications() API를 사용할 수도 있습니다. 이 API는 ApplicationInfo 리스트를 리턴합니다.

다음과 같이 구현할 수 있습니다.

val packageManager = context.packageManager
val flag = 0;
val applications: List<ApplicationInfo> = packageManager.getInstalledApplications(flag)
for (info: ApplicationInfo in applications) {
    Log.d("Test", "packageName: ${info.packageName}"
            + ", targetSdk: ${info.targetSdkVersion}"
            + ", minSdk: ${info.minSdkVersion}"
            + ", sourceDir: ${info.sourceDir}"
            + ", uid: ${info.uid}"
            + ", label: ${info.loadLabel(packageManager)}")
}

플래그 설정 방법은 getInstalledPackages()에서 사용한 것과 동일한 방식으로 사용하시면 됩니다.

특정 Package 설치 여부 확인

위의 API들은 모든 패키지 리스트를 리턴하였습니다. 만약 특정 App이 설치되어있는지 확인하고 싶을 때는 getPackageInfo(packageName, flags) API를 사용할 수 있습니다.

다음과 같이 인자로 PackageName을 전달하면 이 앱에 대한 정보를 얻을 수 있습니다. PackageInfo가 리턴된다면 설치된 앱이고, NameNotFoundException가 발생한다면 설치되지 않은 앱입니다.

val packageManager = context.packageManager
val flag = 0;
val packageName = "com.google.android.youtube"
try {
    val info: PackageInfo = packageManager.getPackageInfo(
        packageName, flag
    )
} catch (e: PackageManager.NameNotFoundException) {
    Log.d("Test", "Not found package : $packageName")
}

다음과 같이 getApplicationInfo(packageName, flags)를 사용할 수도 있습니다. 앱이 설치되어있지 않은 경우 NameNotFoundException가 리턴됩니다.

val info: ApplicationInfo = packageManager.getApplicationInfo(
        "com.google.android.youtube", flag)

Package Visibility

Android 11에서 Package Visibility라는 기능이 추가되었습니다. Package Visibility는 Target SDK 30인 앱에게만 적용이 되며, 다른 앱의 정보를 얻으려면 AndroidManifest에 미리 정의를 해야 한다는 것입니다. 그렇지 않으면, 다른 앱의 정보를 얻을 수 없습니다.

만약 다른 앱의 정보를 얻으려고 PackageManager.getInstalledPackages()를 호출했다면, 일부 중요 시스템 앱을 제외한 다른 앱들은 리스트에 포함안됩니다.

Manifest에 다음과 같은 퍼미션을 추가하면 모든 패키지의 정보를 얻을 수 있습니다.

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

더 자세한 정보는 Package Visibility를 확인해주세요.

adb 명령어로 설치된 패키지 정보 확인

adb shell pm list package 명령어를 입력하면 디바이스에 설치된 패키지 이름이 출력됩니다.

$ adb shell pm list package
package:com.android.cts.priv.ctsshim
package:com.google.android.youtube
package:com.android.internal.display.cutout.emulation.corner
package:com.google.android.ext.services
package:com.android.internal.display.cutout.emulation.double
package:com.android.providers.telephony
package:com.android.dynsystem
package:com.google.android.googlequicksearchbox
package:com.android.providers.calendar
package:com.android.providers.media
....

특정 패키지에 대해서 자세한 정보를 알고 싶다면, adb shell dumpsys package [package name]으로 확인할 수 있습니다.

$ adb shell dumpsys package com.google.android.youtube
Packages:
  Package [com.google.android.youtube] (3d92a10):
    userId=10132
    pkg=Package{4e1cc5 com.google.android.youtube}
    codePath=/system/product/app/YouTube
    resourcePath=/system/product/app/YouTube
    legacyNativeLibraryDir=/system/product/app/YouTube/lib
    primaryCpuAbi=x86
    secondaryCpuAbi=null
    versionCode=1419573700 minSdk=21 targetSdk=28
    versionName=14.19.57
    splits=[base]
    apkSigningVersion=3
    applicationInfo=ApplicationInfo{ed73c com.google.android.youtube}
    flags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP KILL_AFTER_RESTORE RESTORE_ANY_VERSION LARGE_HEAP ]
    privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE HAS_DOMAIN_URLS PRODUCT ]
    dataDir=/data/user/0/com.google.android.youtube
    ....
댓글을 보거나 쓰려면 이 버튼을 눌러주세요.
codechachaCopyright ©2019 codechacha