1. 新建一条短信, 在发送短信之前, 首先创建的是一个会话Conversation,
以后所有与该接收人(一个或多个接收人)的消息交互, 都在该会话Conversation中.
ComposeMessageActivity:
private void initActivityState(Bundle bundle) {
...
mConversation = Conversation.get(this, ContactList.getByNumbers(recipients, false /* don't block */, true /* replace number */), false);
...
}
1.1. 在创建会话的过程中会初始化一些参数, 如ThreadId
Conversation:
public static Conversation get(Context context, ContactList recipients, boolean allowQuery) {
...
// 重点: 创建获取ThreadId
long threadId = getOrCreateThreadId(context, recipients);
// 创建会话
conv = new Conversation(context, threadId, allowQuery);
... }
1.2. ThreadId贯穿着整个信息的发送流程, 因此我们需要查看ThreadId的创建流程
public static long getOrCreateThreadId(Context context, ContactList list) {
...
long retVal = 0;
try {
retVal = Threads.getOrCreateThreadId(context, recipients, names);
} catch (IllegalArgumentException e) {
Log.e(TAG, " Failed to get or create threadId, exception : " + e);
}
...
}
1.3. 继续创建ThreadId
Telephony$Threads:
public static long getOrCreateThreadId(Context context, Set<String> recipients, Set<String> recipientNames) {
// 重点: THREAD_ID_CONTENT_URI = "content://mms-sms/threadID"
Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
...
// 此处可以看见并没有对ThreadId的创建, 只有一个查询的过程
// 因此我们需要通过THREAD_ID_CONTENT_URI找到对应的ContentProvider, 看看查询的实现逻辑
Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
uri, ID_PROJECTION, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(0);
} else {
Rlog.e(TAG, "getOrCreateThreadId returned no rows!");
}
} finally {
cursor.close();
}
}
}
1.4. 由于上面的步骤只看见ThreadId的查询过程, 并没有看见ThreadId的创建,
因此我们需要到短信的内容提供者MmsSmsProvider中看看ThreadId的具体查询过程
MmsSmsProvider:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
...
switch(URI_MATCHER.match(uri)) {
...
case URI_THREAD_ID: // 此处即为ThreadId的查询
List<String> recipients = uri.getQueryParameters("recipient");
List<String> recipientNames = uri.getQueryParameters("recipientNames");
// 获取ThreadId
cursor = getThreadId(recipients,recipientNames);
break;
...
}
} 1.5. 查看查询过程中ThreadId的具体查询过程
MmsSmsProvider:
private synchronized Cursor getThreadId(List<String> recipients,List<String> recipientNames) {
...
// 重点: 根据接收人获取地址id
// 每个接收人的地址id, 在短信模块中有且只有一个addressId
// 该addressId通过接收人的号码生成, 无论联系人的姓名怎样变化, 该id都不会发生改变
Set<Long> addressIds = getAddressIds(recipients);
...
} 1.5.1. 查看联系人的addressId的查询和创建过程
MmsSmsProvider:
private Set<Long> getAddressIds(List<String> addresses) {
...
for (String address : addresses) {
if (!address.equals(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
// 获取联系人的唯一addressId
long id = getSingleAddressId(address);
if (id != -1L) {
result.add(id);
} else {
Log.e(LOG_TAG, "getAddressIds: address ID not found for " + address);
}
}
}
...
} 1.5.2. 继续查看联系人addressId的查询和创建过程
MmsSmsProvider:
private long getSingleAddressId(String address) {
...
// 前面是一些联系人的类型判断和查询参数的拼接
// 注意: sql中的PHONE_NUMBERS_EQUAL函数是android独有的
// 查询接收人的addressId
cursor = db.query("canonical_addresses", ID_PROJECTION, selection, selectionArgs, null, null, null);
if (cursor.getCount() == 0) { // 如果之前没有改接收人的addressId
ContentValues contentValues = new ContentValues(1);
contentValues.put(CanonicalAddressesColumns.ADDRESS, refinedAddress);
db = mOpenHelper.getWritableDatabase();
// 创建接收人的addressId
retVal = db.insert("canonical_addresses", CanonicalAddressesColumns.ADDRESS, contentValues);
Log.d(LOG_TAG, "getSingleAddressId: insert new canonical_address for " +
/*address*/ "xxxxxx" + ", _id=" + retVal);
return retVal;
} if (cursor.moveToFirst()) { // 如果已经有接收人的addressId, 直接获取返回
retVal = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
} ...
} 1.6. 在获取完所有接收的addressId, 接下来继续看获取ThreadId的流程
MmsSmsProvider:
private synchronized Cursor getThreadId(List<String> recipients,List<String> recipientNames) {
...
// 获取接收人的addressId
Set<Long> addressIds = getAddressIds(recipients);
...
// 如果有多个接收人, 需要对接收人的addressIds进行处理, 保证唯一
// 防止因为顺序等原因导致重复
recipientIds = getSpaceSeparatedNumbers(getSortedSet(addressIds));
...
// 通过接收人的addressIds进行查询
Cursor cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
if (cursor.getCount() == 0) { // 如果没有查询到ThreadId, 则进行创建
// 插入ThreadId
insertThread(recipientIds, recipients.size() ,addresses ,names); db = mOpenHelper.getReadableDatabase();
// 查询ThreadId
cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
}
if (cursor.getCount() > 1) { // 如果查询到ThreadId, 直接返回
Log.w(LOG_TAG, "getThreadId: why is cursorCount=" + cursor.getCount());
}
return cursor;
...
} 1.6.1. 查看插入ThreadId的步骤
MmsSmsProvider:
private void insertThread(String recipientIds, int numberOfRecipients, String addresses ,String names) {
ContentValues values = new ContentValues(4); long date = System.currentTimeMillis();
values.put(ThreadsColumns.DATE, date - date % 1000); // 时间
values.put(ThreadsColumns.RECIPIENT_IDS, recipientIds); // 接收人addressIds
if (numberOfRecipients > 1) {
values.put(Threads.TYPE, Threads.BROADCAST_THREAD); // 类型
}
values.put(ThreadsColumns.MESSAGE_COUNT, 0);// 消息数量
values.put(ThreadsColumns.RECIPIENT_ADDRESSES, addresses);// 接收人的号码
values.put(ThreadsColumns.RECIPIENT_NAMES, names);// 接收人的姓名 // TABLE_THREADS = threads
// 从此处可以看出, threads数据保存的是会话信息, threadId是每条会话信息的Id
long result = mOpenHelper.getWritableDatabase().insert(TABLE_THREADS, null, values);
Log.d(LOG_TAG, "insertThread: created new thread_id " + result +
" for recipientIds " + /*recipientIds*/ "xxxxxxx"); getContext().getContentResolver().notifyChange(MmsSms.CONTENT_URI, null);
} 2. 开始进入发送短信的流程
WorkingMessage:
public void send(final String recipientsInUI, final int phoneId) {
...
//TODO 如果是新建短信, 则threadId为0
long origThreadId = mConversation.getThreadId();
...
// 短信的发送
// Same rules apply as above.
final String msgText = mText.toString();
// 启动线程完成发送短信的操作, 目的是不影响界面的返回
new Thread(new Runnable() {
@Override
public void run() {
// 准备短信发送工作, 包含短信发送工作sendSmsWorker
preSendSmsWorker(conv, mmsUri, msgText, recipientsInUI, phoneId);
updateSendStats(conv);
}
}, "WorkingMessage.send SMS").start();
} 3. 准备短信的发送工作
WorkingMessage:
private void preSendSmsWorker(Conversation conv, Uri mmsUri, String msgText, String recipientsInUI, int phoneId) {
...
// 重点:
long origThreadId = conv.getThreadId();// 新建短信的ThreadId为0
Log.d(TAG, "origThreadId: " + origThreadId); // Make sure we are still using the correct thread ID for our recipient set.
long threadId = conv.ensureThreadId();// 创建ThreadId
Log.d(TAG, "threadId: " + threadId);
...
} 3.1 根据接收人确保ThreadId
Conversation:
public synchronized long ensureThreadId() {
...
if (mThreadId <= 0) {
mThreadId = getOrCreateThreadId(mContext, mRecipients);
}
...
} 3.2 获取或创建id, 如果接收人是之前没有过短信记录, 则创建ThreadId
注意: 如果接收人有多个, 而其中某个或者全部接收, 之前都有自己单独的信息记录,
此处依然会创建ThreadId
public static long getOrCreateThreadId(Context context, ContactList list) {
...
long retVal = 0;
try {
// recipients: 接收人的号码
// names: 接收人的姓名
retVal = Threads.getOrCreateThreadId(context, recipients, names);
} catch (IllegalArgumentException e) {
Log.e(TAG, " Failed to get or create threadId, exception : " + e);
}
...
} 3.3 通过接收人的号码和姓名获取或创建ThreadId
Threads:
public static long getOrCreateThreadId(Context context, Set<String> recipients, Set<String> recipientNames) {
...
// 中间有一段设置查询参数
Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), uri, ID_PROJECTION, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(0);
} else {
Rlog.e(TAG, "getOrCreateThreadId returned no rows!");
}
} finally {
cursor.close();
}
}
...
}
4. 获取到ThreadId, 即会话的id后, 继续消息发送的流程
WorkingMessage:
private void preSendSmsWorker(Conversation conv, Uri mmsUri, String msgText, String recipientsInUI, int phoneId) {
...
// 重点:
long origThreadId = conv.getThreadId();// 新建短信的ThreadId为0
Log.d(TAG, "origThreadId: " + origThreadId); // Make sure we are still using the correct thread ID for our recipient set.
long threadId = conv.ensureThreadId();// 创建ThreadId
Log.d(TAG, "threadId: " + threadId);
...
// 进行短信发送, 主要逻辑分为两步:
// 1. 创建SmsMessageSender对象
// 2. 调用SmsMessageSender.sendMessage继续发起发送短信请求
sendSmsWorker(msgText, semiSepRecipients, threadId, phoneId); // Be paranoid and clean any draft SMS up.
deleteDraftSmsMessage(threadId);
} 5. 执行消息的发送
WorkingMessage:
private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId, int phoneId) {
...
// 1. 创建信息发送者
MessageSender sender;
if (SignatureAppend.getInstance().isSignatureEnabled()) {
Log.e(TAG, "sign:signatureText sender true");
sender = new SmsMessageSender(mActivity, dests, msgText, threadId,phoneId, signatureText);
} else {sender = new SmsMessageSender(mActivity, dests, msgText, threadId,phoneId);
Log.e(TAG, "sign:signatureText sender false");
} try {
// 2. 进行信息的发送
sender.sendMessage(threadId);
// Make sure this thread isn't over the limits in message count
Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
} catch (Exception e) {
Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
}
} 6. 将即将发送的消息, 添加到消息队列, 并发送广播
SmsMessageSender:
public boolean sendMessage(long token) throws MmsException {
return queueMessage(token);
} private boolean queueMessage(long token) throws MmsException {
...
// 获取编码模式: Auto, 7bit, 16bit
requestDeliveryReport = prefs.getBoolean(SimSettingPreferenceActivity.SMS_DELIVERY_REPORT_MODE, DEFAULT_DELIVERY_REPORT_MODE);
...
for (int i = 0; i < mNumberOfDests; i++) {
// 将消息添加到消息队列
Sms.addMessageToUri(mContext.getContentResolver(),
Uri.parse("content://sms/queued"), mDests[i],
mMessageText, null,
mSignatureText, mTimestamp,
true /* read */,
requestDeliveryReport,
mThreadId,
// SPRD: Add for multi-sim module.
mPhoneId);
}
...
// 发送消息发送的广播
Intent intent = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,
null,
mContext,
SmsReceiver.class);// 发送到指定的广播接收者
intent.putExtra(Sms.PHONE_ID, mPhoneId);
mContext.sendBroadcast(intent);
... } 7. 在SmsReceiver中接收处理SmsReceiverService.ACTION_SEND_MESSAGE的广播
SmsReceiver:
protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
...
intent.setClass(context, SmsReceiverService.class);
intent.putExtra("result", getResultCode());
beginStartingService(context, intent); // 启动SmsReceiverService服务, 处理消息的发送
} 8. 在SmsReceiverService进行短信发送处理
SmsReceiverService:
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg); // 发送消息到线程处理
}
SmsReceiverService$ServiceHandler:
public void handleMessage(Message msg) {
...
else if (ACTION_SEND_MESSAGE.endsWith(action)) {
// 处理发送消息
handleSendMessage(intent.getIntExtra(Sms.PHONE_ID, 0));
}
...
}
SmsReceiverService:
private void handleSendMessage(int phoneId) {
if (!mSending) { // 未发送
// 发送消息队列中的第一条消息
sendFirstQueuedMessage(phoneId);
}
}
public synchronized void sendFirstQueuedMessage(int selectionPhoneId) {
...
// 查询待发送的消息
final Uri uri = Uri.parse("content://sms/queued");
ContentResolver resolver = getContentResolver();
// date ASC so we send out in same order the user tried to send messages.
Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, selection, null, "date ASC");
...
if (c.moveToFirst()) { // 查询到有待发送的消息
...
// 创建消息发送器
SmsMessageSender sender = new SmsSingleRecipientSender(this, address, msgText, threadId, status == Sms.STATUS_PENDING, msgUri, phoneId);
...
// 发送消息
sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);;
mSending = true;
}
} 9. 最终在SmsSingleRecipientSender中, 执行消息的发送
SmsSingleRecipientSender:
public boolean sendMessage(long token) throws MmsException {
...
// 消息管理
SmsManager smsManager = SmsManager.getDefault(mPhoneId);
ArrayList<String> messages = null;
// 增加签名
SmsSignatureAppend smsSignatureAppend = new SmsSignatureAppend(mContext, mUri);
// 增加签名后的消息内容
mMessageText = smsSignatureAppend.addSignatureToMsgText(mMessageText);
...
// 获取编码类型, 0: Auto; 1: 7bit; 3: 16bit
if (OperatorUtils.OPEN_MARKET && Settings.System.getInt(mContext.getContentResolver(), Settings.System.SMS_ENCODE_TYPE, 1) == 3) {
messages = divideMessageFor16Bit(mMessageText); // 进行16bit编码进行消息分割
} else {
messages = smsManager.divideMessage(mMessageText);// 进行7bit编码进行消息的分割
}
} 9.1. 查看16bit编码进行消息分割
private ArrayList<String> divideMessageFor16Bit(String text) {
int msgCount;
int limit;
int count = text.length() * 2;// 如果是16bit, 短信允许的长度为70个字符
if (count > SmsMessage.MAX_USER_DATA_BYTES/**/) { // 说明短信的条数大于1条
// 因为java的除法没有小数, 因此这里直接+1条进行计算, 如5.2/2==(int)2; 而我们需要3, (5.2+1)/2==3
msgCount = (count + (SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER/**/ - 1))
/ SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER/**/;
} else {// 说明短信的条数为1
msgCount = 1;
}
if (msgCount > 1) {// 如果短信的条数大于1, 则每条短信的长度为134, 会比只有1条短信少6个字节, 少的6个字节存储索引等数据
limit = SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER/**/;
} else {// 如果短信的条数小于1, 则短信的长度为140
limit = SmsMessage.MAX_USER_DATA_BYTES/**/;
}
ArrayList<String> result = new ArrayList<String>(msgCount);
int pos = 0; // Index in code units.
int textLen = text.length();
while (pos < textLen) {
int nextPos = 0; // Counts code units.
// limit/2: 实际的文本长度
nextPos = pos + Math.min(limit / 2, textLen - pos);// 获取截止的索引 if ((nextPos <= pos) || (nextPos > textLen)) {
Log.e(TAG, "fragmentText failed (" + pos + " >= " + nextPos
+ " or " + nextPos + " >= " + textLen + ")");
break;
}
result.add(text.substring(pos, nextPos));// 保存截取的信息, 注意List是有序的
pos = nextPos;
}
return result;
} 9.2. 查看7bit编码进行消息的分割
SmsManager:
public ArrayList<String> divideMessage(String text) {
return SmsMessage.fragmentText(text);
}
public static ArrayList<String> fragmentText(String text) {
// This function is for MO SMS
TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?// 此处用的是gsm格式
com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :// 计算长度
com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);// 执行此处 // TODO(cleanup): The code here could be rolled into the logic
// below cleanly if these MAX_* constants were defined more
// flexibly... int limit;
if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {// 执行此处
int udhLength;
if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
} else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
} else {
udhLength = 0;// 执行此处
} if (ted.msgCount > 1) {
udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE/**/;
} if (udhLength != 0) {
udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
}
// 如果消息的条数大于1, 则limit为153, 如果消息的条数等于1, 则limit为160
limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;//
} else {
if (ted.msgCount > 1) {
limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
} else {
limit = SmsConstants.MAX_USER_DATA_BYTES;
}
} // 以下进行短信的分割和16bit消息的分割一致
int pos = 0; // Index in code units.
int textLen = text.length();
ArrayList<String> result = new ArrayList<String>(ted.msgCount);
while (pos < textLen) {
int nextPos = 0; // Counts code units.
if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
if (useCdmaFormatForMoSms() && ted.msgCount == 1) {
// For a singleton CDMA message, the encoding must be ASCII...
nextPos = pos + Math.min(limit, textLen - pos);
} else {
// For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
ted.languageTable, ted.languageShiftTable);
}
} else { // Assume unicode.
nextPos = pos + Math.min(limit / 2, textLen - pos);
}
if ((nextPos <= pos) || (nextPos > textLen)) {
Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
nextPos + " >= " + textLen + ")");
break;
}
result.add(text.substring(pos, nextPos));
pos = nextPos;
}
return result;
} 9.2.1 查看文本长度的计算
com.android.internal.telephony.gsm.SmsMessage:
public static TextEncodingDetails calculateLength(CharSequence msgBody, boolean use7bitOnly) {
TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);//计算, 此处use7bitOnly==false
...
}
9.2.2 继续文本的计算
GsmAlphabet:
public static TextEncodingDetails countGsmSeptets(CharSequence s, boolean use7bitOnly) {
...
// fast path for common case where no national language shift tables are enabled
// 常见情况的快速路径,没有启用国家语言转换表
if (sEnabledSingleShiftTables.length + sEnabledLockingShiftTables.length == 0) {// 执行此处
TextEncodingDetails ted = new TextEncodingDetails();
int septets = GsmAlphabet.countGsmSeptetsUsingTables(s, use7bitOnly, 0, 0); // use7bitOnly此处为false
if (septets == -1) {
return null;
}
ted.codeUnitSize = SmsConstants.ENCODING_7BIT;
ted.codeUnitCount = septets;// 编码后的消息长度
if (septets > SmsConstants.MAX_USER_DATA_SEPTETS/**/) { // 说明条数大于1条
ted.msgCount = (septets + (SmsConstants.MAX_USER_DATA_SEPTETS_WITH_HEADER/**/ - 1)) /
SmsConstants.MAX_USER_DATA_SEPTETS_WITH_HEADER/**/;// 计算条数
ted.codeUnitsRemaining = (ted.msgCount *
SmsConstants.MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;// 获取剩余的数量
} else {// 说明条数为1条
ted.msgCount = 1;
ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets;// 获取剩余的数量
}
ted.codeUnitSize = SmsConstants.ENCODING_7BIT;// 设置为7bit编码
return ted;
}
...
} 9.2.3 继续文本长度的计算
GsmAlphabet:
public static int countGsmSeptetsUsingTables(CharSequence s, boolean use7bitOnly,// s, false
int languageTable, int languageShiftTable) {//0, 0
int count = 0;
int sz = s.length();
SparseIntArray charToLanguageTable = sCharsToGsmTables[languageTable]; // GSM 7 bit Default Alphabet Extension Table
SparseIntArray charToShiftTable = sCharsToShiftTables[languageShiftTable];// GSM 7 bit Default Alphabet Extension Table
for (int i = 0; i < sz; i++) {
char c = s.charAt(i);
if (c == GSM_EXTENDED_ESCAPE) {
Rlog.w(TAG, "countGsmSeptets() string contains Escape character, skipping.");
continue;
}
if (charToLanguageTable.get(c, -1) != -1) {
count++;
} else if (charToShiftTable.get(c, -1) != -1) {
count += 2; // escape + shift table index
} else if (use7bitOnly) {
count++; // encode as space
} else {
return -1; // caller must check for this case
}
}
return count;
} 10. 继续消息的发送流程
SmsSingleRecipientSender:
public boolean sendMessage(long token) throws MmsException {
...
if (OperatorUtils.OPEN_MARKET && Settings.System.getInt(mContext.getContentResolver(), Settings.System.SMS_ENCODE_TYPE, 1) == 3) {
messages = divideMessageFor16Bit(mMessageText);
} else {
messages = smsManager.divideMessage(mMessageText);// 进行发送信息
}
... // 将消息移到发件箱, 因此在content://sms/queued中只会存在待发送的信息
boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0);
...
ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount);
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount);
for (int i = 0; i < messageCount; i++) {
// mRequestDeliveryReport: 接收人接收到消息的状态的回复
if (mRequestDeliveryReport && (i == (messageCount - 1))) {
// TODO: Fix: It should not be necessary to
// specify the class in this intent. Doing that
// unnecessarily limits customizability.
deliveryIntents.add(PendingIntent.getBroadcast(
mContext, 0,
new Intent(
MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,
mUri,
mContext,
MessageStatusReceiver.class),
0));
} else {
deliveryIntents.add(null);
} // 消息发送的意图
Intent intent = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
mUri,
mContext,
SmsReceiver.class); intent.putExtra("smssignature", mMessageText);
int requestCode = 0;
if (i == messageCount -1) {
// Changing the requestCode so that a different pending intent
// is created for the last fragment with
// EXTRA_MESSAGE_SENT_SEND_NEXT set to true.
requestCode = 1;
intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true);
intent.putExtra(Sms.PHONE_ID, mPhoneId);
}
sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));
}
...
// 执行消息的发送
smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);
} 11. 消息发送转移至IccSmsInterfaceManager
SmsManager:
public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
...
if (parts.size() > 1) {
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService(TelephonyManager.getServiceName("isms", mPhoneId)));
if (iccISms != null) {
// 发送消息, 至此Mms中的流程已经走完, 下面进入framework层的流程
iccISms.sendMultipartText(ActivityThread.currentPackageName(), destinationAddress, scAddress, parts, sentIntents, deliveryIntents);
}
} catch (RemoteException ex) {
// ignore it
}
} else {
...
// 发送消息
sendTextMessage(destinationAddress, scAddress, parts.get(0),
sentIntent, deliveryIntent);
}
} public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
...
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService(TelephonyManager.getServiceName("isms", mPhoneId)));
if (iccISms != null) {
// 发送消息, 至此Mms中的流程已经走完, 下面进入framework层的流程
iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress,
scAddress, text, sentIntent, deliveryIntent);
}
} catch (RemoteException ex) {
// ignore it
}
}

