受欢迎的博客标签

Android|Android Studio编程入门之2(Android 11:短信转发权限适配)

Published

SMS verification code forwarder

Samsung phone supporting Galaxy  S21

create android app project with java Android Studio

create new porject with Android Studio

choice java

需要获取刚接收到的第一条短信,比如登录账号时需要手机验证码,这就需要实时监听手机短信。

1. basic

在不考虑Android系统对短信读取权限有限制的情况下,实时监听手机短信的到来有两种方法:

(1)通过广播接收器:当手机收到一条短信时系统会发送一条广播:"android.provider.Telephony.SMS_RECEIVED",通过该广播就能够知道是否有新短信,通过该广播可以获取到短信内容。

(2)通过观察短信数据库:当手机收到一条短信时,短信数据库会发生变化。Android提供了ContentObserver类,只要创建一个类继承自ContentObserver类,并重写类里的onChange()方法,当短信数据库发生变化时,就会回调到onChange()方法。

2. Android系统对短信读取权限的限制条件及各版本的主要变化

(1)安卓最近几个版本的新特性: 5.0的时候出现了Design风格 6.0出现的危险权限需要申请 7.0出现的目录访问被限制 8.0通知栏的机制   

(2)API 对应关系 Android 8.1  --- Api 27 Android 8.0  --- Api 26  

(3)android 4.4之后,新增了一个default sms的机制,应用必须是默认的短信程序才可以收发短信.

  (4) Android 4.4 版本以上系统第三方应用无权写短信数据库;

没有 default SMS app能力的app应用可以接收SMS_RECEIVED_ACTION的广播接收到短信,接收到这个广播,短信不会写入短信数据库

 没有 default SMS app能力的app发送短信,不会出现在短信数据库中。

(5)第三方应用对发送者为特服号码(1开头)、内容为验证码的通知类短信没有权限读取。

只能监听到发送者号码是普通11位手机号码的短信内容。

(6) blogspot上的一篇文章,阐述短信权限的限制条件问题,原文

https://www.cnblogs.com/wangyk517/p/5881160.html

3.

用户把第三方应用设置为default SMS app

 

Andriod App短信读取流程

在 Android 开发中,整体就分为三个必要环节:

1.注册读取短信的权限:在 Andriod App 的 AndroidManifest.xml

2.注册广播事件:Android 有一个基本组件叫做 BroadcastReceiver,用它来监听来自系统的各种事件广播.

3.实现短信广播接收:这里就需要我们真正实现短信接收的逻辑了,这里只需要实现一个 SmsReceiver 类来继承一个 BroadcastReceiver 然后实现其 onReceive 方法即可

 

 

 

Andriod App短信读取源码实现(java)

Step 1:注册读取短信的权限

在一个 Android App 中,读取短信是需要特定的权限的,所以我们需要在 Andriod App 的 AndroidManifest.xml 中将读取短信的权限配置,比如接收短信的权限配置如下:

<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.smsforwarder">
    <!--blog see:https://mp.weixin.qq.com/s/2Azi7iEFyrVkhrEcECcYzg-->
    <!--GitHub 仓库地址为:https://github.com/pppscn/SmsForwarder-->

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    
     <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SmsForwarder">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.SmsForwarder.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
    

 

 

(一)root后直接读取短信数据库  

public void readSms(View view)

{ String path = "data/data/com.android.providers.telephony/databases/mmssms.db"; try { //修改权限 RootTools.sendShell("chmod 777 " + path, 3000); //读取短信 SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY); Cursor cursor = db.query("sms", new String[]{"address","body"}, null, null, null, null, null); while(cursor.moveToNext()){ System.out.println(cursor.getString(cursor.getColumnIndex("address"))); System.out.println(cursor.getString(cursor.getColumnIndex("body"))); } //恢复权限 RootTools.sendShell("chmod 660 " + path, 3000); } catch (Exception e) { e.printStackTrace(); } }

Step 2:监听广播:BroadCastReceiver:通过广播接收器来实现监听用监听广播的方法来实现。但是这种方法只对新接收到的短信有效,对收件箱中的未读和已读短信无法响应。而且如果一些安全软件对短信进行拦截后,无法接收到广播。

Creating the Broadcast Receiver.

package com.example.sms.forwarder.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;

import androidx.annotation.RequiresApi;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;



public class SmsBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

    }

}

