记一次Android Flash Sms引起的问题

闪信接收分析

flash sms现在虽然用的很少了,但是手机的代码中默认还是支持闪信这种类型的,在某邮箱里还是支持下发闪信的

由于我们设备一直不用短信这个功能,突然一天跳出一个页面:

flash sms 权限提示

一开始直接懵逼,怎么会跳出一个短信页面呢,查看记录的日志跟踪到该信息为闪信即零级短信(由于收到一个诈骗电话,设备直接挂断了,运营商发下来一个闪信,导致跳出了这个页面),在没有同意权限的情况下,就会跳出这个授权的页面.如果同意授权了,再收到闪信,就会是下面这个样子:

接收flash sms

出现这个问题了,那就找原因吧:

  • 第一步:

在脑海里回顾运行模型,拉通整个流程,缩小范围想可能是哪里的问题。

得了,这个不是咱们写的,没得搞,进入第二步

  • 第二步:

查看日志、查看各种快照。

由于咱们有记录日志,那么可以先查看日志,根据大概时间一行一行查看,看到了如下日志

I/ActivityManager( 1623): START u0 {flg=0x18000000 cmp=com.android.mms/.ui.ClassZeroActivity (has extras)} from uid 10021 on display 0
#省略部分
I/ActivityManager( 1623): START u0 {cmp=com.android.mms/.ui.PermissionGuardActivity (has extras)} from uid 10021 on display 0

难道这就是起的这个页面吗?当然如果有条件,可以用命令查看一下当前顶部焦点页面是哪个也能确认到就是这个PermissionGuardActivity页面,接下来就是从代码的角度(源码)里去分析这个页面的弹出流程。

由于我们不需要零级短信,最后的处理呢,自然是修改源码,过滤掉零级短信的显示了,接下来看下闪信接收流程。源码为7.1.1原生代码

Mms的类图与时序图

类图:

flash sms 类图

时序图:

flash sms 时序图

涉及对象:

  1. PrivilegedCbReceiver
  2. PrivilegedCbReceiver的父类SmsReceiver
  3. SmsReceiverService
  4. SmsReceiverService的内部类ServiceHandler
  5. ClassZeroActivity
  6. MessageUtils
  7. PermissionGuardActivity

查看时序图大致就能知道跳出闪信页面的流程了,下面贴下代码

流程代码分析

新信息从RIL到达framework后,是从哪里发送给MMs应用处理的- - 我目前还没理清具体是从哪里发出来的,看了GsmSMSDispatcher->GsmInboundSmsHandler->StateMachine,但也没看出来是怎样子发送出来的,有更了解的可以科普科普。

不了解具体是从哪里发出来,不影响对这个问题的分析,接下来看MMs里的接收

  • 发送到了MMS应用内的PrivilegedCbReceiver
public class PrivilegedCbReceiver extends SmsReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //接收到消息后,调用父类的onReceiveWithPrivilege方法
        onReceiveWithPrivilege(context, intent, true);
    }
}
  • SmsReceiver的onReceiveWithPrivilege

这里会启动SmsReceiverService服务,利用intent将消息传过去

protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
        if (!MessageUtils.hasBasicPermissions()) {
            Log.d("Mms", "SmsReceiver do not have basic permissions");
            return;
        }
        String action = intent.getAction();
        LogTag.debugD("onReceiveWithPrivilege:intent="+intent+"|privileged="+privileged);
        // If 'privileged' is false, it means that the intent was delivered to the base
        // no-permissions receiver class.  If we get an SMS_RECEIVED message that way, it
        // means someone has tried to spoof the message by delivering it outside the normal
        // permission-checked route, so we just ignore it.
        if (!privileged && (Intents.SMS_DELIVER_ACTION.equals(action) ||
                "android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED".equals(action))) {
            return;
        }

        intent.setClass(context, SmsReceiverService.class);
        intent.putExtra("result", getResultCode());
        //起SmsReceiverService服务,在beginStartingService方法内会唤醒屏幕锁
        beginStartingService(context, intent);
    }
  • SmsReceiverService

在SmsReceiverService内部,主要是经过它的onStartCommand,将消息传递到mServiceHandler内,ServiceHandler是SmsReceiverService的一个内部类继承Handler

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!MmsConfig.isSmsEnabled(this)) {
            LogTag.debugD("SmsReceiverService: is not the default sms app");
            // NOTE: We MUST not call stopSelf() directly, since we need to
            // make sure the wake lock acquired by AlertReceiver is released.
            SmsReceiver.finishStartingService(SmsReceiverService.this, startId);
            return Service.START_NOT_STICKY;
        }
        // Temporarily removed for this duplicate message track down.

        int resultCode = intent != null ? intent.getIntExtra("result", 0) : 0;

        if (resultCode != 0) {
            LogTag.debugD("onStart: #" + startId + " resultCode: " + resultCode +
                    " = " + translateResultCode(resultCode));
        }
        //将消息传递到mServiceHandler内处理
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
        return Service.START_NOT_STICKY;
    }
  • ServiceHandler
  1. handleMessage : 消息队列方式,处理接收到的消息
private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            int serviceId = msg.arg1;
            Intent intent = (Intent)msg.obj;
            LogTag.debugD("handleMessage serviceId: " + serviceId + " intent: " + intent);
                LogTag.debugD("handleMessage action: " + action + " error: " + error);
                ~~~省去部分代码
                
                if (CB_AREA_INFO_RECEIVED_ACTION.equals(action)) {
                    //调用消息接收处理
                    handleSmsReceived(intent, error);
            }
            ~~~省去部分代码
        }
    }
  1. handleSmsReceived : 处理接收到的短消息
