Как отправить SMS с помощью SMSmanager в мобильном телефоне с двумя SIM-картами?

Я использую SMS-менеджер для отправки SMS. Для одной SIM-карты он отлично работает для отправки SMS. Но на двух SIM-картах SMS не отправляется. Можно ли отправлять SMS с двух SIM-карт, если это возможно, означает, как я могу выбрать какую SIM для отправки SMS. Может кто-нибудь знает, помогите мне решить эту проблему.

Рабочий код для одной SIM-карты

SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(ph_number, null, body, null,null);

person Yugesh    schedule 08.12.2014    source источник
comment
это может быть то, что вы ищете stackoverflow.com/questions/24007885/   -  person QAMAR    schedule 08.12.2014
comment
comment
@MurtazaHussain Я хочу сделать все сам в своем коде. Возможно ли это.   -  person Yugesh    schedule 08.12.2014
comment
да это оно. найдите номер scAddress и передайте его в свой код.   -  person Murtaza Khursheed Hussain    schedule 08.12.2014
comment
@MurtazaHussain, но в этом ответе они сказали. Похоже, нет способа получить число программно   -  person Yugesh    schedule 08.12.2014
comment
ну, официальной документации Android по поддержке двух SIM-карт нет, устройства, которые поддерживают эту функцию, производятся зависимо. Вам необходимо связаться с производителем устройства для конкретного API   -  person Murtaza Khursheed Hussain    schedule 08.12.2014
comment
вы можете сделать это, используя намерение. но это не отправка смс в фоновом режиме.   -  person Krunal Indrodiya    schedule 08.12.2014
comment
@KrunalIndrodiya, чтобы открыть и отправить его в приложении SMS по умолчанию.   -  person Yugesh    schedule 08.12.2014
comment
@Yugesh ya, это отличная идея, чтобы пользователь мог выбрать, нет ли, чтобы отправить смс. Надеюсь, вы уловили суть.   -  person Krunal Indrodiya    schedule 08.12.2014


Ответы (4)


Я использую этот способ для управления тем, какую SIM-карту использовать для отправки SMS, даже длинных сообщений. Он работает на моем телефоне Lenovo A319 с двумя SIM-картами (4.4.3), нет необходимости в руте. он построен на размышлении.

import android.app.PendingIntent;
import android.content.Context;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Apipas on 6/4/15.
 */
public class SimUtil {

    public static boolean sendSMS(Context ctx, int simID, String toNum, String centerNum, String smsText, PendingIntent sentIntent, PendingIntent deliveryIntent) {
        String name;

        try {
            if (simID == 0) {
                name = "isms";
                // for model : "Philips T939" name = "isms0"
            } else if (simID == 1) {
                name = "isms2";
            } else {
                throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
            }
            Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class);
            method.setAccessible(true);
            Object param = method.invoke(null, name);

            method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", IBinder.class);
            method.setAccessible(true);
            Object stubObj = method.invoke(null, param);
            if (Build.VERSION.SDK_INT < 18) {
                method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
                method.invoke(stubObj, toNum, centerNum, smsText, sentIntent, deliveryIntent);
            } else {
                method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
                method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsText, sentIntent, deliveryIntent);
            }

