Android 默认短信应用查询技术文档1. 需求背景开发一个功能用于获取 Android 系统中当前默认短信应用的包名并查询所有具备成为默认短信应用资格的应用列表。2. 初始实现2.1 基础方法fungetDefaultSmsAppPackageName(context:Context):String?{returnTelephony.Sms.getDefaultSmsPackage(context)}这是 Android 官方提供的 API用于获取当前默认短信应用的包名。3. 遇到的问题3.1 Java 版本兼容性问题问题Android Gradle Plugin 8.5.1 requires Java 17 to run. You are currently using Java 11.解决方案降级 Android Gradle Plugin 到 7.4.2支持 Java 11或在gradle.properties中设置org.gradle.java.home指向 Java 17 的路径3.2 PackagingOptions DSL 兼容性问题Unresolved reference: packagingAGP 7.4.2 不支持新的packaging {}DSL 语法。解决方案// 旧版本语法packagingOptions{resources{excludes/META-INF/{AL2.0,LGPL2.1}}}3.3 Settings.Secure 返回 null问题From Settings.Secure: null From Telephony.Sms: com.android.mmsSettings.Secure 中的sms_default_application键返回 null但 Telephony.Sms API 能返回值。原因某些 ROM 可能没有正确保存设置到 Settings.Secure或者使用了不同的存储机制解决方案同时使用两种方式优先使用 Settings.Secure 的值fungetDefaultSmsAppPackageName(context:Context):String?{valfromSettingsSettings.Secure.getString(context.contentResolver,sms_default_application)valfromTelephonyTelephony.Sms.getDefaultSmsPackage(context)returnfromSettings?.takeIf{it.isNotEmpty()}?:fromTelephony}3.4 切换默认应用后值不更新问题使用remember { mutableStateOf(...) }缓存了初始值切换默认应用后显示的还是旧值。解决方案使用remember(key)方式当 key 改变时重新获取varrefreshCounterbyremember{mutableIntStateOf(0)}valdefaultSmsPackageremember(refreshCounter){SmsUtils.getDefaultSmsAppPackageName(context)}3.5 无法查询到所有短信应用 ⭐核心问题问题系统设置中有 2 个短信应用短信、测试短信但只能查询到 1 个。具备资格的应用: - com.vivo.easyshare (互传) - com.android.mms (信息)缺少com.test.mms。原因分析通过dumpsys package可以看到这些应用确实注册了sms:schemesms: 1b41c51 com.test.mms/...ComposeSmsActivity 1189e3d com.android.mms/.ui.ComposeMessageActivity 8e016f1 com.vivo.easyshare/.activity.DefaultSmsActivity但queryIntentActivities无法查询到它们。根本原因缺少 QUERY_ALL_PACKAGES 权限在 Android 11API 30 中引入了包可见性过滤。默认情况下应用只能查询到以下内容自己通过queries声明的应用具有相同签名的应用解决方案在AndroidManifest.xml中添加uses-permissionandroid:nameandroid.permission.QUERY_ALL_PACKAGEStools:ignoreQueryAllPackagesPermission/这个权限是特殊权限不需要用户授权但需要在 Google Play 审核时提供正当理由。3.6 Intent 查询方式的限制尝试了多种 Intent 查询方式// 方式1: 使用 ACTION_VIEW sms: schemeIntent(Intent.ACTION_VIEW).apply{dataUri.parse(sms:)addCategory(Intent.CATEGORY_DEFAULT)}// 方式2: 使用 ACTION_SENDTO sms: schemeIntent(Intent.ACTION_SENDTO).apply{dataUri.parse(sms:)addCategory(Intent.CATEGORY_DEFAULT)}// 方式3: 不指定 action只用 schemeIntent().apply{dataUri.parse(sms:)addCategory(Intent.CATEGORY_DEFAULT)}即使添加了各种 category 组合仍然无法查询到所有应用。这说明某些应用的 intent-filter 配置可能比较特殊或者使用了非标准的注册方式。4. 最终实现4.1 AndroidManifest.xml?xml version1.0 encodingutf-8?manifestxmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:toolshttp://schemas.android.com/tools!-- 查询所有应用的权限 (Android 11) --uses-permissionandroid:nameandroid.permission.QUERY_ALL_PACKAGEStools:ignoreQueryAllPackagesPermission/application.../application/manifest4.2 SmsUtils.ktobjectSmsUtils{privateconstvalTAGSmsUtils/** * 获取当前默认短信应用的包名 */fungetDefaultSmsAppPackageName(context:Context):String?{// 方式1: Settings.SecurevalfromSettingsSettings.Secure.getString(context.contentResolver,sms_default_application)Log.d(TAG,From Settings.Secure:$fromSettings)// 方式2: Telephony.Sms APIvalfromTelephonyTelephony.Sms.getDefaultSmsPackage(context)Log.d(TAG,From Telephony.Sms:$fromTelephony)// 优先使用 Settings.Secure 的值valresultfromSettings?.takeIf{it.isNotEmpty()}?:fromTelephony Log.d(TAG,Final result:$result)returnresult}/** * 获取所有具备资格成为默认短信应用的应用列表 */fungetEligibleSmsApps(context:Context):ListAppInfo{valappsmutableListOfAppInfo()valpackageManagercontext.packageManagervalseenPackagesmutableSetOfString()try{valintentslistOf(Intent(Intent.ACTION_VIEW).apply{dataUri.parse(sms:)addCategory(Intent.CATEGORY_DEFAULT)addCategory(Intent.CATEGORY_BROWSABLE)},Intent(Intent.ACTION_SENDTO).apply{dataUri.parse(sms:)addCategory(Intent.CATEGORY_DEFAULT)})for(intentinintents){valresolveInfospackageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY)for(resolveInfoinresolveInfos){valpackageNameresolveInfo.activityInfo.packageNameif(!seenPackages.contains(packageName)){seenPackages.add(packageName)valappNameresolveInfo.loadLabel(packageManager).toString()apps.add(AppInfo(packageName,appName))}}}}catch(e:Exception){Log.e(TAG,Error:${e.message})}returnapps.sortedBy{it.name}}/** * 打开系统默认短信应用设置页面 */funopenDefaultSmsAppSettings(context:Context){try{valintentIntent(android.settings.DEFAULT_APP_SETTINGS)context.startActivity(intent)}catch(e:Exception){valintentIntent(android.settings.SETTINGS)context.startActivity(intent)}}dataclassAppInfo(valpackageName:String,valname:String)}4.3 MainActivity.ktComposablefunSmsInfoScreen(modifier:ModifierModifier){valcontextLocalContext.currentvarrefreshCounterbyremember{mutableIntStateOf(0)}varshowDiagnosisbyremember{mutableStateOf(false)}valdefaultSmsPackageremember(refreshCounter){SmsUtils.getDefaultSmsAppPackageName(context)}valisDefaultAppremember(refreshCounter){SmsUtils.isDefaultSmsApp(context)}valdiagnosisremember(refreshCounter){SmsUtils.diagnoseDefaultSmsApp(context)}Column(modifiermodifier.fillMaxSize().padding(16.dp)){Text(默认短信应用:${defaultSmsPackage?:未设置})Row{Button(onClick{SmsUtils.openDefaultSmsAppSettings(context)}){Text(打开设置)}Button(onClick{refreshCounter}){Text(刷新)}Button(onClick{showDiagnosis!showDiagnosis}){Text(if(showDiagnosis)隐藏诊断else显示诊断)}}if(showDiagnosis){Text(diagnosis,styleMaterialTheme.typography.bodySmall)}}}5. 关键注意事项5.1 QUERY_ALL_PACKAGES 权限作用允许应用查询设备上所有已安装的应用适用版本Android 11 (API 30) 及以上授权方式特殊权限不需要用户运行时授权Google Play 政策使用此权限需要提供正当的使用理由5.2 默认短信应用的条件一个应用要成为默认短信应用必须满足必需权限SEND_SMSRECEIVE_SMSREAD_SMSRECEIVE_MMSRECEIVE_WAP_PUSH必需组件BroadcastReceiver 处理SMS_DELIVERactionBroadcastReceiver 处理WAP_PUSH_DELIVERactionService 处理RESPOND_VIA_MESSAGEactionIntent Filter处理ACTION_SENDTO与sms:或smsto:scheme5.3 调试命令# 查看所有处理短信的应用adb shell dumpsys package|grep-A5sms:# 查看指定应用的权限adb shell dumpsys packagepackage_name|grep-ipermission6. 总结Android 11 的包可见性限制是需要特别注意的问题QUERY_ALL_PACKAGES权限是解决查询不到应用的关键Settings.Secure和Telephony.Sms两种方式获取默认应用各有优缺点建议结合使用remember的使用方式会影响数据的实时更新需要配合状态管理