Android系统应用Mms之Sms短信发送流程(Mms应用部分)二的更多相关文章

  1. Android短彩信源码解析-短信发送流程(二)

    转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935 2,短彩信发送framework逻辑 短信在SmsSingleRecipien ...

  2. Android Sms短信发送

    界面布局: 具体代码: private void sendSms() { // 获取电话号码和短信内容 String number = number1.getText().toString(); St ...

  3. Android短彩信源码解析-短信发送流程(三)

    3.短信pdu的压缩与封装 相关文章: ------------------------------------------------------------- 1.短信发送上层逻辑 2.短信发送f ...

  4. Laravel SMS 短信发送包

    Laravel Sms Laravel 贴合实际需求同时满足多种通道的短信发送组件 我们基于业务需求在 overtrue/easy-sms 基础进行扩展开发,主要实现如下目标: 支持短信验证码直接在 ...

  5. Android系统自带APP分析——短信app

    Android操作系统本身就是一个巨大的开源软件仓库,熟悉它既可以了解到Android系统的设计框架,也可以获得高效的应用程序编写方式.本文所分析的源码来自于Google官方的AOSP源码4.0.1_ ...

  6. 中国网建提供的SMS短信发送

    一个简单的发送短信的小demo 第一步: 兄弟们,首先你们去中国网建的官网去注册一个账户:网址http://sms.webchinese.cn/reg.shtml 第二步: 注册完成之后会有免费的测试 ...

  7. Android黑科技,读取用户短信+修改系统短信数据库

    安卓系统比起ios系统最大的缺点,相信大家都知道,就是系统安全问题.这篇博客就秀一波“黑科技”. 读取用户短信 Android应用能读取用户手机上的短信,相信已经不是什么新鲜事,比如我们收到的短信验证 ...

  8. 短信验证登陆-中国网建提供的SMS短信平台

    一.JAVA发送手机短信常见的有三种方式(如下所列): 使用webservice接口发送手机短信,这个可以使用sina提供的webservice进行发送,但是需要进行注册 使用短信mao的方式进行短信 ...

  9. 发送SMS短信(JSON) 转载

    http://blog.csdn.net/ldl22847/article/details/42553883 public   static string GetMobileConfByUserId( ...

随机推荐

  1. Express全系列教程之(八):session的基本使用

    一.关于session session是另一种记录客户状态的机制,与cookie保存在客户端浏览器不同,session保存在服务器当中:当客户端访问服务器时,服务器会生成一个session对象,对象中 ...

  2. Jmeter post请求传参问题

    同线程组引用参数 举例1:新增数据bizId,然后将此次新增数据删除 添加新增数据接口,然后查询数据列表,正则表达式提取bizId 在删除接口引用此值${bizId} 添加断言,执行查看结果

  3. RemoveError: 'requests' is a dependency of conda and cannot be removed from conda's operating environment

    (base)$ conda update conda conda remove -n *** --all 或者 (base)$ conda update --force conda conda rem ...

  4. Elasticsearch学习笔记(八)Elasticsearch的乐观锁并发控制

    一.基于_version的乐观锁并发控制                 语法:PUT /test_index/test_type/id?version=xxx             更新时带上数据 ...

  5. VUE-007-通过路由 router.push 传递 query 参数(路由 name 识别,请求链接显示参数传递)

    在前端页面表单列表修改时,经常需要在页面切换的时候,传递需要修改的表单内容,通常可通过路由进行表单参数的传递. 首先,配置页面跳转路由.在 router/index.js 中配置相应的页面跳转路由,如 ...

  6. opencart3调用三级菜单level 3 sub categories

    Opencart 3的menu菜单默认只调用一级和二级菜单,但很多电商网站类目复杂,三级菜单一般都是需要的,甚至更深,那么如何调用三级菜单level 3 sub categories呢?ytkah有一 ...

  7. HDD ,SSD和PCIE SSD性能测试

      PCIE SSD   * MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s] * KB = 1000 bytes, KiB = 10 ...

  8. 2019年春季学期第四周作业Compile Summarize

    这个作业属于哪个课程 C语言程序设计一 这个作业要求在哪里 2019春季学期第四周作业 我的课程目标 重新学习有关数组的问题 这个作业在哪个具体方面帮助我实现目标 对于置换有了新的见解 参考文献 中国 ...

  9. python函数带()与否

    一.不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成二.带括号(参数或者无参),调用的是函数的执行结果,须等该函数执行完成的结果 进程和线程的target=fun ...

  10. No input file specified. phpStudy nginx报错解决方案

    1.首先观察路径是否存在, 2.在vhsos.conf文件中 先科普下: 在Windows系统中,正斜杠 / 表示除法,用来进行整除运算:反斜杠 \ 用来表示目录. 在Unix系统中,/ 表示目录:\ ...