            return true;
        } catch (ClassNotFoundException e) {
            Log.e("apipas", "ClassNotFoundException:" + e.getMessage());
        } catch (NoSuchMethodException e) {
            Log.e("apipas", "NoSuchMethodException:" + e.getMessage());
        } catch (InvocationTargetException e) {
            Log.e("apipas", "InvocationTargetException:" + e.getMessage());
        } catch (IllegalAccessException e) {
            Log.e("apipas", "IllegalAccessException:" + e.getMessage());
        } catch (Exception e) {
            Log.e("apipas", "Exception:" + e.getMessage());
        }
        return false;
    }


    public static boolean sendMultipartTextSMS(Context ctx, int simID, String toNum, String centerNum, ArrayList<String> smsTextlist, ArrayList<PendingIntent> sentIntentList, ArrayList<PendingIntent> deliveryIntentList) {
        String name;
        try {
            if (simID == 0) {
                name = "isms";
                // for model : "Philips T939" name = "isms0"
            } else if (simID == 1) {
                name = "isms2";
            } else {
                throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
            }
            Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class);
            method.setAccessible(true);
            Object param = method.invoke(null, name);

            method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", IBinder.class);
            method.setAccessible(true);
            Object stubObj = method.invoke(null, param);
            if (Build.VERSION.SDK_INT < 18) {
                method = stubObj.getClass().getMethod("sendMultipartText", String.class, String.class, List.class, List.class, List.class);
                method.invoke(stubObj, toNum, centerNum, smsTextlist, sentIntentList, deliveryIntentList);
            } else {
                method = stubObj.getClass().getMethod("sendMultipartText", String.class, String.class, String.class, List.class, List.class, List.class);
                method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsTextlist, sentIntentList, deliveryIntentList);
            }
            return true;
        } catch (ClassNotFoundException e) {
            Log.e("apipas", "ClassNotFoundException:" + e.getMessage());
        } catch (NoSuchMethodException e) {
            Log.e("apipas", "NoSuchMethodException:" + e.getMessage());
        } catch (InvocationTargetException e) {
            Log.e("apipas", "InvocationTargetException:" + e.getMessage());
        } catch (IllegalAccessException e) {
            Log.e("apipas", "IllegalAccessException:" + e.getMessage());
        } catch (Exception e) {
            Log.e("apipas", "Exception:" + e.getMessage());
        }
        return false;
    }


}

Добавить разрешение:

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

затем просто вызовите этот (чертов) статический метод следующим образом:)

Чтобы использовать SIM1:

SimUtil.sendSMS(this,0,"00970XXXXXXXXX",null,"Hi Stackoverflow! its me Maher. Sent by sim1",null,null);

Чтобы использовать SIM2:

SimUtil.sendSMS(this,1,"00970XXXXXXXXX",null,"Hi Stackoverflow! its me Maher. Sent by sim2",null,null);

Но подождите... это не сработает, если сообщение длиннее 160 символов... так что лучше:

String textSMS;
//short <160
//    textSMS = "Hi Stackoverflow! its me Maher.";


//long >160
textSMS = "Hi Jerusalem, hi Cairo, Hi Prague, hi Baghdad, hi Riyadh, hi Jeddah, hi Dammam, hi Aleppo, hi Casablanca, hi Damascus, hi Alexandria, hi Algiers, hi Mosul, hi Basra, hi Arabia, hi Tripoli, hi Amman, hi Kuwait, hi Beirut, hi Abu Dhabi";

int simID = 0;//0:sim_1,   1:sim_2

ArrayList<String> messageList = SmsManager.getDefault().divideMessage(textSMS);
if (messageList.size() > 1) {
    SimUtil.sendMultipartTextSMS(this, simID, "00972XXXXXXXXX", null, messageList, null, null);
} else {
    SimUtil.sendSMS(this, simID, "00972XXXXXXXXX", null, textSMS, null, null);
}

поэтому вы можете безопасно передавать тело сообщения, не беспокоясь о длине.

------------ОБНОВЛЕНИЕ 09.10.2016----------

Чтобы использовать PendingIntent/DeliveryIntent в MultipartMessage.. просто создайте ArrayList с тем же содержимым и передайте его. Вот реализация создания списка PendingIntent:

final static String sSMSManagerIntentSENT = "package.DeliveryReport.SMS_SENT";
int numParts = parts.size();
ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();

for (int i = 0; i < numParts; i++) {

    Intent pendingIntent = new Intent(sSMSManagerIntentSENT); 
    //optional if you want to keep info about what action has been done for feedback or analysis later when message is sent
    pendingIntent.putExtra("package.DeliveryReport.phoneNumber", phoneNo); // receiver phoneNo
    pendingIntent.putExtra("package.DeliveryReport.textSMS", msg);// msg body
    pendingIntent.putExtra("SIM", simID); // which sim is sending this message

    pendingIntents.add(PendingIntent.getBroadcast(getActivity(), 0, pendingIntent,PendingIntent.FLAG_ONE_SHOT));
}

Для доставки просто используйте тот же подход.

------------------ Дополнительный ------------------

Я видел, что Android 22 поддерживает несколько сим-карт из Android 5.1, и вот как его использовать ... к сожалению, у меня нет устройства с этой версией для тестирования, поэтому, пожалуйста, для обратной связи:

SmsManager.getSmsManagerForSubscriptionId(int subscriptionId).sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent);

