diff --git a/README.md b/README.md index 6947772..3bee4d9 100644 --- a/README.md +++ b/README.md @@ -49,16 +49,16 @@ Example project: [GitHub](https://github.com/sharmadhiraj/installed_apps/tree/ma List apps = await InstalledApps.getInstalledApps( // Optional: whether to exclude system apps from the list. Default is true. excludeSystemApps: true, - + // Optional: whether to exclude apps that cannot be launched (no launch intent). Default is true. excludeNonLaunchableApps: true, - + // Optional: whether to include app icons in the result. Default is false. withIcon: false, - + // Optional: filter apps whose package names start with this prefix. Default is null (no filtering). packageNamePrefix: "com.example", - + // Optional: filter apps by platform type (Flutter, React Native, etc.). Default is null (no filtering). platformType: PlatformType.flutter, ); @@ -83,6 +83,8 @@ class AppInfo { int installedTimestamp; bool isSystemApp; bool isLaunchableApp; + bool hasMultipleSigners; + List certificateHashes; } ``` diff --git a/android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt b/android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt index d04ea26..52b4dc2 100644 --- a/android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt +++ b/android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt @@ -4,10 +4,13 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.P +import android.util.Base64 import android.util.Log import java.io.File +import java.security.MessageDigest class Util { companion object { @@ -38,6 +41,8 @@ class Util { map["is_system_app"] = isSystemApp(packageManager, packageInfo.packageName) map["is_launchable_app"] = isLaunchableApp(packageManager, packageInfo.packageName) + map["has_multiple_signers"] = hasMultipleSigners(packageManager, packageInfo.packageName) + map["certificate_hashes"] = getCertificateHashes(packageManager, packageInfo.packageName) } } else { map["name"] = "Unknown" @@ -75,5 +80,35 @@ class Util { false } } + + fun hasMultipleSigners(packageManager: PackageManager, packageName: String): Boolean { + return packageManager + .getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES) + .signingInfo + .hasMultipleSigners() + } + + fun getCertificateHashes(packageManager: PackageManager, packageName: String): List { + val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES) + val signingInfo = packageInfo.signingInfo + + // https://developer.android.com/reference/android/content/pm/SigningInfo#getApkContentsSigners() + val signatures = if (signingInfo.hasMultipleSigners()) { + signingInfo.apkContentsSigners + } else { + signingInfo.signingCertificateHistory + } + + val hashes = signatures.map { + signature -> MessageDigest + .getInstance("SHA-256") + .digest(signature.toByteArray()) + .joinToString(":") { + "%02X".format(it) + } + } + + return hashes + } } -} \ No newline at end of file +} diff --git a/lib/app_info.dart b/lib/app_info.dart index 79e127e..49ff3c7 100644 --- a/lib/app_info.dart +++ b/lib/app_info.dart @@ -10,6 +10,8 @@ class AppInfo { final int installedTimestamp; final bool isSystemApp; final bool isLaunchableApp; + final bool hasMultipleSigners; + final List certificateHashes; const AppInfo({ required this.name, @@ -21,6 +23,8 @@ class AppInfo { required this.installedTimestamp, required this.isSystemApp, required this.isLaunchableApp, + required this.hasMultipleSigners, + required this.certificateHashes, }); factory AppInfo.create(dynamic data) { @@ -34,6 +38,8 @@ class AppInfo { installedTimestamp: data["installed_timestamp"] ?? 0, isSystemApp: data["is_system_app"] ?? false, isLaunchableApp: data["is_launchable_app"] ?? true, + hasMultipleSigners: data["has_multiple_signers"] ?? false, + certificateHashes: data["certificate_hashes"].cast() ?? [], ); }