ด้วยความที่แอนดรอยด์เป็นระบบปฏิบัติการณ์แบบเปิด จึงทำให้การติดตั้งแอปลงในเครื่องสามารถทำได้หลายวิธี และในบางครั้งนักพัฒนาแอปอย่างเราก็ต้องการเช็คว่าแอปถูกติดตั้งมาจากที่ใดหรือติดตั้งด้วยวิธีไหน

เพราะเวลาติดตั้งแอปลงในอุปกรณ์แอนดรอยด์ (ยกเว้นการติดตั้งผ่าน ADB หรือ Recovery Mode) จะต้องติดตั้งผ่านแอปตัวใดตัวหนึ่งเสมอ ซึ่งจะเรียกแอปดังกล่าวว่า Installer และนอกจากนี้ในอุปกรณ์แอนดรอยด์ก็จะมีแอปที่เรียกว่า Package Installer ติดตั้งอยู่ด้วย เพื่อรองรับการติดตั้งผ่าน Sideload

วิธีตรวจสอบ

โดย Package Name จะมีคำสั่งดังกล่าวให้ใช้งานอยู่ โดยที่ Android 11 (API Level 30) จะต้องใช้คำสั่ง getInstallSourceInfo ส่วนเวอร์ชันที่ต่ำกว่านั้นให้ใช้ getInstallerPackageName แทน และกำหนด Package Name ที่ต้องการตรวจสอบได้เลย

val packageManager: PackageManager = /* ... */
val installerPackageName = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    packageManager.getInstallSourceInfo(packageName).installingPackageName
} else {
    packageManager.getInstallerPackageName(packageName)
}

ผลลัพท์ที่ได้หรือ installerPackageName จะเป็นไปตามนี้

  • com.android.vending - ติดตั้งผ่าน Google Play
  • com.huawei.appmarket - ติดตั้งผ่าน Huawei AppGallery
  • com.amazon.venezia - ติดตั้งผ่าน Amazon Appstore
  • com.sec.android.app.samsungapps - ติดตั้งผ่าน Galaxy Store ของ Samsung
  • com.heytap.market - ติดตั้งผ่าน App Market ของ HeyTap
  • com.vivo.appstore - ติดตั้งผ่าน V-Appstore ของ VIVO
  • com.xiaomi.market - ติดตั้งผ่าน Xiaomi Market
  • com.apkpure.aegon - ติดตั้งผ่าน APKPure
  • com.google.android.packageinstaller - ติดตั้งผ่าน Sideload ไม่ว่าจะโหลดไฟล์ APK จาก Google Chrome หรือติดตั้งผ่าน Firebase App Distribution ก็ตาม
  • null - ไม่ทราบแหล่งที่มา อาจจะ Pre-install อยู่ในเครื่องตั้งแต่แรกหรือติดตั้งผ่าน ADB

คำสั่งเพิ่มเติมสำหรับ Android 13 (API Level 33)

เพราะคำสั่ง getInstallSourceInfo ให้ผลลัพธ์ออกมาเป็น InstallSourceInfo จึงทำให้นักพัฒนาสามารถเช็คข้อมูลผ่านคำสั่งอื่น ๆ ในคลาสดังกล่าวนอกเหนือจาก installingPackageName ได้ด้วย และหนึ่งในนั้นก็คือค่าที่เรียกว่า packageSource

// API level 33 only
val packageName: String = /* ... */
val packageManager: PackageManager = /* ... */
val info: InstallSourceInfo = packageManager.getInstallSourceInfo(packageName)

val source: Int = source.packageSource

สำหรับ source จะเป็นค่า Integer ที่ Installer จะกำหนดไว้ในระหว่างทำการติดตั้งแอปให้ (อาจจะไม่ได้กำหนดค่านี้ก็ได้) โดยค่าที่เป็นไปได้จะมีดังนี้

  • PACKAGE_SOURCE_UNSPECIFIED (0) - Installer ไม่ได้กำหนดค่า
  • PACKAGE_SOURCE_OTHER (1) - ไม่สามารถระบุที่มาของ Installer ได้
  • PACKAGE_SOURCE_STORE (2) - ติดตั้งผ่าน Android App Store
  • PACKAGE_SOURCE_LOCAL_FILE (3) - ติดตั้งจากไฟล์ในเครื่อง เช่น ติดตั้งผ่าน File Manager
  • PACKAGE_SOURCE_DOWNLOADED_FILE (4) - ติดตั้งจากไฟล์ที่ได้มาจากการดาวน์โหลด ในกรณีที่ File Manager รู้ว่าไฟล์ได้มาจากการดาวน์โหลดก็จะได้เป็นค่านี้แทน

และจากการทดสอบบน Google Pixel 4 (Android 13) ได้ผลลัพธ์ดังนี้

  • ติดตั้งผ่าน Google Play ได้เป็น PACKAGE_SOURCE_UNSPECIFIED
  • ติดตั้งผ่าน Google Chrome ได้เป็น PACKAGE_SOURCE_DOWNLOADED_FILE
  • ติดตั้งผ่าน Firebase App Distribution ได้เป็น PACKAGE_SOURCE_LOCAL_FILE
  • ติดตั้งผ่าน ADB ได้เป็น PACKAGE_SOURCE_UNSPECIFIED
  • ติดมากับเครื่องตั้งแต่แรก ได้เป็น PACKAGE_SOURCE_UNSPECIFIED

จะเห็นว่าการติดตั้งผ่าน Google Play ที่ควรจะได้ PACKAGE_SOURCE_STORE แต่กลับได้ PACKAGE_SOURCE_UNSPECIFIED แทน