Obscurus Grassator
Obscurus Grassator

Reputation: 165

Android - Java - BroadcastReceiver.onReceive() is not called after sent SMS

My Xiaomi doesn't send SMS at all, but the SMS is listed as unsent in the message history of the default SMS client.

Samsung will real send the SMS, but BroadcastReceiver.onReceive() is not called and my app don't know if it was sent. Result of sms.sent() is my timeout error message.

(I'm not a Java developer - I just put together all the solutions I found. If you see any mistakes, please do not hesitate to point them out to me.)

LogCat show:

SMS.java:

public class SMS {
    public Context context = null;

    SMS(Context context) { this.context = context; }

    static class Result {
        public String status = null;
        public String body = null;
        public String error = null;

        Result(String body) { this.body = body; }
        Result(String body, String error) { this.error = error; }
        Result(String body, String error, String status) {
            if (body != null && !body.equals(""))
                 this.body = body;
            else this.error = error;
            this.status = status;
        }
    }

    public static String SENT = "SMS_SENT";
    public static String DELIVERED = "SMS_DELIVERED";

    public Result sendSMS(JSONObject input) {
        final Result[] res = {null};

        try {
            ArrayList<PendingIntent> sentPendingIntents      = new ArrayList<PendingIntent>();
            ArrayList<PendingIntent> deliveredPendingIntents = new ArrayList<PendingIntent>();

            SmsManager smsManager = SmsManager.getDefault();
            ArrayList<String> mSMSMessage = smsManager.divideMessage(input.getString("message"));

            for (int i = 0; i < mSMSMessage.size(); i++) {
                Intent iSent = new Intent(SENT)
                    .setPackage(context.getPackageName())
                    .setClass(context, SMSBroadcastReceiver.class)
                    .putExtra("result", res);
                Intent iDelivered = new Intent(DELIVERED)
                    .setPackage(context.getPackageName())
                    .setClass(context, SMSBroadcastReceiver.class)
                    .putExtra("result", res);

                PendingIntent sentPI      = PendingIntent.getBroadcast(context, i, iSent,      PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
                PendingIntent deliveredPI = PendingIntent.getBroadcast(context, i, iDelivered, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);

                sentPendingIntents.add(i, sentPI);
                deliveredPendingIntents.add(i, deliveredPI);
            }

            BroadcastReceiver sentReceiver = new SMSBroadcastReceiver();
            context.registerReceiver(sentReceiver, new IntentFilter(SENT), context.RECEIVER_NOT_EXPORTED);

            BroadcastReceiver deliveredReceiver = new SMSBroadcastReceiver();
            context.registerReceiver(deliveredReceiver, new IntentFilter(DELIVERED), context.RECEIVER_NOT_EXPORTED);

            smsManager.sendMultipartTextMessage(input.getString("number"), null, mSMSMessage, sentPendingIntents, deliveredPendingIntents);

            int miliSecSleep = 300;
            int miliSecMax = 1000 * 18;
            int miliSecDeliveredMin = 1000 * 5;
            while (miliSecMax > 0) {
                Thread.sleep(miliSecSleep);
                miliSecMax = miliSecMax - miliSecSleep;
                miliSecDeliveredMin = miliSecDeliveredMin - miliSecSleep;

                if (res[0] != null && (res[0].status.equals(DELIVERED) || miliSecDeliveredMin < 0)) {
                    if (res[0].error != null)
                         Log.e("~=", res[0].error);
                    else Log.d("~=", res[0].body);

                    break;
                }
            }
            if (res[0] == null) {
                res[0] = new Result("", "SMS status: 25s TimeOut for SMS sending has expired");
            }

            context.unregisterReceiver(sentReceiver);
            context.unregisterReceiver(deliveredReceiver);
        } catch (Exception e) {
            Log.e("~=", "SMS error: " + e.toString());
            res[0] = new Result("", "SMS error: " + e.toString());
        }

        return res[0];
    }

}

service.java:

public class SMSService extends Service {
    SMS sms = new SMS(this);

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        sms.sent('123456', 'test message');

SMSBroadcastReceiver.java:

public class SMSBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("~=", "this is never be called");

        Functions.Result[] result = intent.getExtras().getParcelableArray("result", Functions.Result.class);

        SmsMessage smsM = SmsMessage.createFromPdu(
            intent.getByteArrayExtra("pdu"),
            intent.getStringExtra("format")
        );

        if (getResultCode() == Activity.RESULT_OK || (smsM != null && smsM.getStatus() == Telephony.Sms.STATUS_COMPLETE))
            result[0] = new Functions.Result(intent.getAction() + " status: DONE", null, intent.getAction());
        else if (intent.getAction().equals(Functions.SENT))
            result[0] = new Functions.Result(null, intent.getAction() + " status: " + getConstantName(getResultCode(), SmsManager.class), intent.getAction());
        else if (smsM != null && smsM.getStatus() == Telephony.Sms.STATUS_FAILED)
            result[0] = new Functions.Result(null, intent.getAction() + " status: STATUS_FAILED (SMS not delivered)", intent.getAction());
    }

    private String getConstantName(int value, Class<?> cls) {
        for ( java.lang.reflect.Field f : cls.getDeclaredFields()) {
            int mod = f.getModifiers();
            if (Modifier.isStatic(mod) && Modifier.isPublic(mod) && Modifier.isFinal(mod)) {
                try {
                    // Log.d(LOG_TAG, String.format("%s = %d%n", f.getName(), (int) f.get(null)));
                    if ((int) f.get(null) == value) return f.getName();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}

MainActivity.java -> permissions for: READ_SMS, SEND_SMS, RECEIVE_SMS

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher_foreground"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Material3.DayNight"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".SMSService"
            android:enabled="true"
            android:exported="true">
        </service>

        <receiver
            android:name=".SMSBroadcastReceiver"
            android:exported="false">
            <intent-filter android:priority="2147483645">
                <action android:name="SMS_SENT" />
                <action android:name="SMS_DELIVERED" />
                <action android:name="android.provider.telephony.SMS_RECEIVED"></action>
            </intent-filter>
        </receiver>
    </application>

</manifest>

Upvotes: -1

Views: 146

Answers (1)

Obscurus Grassator
Obscurus Grassator

Reputation: 165

Big thanks @MikeM. I was already completely depressed. I combined your (in my opinion non-essential parts of) the code with my code, and inexplicably it started working for me on the Samsung \o/. It either doesn't work on Xiaomi, or it works completely randomly, and the delivered event message can't be recognized at all (SmsMessage.createFromPdu() is null), but for now I probably don't have the nerve for next edit of my solution. After this SMS experience, I consider Android to be a very bad OS. I'm glad that I'm not officially developing mobile apps :D .
Just for fun, the current version of my code is here https://github.com/ObscurusGrassator/jjplugin-sms/tree/main/android-apk-source/app/src/main/java/jjplugin/obsgrass/sms

Java @MikeM solution: https://gist.github.com/gonodono/8802779b2119497b848b72803fbaeec1

Kotlin @MikeM solution: https://github.com/gonodono/sms-sender

Upvotes: 0

Related Questions