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?{valfromSettings=Settings.Secure.getString(context.contentResolver,"sms_default_application")valfromTelephony=Telephony.Sms.getDefaultSmsPackage(context)returnfromSettings?.takeIf{it.isNotEmpty()}?:fromTelephony}3.4 切换默认应用后值不更新
问题:
使用remember { mutableStateOf(...) }缓存了初始值,切换默认应用后显示的还是旧值。
解决方案:
使用remember(key)方式,当 key 改变时重新获取:
varrefreshCounterbyremember{mutableIntStateOf(0)}valdefaultSmsPackage=remember(refreshCounter){SmsUtils.getDefaultSmsAppPackageName(context)}3.5 无法查询到所有短信应用 ⭐核心问题
问题:
系统设置中有 2 个短信应用(短信、测试短信),但只能查询到 1 个。
具备资格的应用: - com.vivo.easyshare (互传) - com.android.mms (信息)缺少com.test.mms。
原因分析:
通过dumpsys package可以看到这些应用确实注册了sms:scheme:
sms: 1b41c51 com.test.mms/...ComposeSmsActivity 1189e3d com.android.mms/.ui.ComposeMessageActivity 8e016f1 com.vivo.easyshare/.activity.DefaultSmsActivity但queryIntentActivities无法查询到它们。
根本原因:缺少 QUERY_ALL_PACKAGES 权限
在 Android 11(API 30)+ 中,引入了包可见性过滤。默认情况下,应用只能查询到以下内容:
- 自己
- 通过
<queries>声明的应用 - 具有相同签名的应用
解决方案:
在AndroidManifest.xml中添加:
<uses-permissionandroid:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission"/>这个权限是特殊权限,不需要用户授权,但需要在 Google Play 审核时提供正当理由。
3.6 Intent 查询方式的限制
尝试了多种 Intent 查询方式:
// 方式1: 使用 ACTION_VIEW + sms: schemeIntent(Intent.ACTION_VIEW).apply{data=Uri.parse("sms:")addCategory(Intent.CATEGORY_DEFAULT)}// 方式2: 使用 ACTION_SENDTO + sms: schemeIntent(Intent.ACTION_SENDTO).apply{data=Uri.parse("sms:")addCategory(Intent.CATEGORY_DEFAULT)}// 方式3: 不指定 action,只用 schemeIntent().apply{data=Uri.parse("sms:")addCategory(Intent.CATEGORY_DEFAULT)}即使添加了各种 category 组合,仍然无法查询到所有应用。
这说明某些应用的 intent-filter 配置可能比较特殊,或者使用了非标准的注册方式。
4. 最终实现
4.1 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><!-- 查询所有应用的权限 (Android 11+) --><uses-permissionandroid:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission"/><application...></application></manifest>4.2 SmsUtils.kt
objectSmsUtils{privateconstvalTAG="SmsUtils"/** * 获取当前默认短信应用的包名 */fungetDefaultSmsAppPackageName(context:Context):String?{// 方式1: Settings.SecurevalfromSettings=Settings.Secure.getString(context.contentResolver,"sms_default_application")Log.d(TAG,"From Settings.Secure:$fromSettings")// 方式2: Telephony.Sms APIvalfromTelephony=Telephony.Sms.getDefaultSmsPackage(context)Log.d(TAG,"From Telephony.Sms:$fromTelephony")// 优先使用 Settings.Secure 的值valresult=fromSettings?.takeIf{it.isNotEmpty()}?:fromTelephony Log.d(TAG,"Final result:$result")returnresult}/** * 获取所有具备资格成为默认短信应用的应用列表 */fungetEligibleSmsApps(context:Context):List<AppInfo>{valapps=mutableListOf<AppInfo>()valpackageManager=context.packageManagervalseenPackages=mutableSetOf<String>()try{valintents=listOf(Intent(Intent.ACTION_VIEW).apply{data=Uri.parse("sms:")addCategory(Intent.CATEGORY_DEFAULT)addCategory(Intent.CATEGORY_BROWSABLE)},Intent(Intent.ACTION_SENDTO).apply{data=Uri.parse("sms:")addCategory(Intent.CATEGORY_DEFAULT)})for(intentinintents){valresolveInfos=packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY)for(resolveInfoinresolveInfos){valpackageName=resolveInfo.activityInfo.packageNameif(!seenPackages.contains(packageName)){seenPackages.add(packageName)valappName=resolveInfo.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{valintent=Intent("android.settings.DEFAULT_APP_SETTINGS")context.startActivity(intent)}catch(e:Exception){valintent=Intent("android.settings.SETTINGS")context.startActivity(intent)}}dataclassAppInfo(valpackageName:String,valname:String)}4.3 MainActivity.kt
@ComposablefunSmsInfoScreen(modifier:Modifier=Modifier){valcontext=LocalContext.currentvarrefreshCounterbyremember{mutableIntStateOf(0)}varshowDiagnosisbyremember{mutableStateOf(false)}valdefaultSmsPackage=remember(refreshCounter){SmsUtils.getDefaultSmsAppPackageName(context)}valisDefaultApp=remember(refreshCounter){SmsUtils.isDefaultSmsApp(context)}valdiagnosis=remember(refreshCounter){SmsUtils.diagnoseDefaultSmsApp(context)}Column(modifier=modifier.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,style=MaterialTheme.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_DELIVERaction - BroadcastReceiver 处理
WAP_PUSH_DELIVERaction - Service 处理
RESPOND_VIA_MESSAGEaction
- BroadcastReceiver 处理
Intent Filter:
- 处理
ACTION_SENDTO与sms:或smsto:scheme
- 处理
5.3 调试命令
# 查看所有处理短信的应用adb shell dumpsys package|grep-A5"sms:"# 查看指定应用的权限adb shell dumpsys package<package_name>|grep-ipermission6. 总结
- Android 11+ 的包可见性限制是需要特别注意的问题
- QUERY_ALL_PACKAGES权限是解决查询不到应用的关键
- Settings.Secure和Telephony.Sms两种方式获取默认应用各有优缺点,建议结合使用
- remember的使用方式会影响数据的实时更新,需要配合状态管理