Как получить идентификатор подписки? чтобы просмотреть все доступные идентификаторы подписки, принадлежащие сим-карте:

SubscriptionManager subscriptionManager = SubscriptionManager.from(getApplicationContext());
        List<SubscriptionInfo> subscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
        for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
            int subscriptionId = subscriptionInfo.getSubscriptionId();
            Log.d("apipas","subscriptionId:"+subscriptionId);
        }

** Обратите внимание, что этот код работает на 5.1. если вы попытаетесь запустить его в более старой версии, вы получите исключение, что метод не существует.

------------ОБНОВЛЕНИЕ 19.08.2015----------

Информация о SIM-картах находится в БД: telephony.db (по умолчанию: /data/data/com.android.providers.telephony/databases/telephony.db ) в таблице siminfo. Смотрите скриншот таблицы siminfo в БД на реальном устройстве.

введите описание изображения здесь

К счастью, для этого есть поставщик контента:

"content://telephony/siminfo/"

Таким образом, в основном просто запрашивайте данные из этой таблицы. Важно отметить, что слот 0 представляет SIM1, а 1 представляет SIM2, а слот -1 — со старых/удаленных/замененных SIM-карт.

Это относится к Lenovo A319. Я думаю, что это может работать на других устройствах. Вот метод, который я использую:

public static List<SimInfo> getSIMInfo(Context context) {
        List<SimInfo> simInfoList = new ArrayList<>();
        Uri URI_TELEPHONY = Uri.parse("content://telephony/siminfo/");
        Cursor c = context.getContentResolver().query(URI_TELEPHONY, null, null, null, null);
        if (c.moveToFirst()) {
            do {
                int id = c.getInt(c.getColumnIndex("_id"));
                int slot = c.getInt(c.getColumnIndex("slot"));
                String display_name = c.getString(c.getColumnIndex("display_name"));
                String icc_id = c.getString(c.getColumnIndex("icc_id"));
                SimInfo simInfo = new SimInfo(id, display_name, icc_id, slot);
                Log.d("apipas_sim_info", simInfo.toString());
                simInfoList.add(simInfo);
            } while (c.moveToNext());
        }
        c.close();

        return simInfoList;
    }

а вот класс сущностей SimInfo:

public class SimInfo {
    private int id_;
    private String display_name;
    private String icc_id;
    private int slot;

    public SimInfo(int id_, String display_name, String icc_id, int slot) {
        this.id_ = id_;
        this.display_name = display_name;
        this.icc_id = icc_id;
        this.slot = slot;
    }

    public int getId_() {
        return id_;
    }

    public String getDisplay_name() {
        return display_name;
    }

    public String getIcc_id() {
        return icc_id;
    }

    public int getSlot() {
        return slot;
    }

    @Override
    public String toString() {
        return "SimInfo{" +
                "id_=" + id_ +
                ", display_name='" + display_name + '\'' +
                ", icc_id='" + icc_id + '\'' +
                ", slot=" + slot +
                '}';
    }
}

Удачи,'.

person Maher Abuthraa    schedule 06.06.2015
comment
Отлично, но как я могу получить информацию о sim1 и sim2? Я хочу отправить текст, используя только определенного оператора, скажем, AT&T. есть идеи, как узнать информацию о каждой сим-карте? - person Alireza A. Ahmadi; 18.08.2015
comment
Привет, Алиреза, посмотри мой обновленный ответ. Я включил утилиту, которую использую для этой цели. - person Maher Abuthraa; 19.08.2015
comment
спасибо большое обязательно попробую. - person Alireza A. Ahmadi; 20.08.2015
comment
когда я попробовал это .. sms отправляет только sim1 только .. можете ли вы дать какой-либо общий рабочий исходный код, пожалуйста - person harikrishnan; 22.06.2016
comment
@harikrishnan, передайте 1 в качестве параметра для simID, чтобы иметь возможность отправлять сообщения через Sim2, поскольку 0: sim1, sim2: 1 ... у вас тот же исходный код, что и у меня .. больше нет кода, связанного с двумя SIM-картами. инг - person Maher Abuthraa; 30.06.2016
comment
спасибо за хороший ответ! как я могу иметь ожидающее намерение для текстового сообщения, состоящего из нескольких частей? не могли бы вы мне помочь, пожалуйста? - person e.hadid; 09.10.2016
comment
@e.hadid Я обновил свой ответ .. см. обновление 09.10.2016 - person Maher Abuthraa; 09.10.2016
comment
@махерАбутраа. в Android 6 Marshmallow это дает мне NoSuchMethodException:sendText . не могли бы вы помочь мне, что делать? - person e.hadid; 25.10.2016
comment
Поскольку в Android 22 и более поздних версиях у него есть собственная реализация Dual .. и не используется это отражение, см. мой ответ в подзаголовке EXTRA. - person Maher Abuthraa; 25.10.2016
comment
@MaherAbuthraa, я получил исключение NullpointerException для 'stubObj' в методе sendSMS(). - person Vasu; 18.01.2017
comment
@Vasu, какой API вы используете? этот код отражения не будет работать с API-22 и выше - person Maher Abuthraa; 18.01.2017
comment
Я использую API 23, у меня нет проблем с API›=22 (другой код работает правильно), мне нужно решение для API‹22, поэтому, пожалуйста, помогите мне. - person Vasu; 18.01.2017
comment
он говорит NoSuchMethodException: sendText для версии Android 7.0. что я должен делать? - person JAMSHAID; 11.06.2018
comment
не существует столбца slot вместо этого существует столбец sim_id в content://telephony/siminfo/ - person hossein hatami; 13.08.2019

