短信发送一次显示两条之谜

近期有个bug是mtk平台的手机,发送短信每次都会发送两条,两条是同样的内容。

看了下发送是只有一次的,是写数据库写了两次,先整理下短信数据库发送时候是咋写入的:

发送短信数据库写入

短信应用

短信应用中SmsMessageSender

 

    private boolean queueMessage(long token) throws MmsException {
        ...
                    Sms.addMessageToUri(mSubId,
                            mContext.getContentResolver(),
                            Uri.parse("content://sms/queued"), mDests[i],
                            mMessageText, null, mTimestamp,
                            true /* read */,
                            requestDeliveryReport,
                            mThreadId);
        ...
    }

发送前都会写入数据库
 

 

SMSDispatcher

frameworks/opt/telephony/src/java/com/android/internal/telephony/SMSDispatcher.java
   private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
            if (!mIsText || !mPersistMessage ||
                    !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context) ||
                    isFilterOutByPpl(context, mDestAddress, mFullMessageText)) {
                return null;
            }
            ...
            try {
                final Uri uri =  resolver.insert(Telephony.Sms.Sent.CONTENT_URI, values);
            ...
   }
该方法中也会插入数据库,打印日志看该方法也走了,所以会导致数据库中有两条记录。看下它被哪个方法调用:
        private void persistOrUpdateMessage(Context context, int messageType, int errorCode) {
            if (mMessageUri != null) {
                updateMessageState(context, messageType, errorCode);
            } else {
                mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode);
            }
        }
这里可以看出mMessageUri不为null的时候才会走persistSentMessageIfRequired,看到这里可能会有奇怪的地方,mMessageUri应该不为空啊,这个值应该是短信引用中传递进来呀。
        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
                AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
                SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
                boolean isText, boolean persistMessage) {
            ...
            mMessageUri = messageUri;
            ...
        }
SmsTracker构造方法中的确也会初始化mMessageUri, 再看SmsTracker初始化代码:
    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
            PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
            AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
        ...
        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
                unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
                fullMessageText, getSubId(), isText, persistMessage);
    }
再往上回追溯到CdmaSMSDispatcher或者GsmSMSDispatcher,这里拿CdmaSMSDispatcher举例
frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
    protected void sendTextWithEncodingType(String destAddr, String scAddr, String text,
            int encodingType, PendingIntent sentIntent, PendingIntent deliveryIntent,
            Uri messageUri, String callingPkg, boolean persistMessage) {
            ...
            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
                    messageUri, false /*isExpectMore*/, text, true /*isText*/,
                    true);
            ...
    }
继续往上
frameworks/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
    public void sendTextWithEncodingType(String callingPackage, String destAddr, String scAddr,
            String text, int encodingType, PendingIntent sentIntent, PendingIntent deliveryIntent,
            boolean persistMessage) {
        mDispatcher.sendTextWithEncodingType(destAddr, scAddr, text, encodingType, sentIntent,
                deliveryIntent, null/*messageUri*/, callingPackage, persistMessage);
    }
追究到这里终于发现messageUri为null,那么意味着SMSDispatcher中的persistSentMessageIfRequired是必走的。

再来回顾其中代码:
            if (!mIsText || !mPersistMessage ||
                    !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context) ||
                    isFilterOutByPpl(context, mDestAddress, mFullMessageText)) {
                return null;
            }
四个条件:
1.是短信文本
2.需要存储短信
3.调用shouldWriteMessageForPackage方法判断是否要写数据库
4.还是判断是否要写数据库,这个是剔除某些运营商自注册的短信
其中1,2,4条件没啥异常的地方,出异常的地方就是第三个条件。
    public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
        if (SmsManager.getDefault().getAutoPersisting()) {
            return true;
        }
        return !isDefaultSmsApplication(context, packageName);
    }
其中isDefaultSmsApplication就是和字面意思一样判断是否为默认短信应用,这个值返回为true才对,然后shouldWriteMessageForPackage返回false,最后会导致persistSentMessageIfRequired直接return。这是因为默认短信应用在app层中已经写入了短信,framework代码中无需再次写入。
加入打印日志后发现isDefaultSmsApplication居然返回了false!!!
 

真正的原因

isDefaultSmsApplication方法就是通过包名判断,打印包名发现居然是com.mediatek.omacp
搜索下这个应用是在vendor/mediatek/proprietary/packages/apps/Omacp
Omacp全称是OMA_Client_Provisioning  ,详细见 点击打开链接,通过发送短信配置手机的一些设置项目,主要是apn项目。
这个应用只可能是收短信,不会发送短信,而且ps查看手机进程中根本无此进程。
   protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
            PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
            AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
        // Get calling app package name via UID from Binder call
        PackageManager pm = mContext.getPackageManager();
        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); //首先依据uid获取包名数组

        // Get package info via packagemanager
        PackageInfo appInfo = null;
        if (packageNames != null && packageNames.length > 0) {
            try {
                String packageName = getPackageNameViaProcessId(packageNames); //再依据pid获取具体的包名
                if (packageName != null) {
                    packageNames[0] = packageName;
                }
                Rlog.d(TAG, "SmsTrackerFactory and get the package name via process id: " +
                        packageNames[0]);
                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
            } catch (PackageManager.NameNotFoundException e) {
                // error will be logged in sendRawPdu
            }
        }
        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
                unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
                fullMessageText, getSubId(), isText, persistMessage);
    }
Mms和omacp的uid是一样的:
        android:sharedUserId="android.uid.mms"
那么问题就出在getPackageNameViaProcessId方法上
    private String getPackageNameViaProcessId(String[] packageNames) {
        String packageName = null;

        if (packageNames.length == 1) {
            packageName = packageNames[0];
        } else if (packageNames.length > 1) {
            int callingPid = Binder.getCallingPid();
            Iterator index = null;

            ActivityManager am = (ActivityManager) mContext.getSystemService(
                    Context.ACTIVITY_SERVICE);
            List processList = am.getRunningAppProcesses(); //获取全部运行的进程
            if (processList != null)
            {
                index = processList.iterator();
                while (index.hasNext()) {
                    ActivityManager.RunningAppProcessInfo processInfo =
                            (ActivityManager.RunningAppProcessInfo) (index.next());
                    if (callingPid == processInfo.pid) { //判断pid来获取包名
                        packageName = processInfo.processName;
                        break;
                    }
                }
            }
        }

        return packageName;
    }
逻辑很简单,不过ActivityManager的getRunningAppProcesses方法是在apiLevel 21之后就只能获取本进程的相关信息了, API21开始getRunningAppProcesses只返回应用本身的进程信息。不过这个描述有点问题,其实不是本进程,而是和本进程同样uid的应用,同uid的应用可以是其它进程。
getPackageNameViaProcessId已经是在com.android.phone进程中,这样返回的全是是uid为phone的几个进程,当然获取不到短信进程,这样getPackageNameViaProcessId会返回null,导致getSmsTracker获取的包名是拥有短信uid的第一个程序包名,不巧的是第一个恰好是omacp。
这样导致短信发一次会插两次数据库。
 

解决方法

不过对手机开发厂商来说,可以有更简单的方法
在phone进程应用packages/services/Telephony中加入如下权限即可:
    <uses-permission android:name="android.permission.REAL_GET_TASKS" />

 
 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值