Registering Broadcast Receiver

 

 

(三)ContentObserver:监听短信数据库的变化使用ContentObserver观察者来观察短信数据库,但数据发生改变时,回调做后续处理。

短信的数据库表结构:

sms主要结构:  _id:          短信序号,如100

thread_id:对话的序号,如100,与同一个手机号互发的短信,其序号是相同的  address:  发件人地址,即手机号,如+86138138000

person:   发件人,如果发件人在通讯录中则为具体姓名,陌生人为null

date:       日期,long型,如1346988516,可以对日期显示格式进行设置

protocol: 协议0SMS_RPOTO短信,1MMS_PROTO彩信

read:      是否阅读0未读,1已读

status:    短信状态-1接收,0complete,64pending,128failed

type:       短信类型1是接收到的,2是已发出

body:      短信具体内容

service_center:短信服务中心号码编号,如+8613800755500   

要操作数据库,使用ContentResolver,短信的content uri :

全部短信:content://sms/

收件箱:content://sms/inbox

发件箱:content://sms/sent

草稿箱:content://sms/draft  

(四)其他说明由于大部分手机都对Android系统进行了深度定制,有时候会发现明明手机里有的短信会话(比如通知类短信),自己的应用却获取不到。

一般是由于权限问题导致的: 以小米手机为例,MIUI的应用有一个权限是允许获取通知类短信,没有给应用开启这项权限的话,是无法获取到通知类的短信的

三、android 发送短信的方法

(一)方法一:利用Intent调用系统的短信APP,发送短信  

Intent smsIntent = new Intent(Intent.ACTION_VIEW); smsIntent.setData(Uri.parse("smsto:")); smsIntent.setType("vnd.android-dir/mms-sms");//必须指定type smsIntent.putExtra("address", new String("15050524563"));//address字段不能改 smsIntent.putExtra("sms_body", "测试");//sms_body 不能改 startActivity(smsIntent);

(二)方法二:利用SmsManager 调用系统发送短信接口(此方法可以实现短信发送成功后回调)

SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent); /** * 参数说明 * destinationAddress:收信人的手机号码 * scAddress:发信人的手机号码 * text:发送信息的内容 * sentIntent:发送是否成功的回执,用于监听短信是否发送成功。 * DeliveryIntent:接收是否成功的回执,用于监听短信对方是否接收成功。 */

四、默认短信程序背景:

Android4.4以上系统第三方应用无权写短信数据库,对发送者为1开头的特服号吗,内容为验证码那类的通知类短信也没有权限读取。只能监听到发送者号码是普通11手机号码的短信内容。

(一)you must create 4 file:

1.smsReader-->Receive SMS messages

2.mmsReader-->Receive MMS messages

3.ComposeSMS-->Handle requests to send messages, with UI

4.HeadlessSMSservice-->Provide a headless service to send SMS messages without UI – e.g. for responding to incoming calls  

 

Other

Android 11, when an app asks for permission to use sensitive features like your location, microphone, or camera, you can choose to only grant it access on a one-time basis. The app will be able to use that permission during that instance of you using the app, but the permission is revoked as soon as you leave it. The next time you use the app, and it wants to use that permission, it needs to be granted access again

 

android 避免进程被杀,或者被杀后,能自启动。保证应用在后台长期运行!
发广播和守护进程好像不太可靠!

1.设置APP开机自启动

2.开一个service,在service中的ondestroy方法,重开这个service.

 

Useful links

验证码收发(wechat)

一个开源软件,叫做 SmsForwarder,中文翻译过来叫做短信转发器,其 GitHub 仓库地址为:https://github.com/pppscn/SmsForwarder。

 

How to save SMS to inbox in android?

Replacing the default SMS app Android之发送短信和接收验证码

android 4.4 设置默认短信和来电短信拒接

Android4.4以上系统选择将第三方短信应用设置为默认短信应用方法

Android设为系统默认的短信应用

Android短信监听实现,及Android4.4之后短信机制变更

Android 4.4 如何开发自己的短信 SMS App.