Я попробовал метод Mahers Refletion для отправки смс на телефоне Android с двумя SIM-картами (API 19 и ниже). Чипсет в смартфоне был от Spreadtrum. Я столкнулся с исключениями в коде Махера, сначала это было исключение Null Pointer, в названии = isms2. Для меня sim1 был isms0, а sim2 был isms1, я получил эту информацию в dumpsys. С большой отладкой и еще несколькими поисками у меня работал следующий код:

public class SimUtil {

public static boolean sendSMS(Context ctx, int simID, String toNum, String centerNum, String smsText, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    String name;

    try {
        if (simID == 0) {
            name = "isms0";
        } else if (simID == 1) {
            name = "isms1";
        } else {
            throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
        }

        try
        {
            Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", new Class[]{String.class});
            method.setAccessible(true);
            Object param = method.invoke(null, new Object[]{name});
            if (param == null)
            {
                throw new RuntimeException("can not get service which is named '" + name + "'");
            }
            method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", new Class[]{IBinder.class});
            method.setAccessible(true);
            Object stubObj = method.invoke(null, new Object[]{param});
            method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
            method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsText, sentIntent, deliveryIntent);
        } catch (ClassNotFoundException e)
        {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e)
        {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e)
        {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }

        return true;
    } catch (ClassNotFoundException e) {
        Log.e("Exception", "ClassNotFoundException:" + e.getMessage());
    } catch (NoSuchMethodException e) {
        Log.e("Exception", "NoSuchMethodException:" + e.getMessage());
    } catch (InvocationTargetException e) {
        Log.e("Exception", "InvocationTargetException:" + e.getMessage());
    } catch (IllegalAccessException e) {
        Log.e("Exception", "IllegalAccessException:" + e.getMessage());
    } catch (Exception e) {
        Log.e("Exception", "Exception:" + e);
    }
    return false;
}

}

Следующие ссылки могут быть полезными:

person Shweta Shrestha    schedule 26.03.2016

Решение Махера почти правильное.

Я попробовал это на Motorola motog 5.1 android (одна сим), но его решение с таблицей чтения content://telephony/siminfo имело одну небольшую ошибку:

на моей мотороле не было поля slot а sim_id

остальное было прекрасно и идентично.

person Kamil Orzechowski    schedule 21.07.2016

что касается решения @Vibhav, вот как я мог бы успешно его реализовать

method = Class.forName("android.telephony.SubscriptionManager").getDeclaredMethod("getSubId", int.class);
method.setAccessible(true);
int simID = 1; //while simID is the slot number of your second simCard
param = (int[]) method.invoke(null, new Integer(simID));
int inst =  param[0];
smsMan = SmsManager.getSmsManagerForSubscriptionId(inst);
smsMan.sendTextMessage(toNum, null, smsText, null, null);

Это решение отлично сработало со мной, но я настоятельно рекомендую это гораздо более чистое решение (не требуется отражения, работает для уровня API 22+), которое можно найти здесь https://stackoverflow.com/a/51380282/3427883

person Abdu    schedule 20.09.2018