private void handleSmsReceived(Intent intent, int error) {
        SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
        String format = intent.getStringExtra("format");

        // Because all sub id have been changed to phone id in Mms,
        // so also change it here.
        SmsMessage sms4log = msgs[0];
        LogTag.debugD("handleSmsReceived" + (sms4log.isReplace() ? "(replace)" : "") +
                    ", address: " + sms4log.getOriginatingAddress() +
                    ", body: " + sms4log.getMessageBody());
        //获取存储本地,在config.xml里 <bool name="config_savelocation">false</bool>,默认是false
        int saveLoc = MessageUtils.getSmsPreferStoreLocation(this,
                SubscriptionManager.getPhoneId(msgs[0].getSubId()));
        if (getResources().getBoolean(R.bool.config_savelocation)
                && saveLoc == MessageUtils.PREFER_SMS_STORE_CARD) {
            LogTag.debugD("PREFER SMS STORE CARD");
            for (int i = 0; i < msgs.length; i++) {
                SmsMessage sms = msgs[i];
                boolean saveSuccess = saveMessageToIcc(sms);
                if (saveSuccess) {
                    int subId = TelephonyManager.getDefault().isMultiSimEnabled()
                            ? sms.getSubId() : (int)MessageUtils.SUB_INVALID;
                    int phoneId = SubscriptionManager.getPhoneId(subId);
                    String address = MessageUtils.convertIdp(this,
                            sms.getDisplayOriginatingAddress(), phoneId);
                    MessagingNotification.blockingUpdateNewIccMessageIndicator(
                            this, address, sms.getDisplayMessageBody(),
                            subId, sms.getTimestampMillis());
                    getContentResolver().notifyChange(MessageUtils.getIccUriBySubscription(
                            phoneId), null);
                } else {
                    LogTag.debugD("SaveMessageToIcc fail");
                    mToastHandler.post(new Runnable() {
                        public void run() {
                            Toast.makeText(getApplicationContext(),
                                    getString(R.string.pref_sim_card_full_save_to_phone),
                                    Toast.LENGTH_LONG).show();
                        }
                    });
                    // 存储消息
                    saveMessageToPhone(msgs, error, format);
                    break;
                }
            }
        } else {
         // 走到这里存储消息
            saveMessageToPhone(msgs, error, format);
        }
    }
  1. saveMessageToPhone : 存入消息到手机
private void saveMessageToPhone(SmsMessage[] msgs, int error, String format){
        setSavingMessage(true);
        //插入消息
        Uri messageUri = insertMessage(this, msgs, error, format);

        MessageUtils.checkIsPhoneMessageFull(this);

        if (messageUri != null) {
            long threadId = MessagingNotification.getSmsThreadId(this, messageUri);
            // Called off of the UI thread so ok to block.
            LogTag.debugD("saveMessageToPhone messageUri: " + messageUri + " threadId: " + threadId);
            MessagingNotification.blockingUpdateNewMessageIndicator(this, threadId, false);
            MessageUtils.updateThreadAttachTypeByThreadId(this, threadId);
        } else {
            LogTag.debugD("saveMessageToPhone messageUri is null !");
        }
        setSavingMessage(false);
    }
  1. insertMessage : 插入消息,会判断消息类型,闪信的话是不存储的,只弹框
private Uri insertMessage(Context context, SmsMessage[] msgs, int error, String format) {
        // Build the helper classes to parse the messages.
        SmsMessage sms = msgs[0];
        //闪信真面目,判断到是闪信
        if (sms.getMessageClass() == SmsMessage.MessageClass.CLASS_0) {
			//显示闪信的弹窗
            displayClassZeroMessage(context, sms, format);
            return null;
        } else if (sms.isReplace()) {
            return replaceMessage(context, msgs, error);
        } else {
            return storeMessage(context, msgs, error);
        }
    }
  1. displayClassZeroMessage : 显示闪信页面
private void displayClassZeroMessage(Context context, SmsMessage sms, String format) {
        int subId = sms.getSubId();
        //起一个页面来显示闪信
        Intent smsDialogIntent = new Intent(context, ClassZeroActivity.class)
                .putExtra("pdu", sms.getPdu())
                .putExtra("format", format)
                .putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                          | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);

        context.startActivity(smsDialogIntent);
    }
  • ClassZeroActivity :显示闪信页面
 protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        //上来先校验权限
        if (MessageUtils.checkPermissionsIfNeeded(this)) {
            return;
        }
 }
  • MessageUtils :工具类
  1. checkPermissionsIfNeeded :校验权限
public static boolean checkPermissionsIfNeeded(Activity activity) {
        if (hasBasicPermissions()) {
            MmsApp.getApplication().initPermissionRelated();
            if (hasPermissions(sSMSExtendPermissions)) {
                return false;
            }
        }
        launchPermissionCheckActivity(activity, sSMSExtendPermissions);//如果没权限,开启权限认证的页面
        activity.finish();
        return true;
    }
  1. launchPermissionCheckActivity :开启权限申请页面
public static void launchPermissionCheckActivity(Activity activity,String [] permissions) {
        final Intent intent = new Intent(activity, PermissionGuardActivity.class);
        intent.putExtra(PermissionGuardActivity.ORIGINAL_INTENT, activity.getIntent());
        intent.putExtra(PermissionGuardActivity.EXT_PERMISSIONS, permissions);
        activity.startActivity(intent);
    }

自此,所以跳出了一个权限申请页面。如果同意了权限后,再来闪信,就会直接弹dialog方式显示出闪信的信息内容,最后的处理就是直接屏蔽掉前面提到的displayClassZeroMessage方法即可。

借此机会记录和分享给大家,在出现这个问题前都不知道这个Flash sms。如果大家遇到了,希望给大家的处理能带来一点思路

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值