/* * Copyright (C) 2007-2008 Esmertec AG. * Copyright (C) 2007-2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mms.transaction; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.provider.Telephony.Sms.Intents.SMS_RECEIVED_ACTION; import static android.provider.Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION; import java.util.ArrayList; import com.android.mms.data.Contact; import com.android.mms.dom.WapPushParser; import com.android.mms.ui.ClassZeroActivity; import com.android.mms.ui.ComposeMessageActivity; import com.android.mms.ui.MessageItem; import com.android.mms.ui.MessageUtils; import com.android.mms.ui.MessagingPreferenceActivity; import com.android.mms.ui.WapPushMessageShowActivity; import com.android.mms.util.Recycler; import com.android.mms.util.SendingProgressTokenManager; import com.google.android.mms.MmsException; import android.database.sqlite.SqliteWrapper; import android.app.Activity; import android.app.Service; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; import android.preference.PreferenceManager; import android.provider.Telephony.Sms; import android.provider.Telephony.Threads; import android.provider.Telephony.Sms.Inbox; import android.provider.Telephony.Sms.Intents; import android.provider.Telephony.Sms.Outbox; import android.provider.Telephony; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.text.format.Time; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.Phone; import com.android.internal.telephony.TelephonyIntents; import com.android.mms.R; import com.android.mms.LogTag; /** * This service essentially plays the role of a "worker thread", allowing us to store * incoming messages to the database, update notifications, etc. without blocking the * main thread that SmsReceiver runs on. */ public class SmsReceiverService extends Service { private static final String TAG = "SmsReceiverService"; private static final boolean DEBUG = true; private ServiceHandler mServiceHandler; private Looper mServiceLooper; private boolean mSending; public static final String MESSAGE_SENT_ACTION = "com.android.mms.transaction.MESSAGE_SENT"; // Indicates next message can be picked up and sent out. public static final String EXTRA_MESSAGE_SENT_SEND_NEXT ="SendNextMsg"; public static final String ACTION_SEND_MESSAGE = "com.android.mms.transaction.SEND_MESSAGE"; // This must match the column IDs below. private static final String[] SEND_PROJECTION = new String[] { Sms._ID, //0 Sms.THREAD_ID, //1 Sms.ADDRESS, //2 Sms.BODY, //3 Sms.STATUS, //4 Sms.PHONE_ID, //5 }; public Handler mToastHandler = new Handler(); // This must match SEND_PROJECTION. private static final int SEND_COLUMN_ID = 0; private static final int SEND_COLUMN_THREAD_ID = 1; private static final int SEND_COLUMN_ADDRESS = 2; private static final int SEND_COLUMN_BODY = 3; private static final int SEND_COLUMN_STATUS = 4; private static final int SEND_COLUMN_PHONE_ID = 5; private int mResultCode; @Override public void onCreate() { // Temporarily removed for this duplicate message track down. // if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { // Log.v(TAG, "onCreate"); // } // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. Log.i(TAG,"onCreate"); HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); Log.i(TAG,"onCreate (1)"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // Temporarily removed for this duplicate message track down. // if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { // Log.v(TAG, "onStart: #" + startId + ": " + intent.getExtras()); // } mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0; Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); return Service.START_NOT_STICKY; } @Override public void onDestroy() { // Temporarily removed for this duplicate message track down. // if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { // Log.v(TAG, "onDestroy"); // } mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) { return null; } private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } /** * Handle incoming transaction requests. * The incoming requests are initiated by the MMSC Server or by the MMS Client itself. */ @Override public void handleMessage(Message msg) { int serviceId = msg.arg1; Intent intent = (Intent)msg.obj; if (intent != null) { String action = intent.getAction(); int error = intent.getIntExtra("errorCode", 0); if (MESSAGE_SENT_ACTION.equals(intent.getAction())) { Intent sendMsg = new Intent("android.provider.Telephony.SEND_SMS"); sendBroadcast(sendMsg); handleSmsSent(intent, error); } else if (SMS_RECEIVED_ACTION.equals(action)) { handleSmsReceived(intent, error); } else if (WAP_PUSH_RECEIVED_ACTION.equals(action)) { Log.i(TAG,"WAP_PUSH_RECEIVED_ACTION"); handleWapPushReceived(intent, error); } else if (ACTION_BOOT_COMPLETED.equals(action)) { handleBootCompleted(); } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) { handleServiceStateChanged(intent); } else if (ACTION_SEND_MESSAGE.endsWith(action)) { handleSendMessage(); } } // 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, serviceId); } } private void handleServiceStateChanged(Intent intent) { // If service just returned, start sending out the queued messages ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras()); int phoneId = intent.getIntExtra(Phone.PHONE_ID, -1); if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) { sendFirstQueuedMessage(phoneId); } } private void handleSendMessage() { if (!mSending) { sendFirstQueuedMessage(); } } public synchronized void sendFirstQueuedMessage(int selectionPhoneId) { boolean success = true; String selection = null; if(selectionPhoneId != -1){ selection = Sms.PHONE_ID + " = " + selectionPhoneId; } // get all the queued messages from the database final Uri uri = Uri.parse("content://sms/queued"); ContentResolver resolver = getContentResolver(); Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, selection, null, "date ASC"); // date ASC so we send out in // same order the user tried // to send messages. if (c != null) { try { if (c.moveToFirst()) { String msgText = c.getString(SEND_COLUMN_BODY); String address = c.getString(SEND_COLUMN_ADDRESS); int threadId = c.getInt(SEND_COLUMN_THREAD_ID); int status = c.getInt(SEND_COLUMN_STATUS); int phoneId = c.getInt(SEND_COLUMN_PHONE_ID); int msgId = c.getInt(SEND_COLUMN_ID); Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId); // sunway:last chance to compute a phoneId used to send the SmsMessageSender sender = new SmsSingleRecipientSender(this, address, msgText, threadId, status == Sms.STATUS_PENDING, msgUri, phoneId ); if (LogTag.VERBOSE || DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "sendFirstQueuedMessage " + msgUri + ", address: " + address + ", threadId: " + threadId); } try { sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);; mSending = true; } catch (MmsException e) { Log.e(TAG, "sendFirstQueuedMessage: failed to send message " + msgUri + ", caught ", e); mSending = false; messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE); success = false; } } } finally { c.close(); } } if (success) { // We successfully sent all the messages in the queue. We don't need to // be notified of any service changes any longer. unRegisterForServiceStateChanges(); } } public synchronized void sendFirstQueuedMessage() { boolean success = true; // get all the queued messages from the database final Uri uri = Uri.parse("content://sms/queued"); ContentResolver resolver = getContentResolver(); Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, null, null, "date ASC"); // date ASC so we send out in // same order the user tried // to send messages. if (c != null) { try { if (c.moveToFirst()) { String msgText = c.getString(SEND_COLUMN_BODY); String address = c.getString(SEND_COLUMN_ADDRESS); int threadId = c.getInt(SEND_COLUMN_THREAD_ID); int status = c.getInt(SEND_COLUMN_STATUS); int phoneId = c.getInt(SEND_COLUMN_PHONE_ID); int msgId = c.getInt(SEND_COLUMN_ID); Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId); // sunway:last chance to compute a phoneId used to send the SmsMessageSender sender = new SmsSingleRecipientSender(this, address, msgText, threadId, status == Sms.STATUS_PENDING, msgUri, phoneId ); if (LogTag.VERBOSE || DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "sendFirstQueuedMessage " + msgUri + ", address: " + address + ", threadId: " + threadId); } try { sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);; mSending = true; } catch (MmsException e) { Log.e(TAG, "sendFirstQueuedMessage: failed to send message " + msgUri + ", caught ", e); mSending = false; messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE); success = false; } } } finally { c.close(); } } if (success) { // We successfully sent all the messages in the queue. We don't need to // be notified of any service changes any longer. unRegisterForServiceStateChanges(); } } private void handleSmsSent(Intent intent, int error) { Uri uri = intent.getData(); mSending = false; boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false); if (mResultCode == Activity.RESULT_OK) { if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "handleSmsSent sending uri: " + uri); } if (sendNextMsg) { if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) { Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder"); } sendFirstQueuedMessage(); } // Update the notification for failed messages since they may be deleted. MessagingNotification.updateSendFailedNotification(this); } else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) || (mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) { if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri: " + uri); } // We got an error with no service or no radio. Register for state changes so // when the status of the connection/radio changes, we can try to send the // queued up messages. registerForServiceStateChanges(); // We couldn't send the message, put in the queue to retry later. Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error); mToastHandler.post(new Runnable() { public void run() { Toast.makeText(SmsReceiverService.this, getString(R.string.message_queued), Toast.LENGTH_SHORT).show(); } }); } else if (mResultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE) { mToastHandler.post(new Runnable() { public void run() { Toast.makeText(SmsReceiverService.this, getString(R.string.fdn_check_failure), Toast.LENGTH_SHORT).show(); } }); } else { messageFailedToSend(uri, error); if (sendNextMsg) { sendFirstQueuedMessage(); } } } private void messageFailedToSend(Uri uri, int error) { if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "messageFailedToSend msg failed uri: " + uri); } Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_FAILED, error); MessagingNotification.notifySendFailed(getApplicationContext(), true); } private void handleSmsReceived(Intent intent, int error) { SmsMessage[] msgs = Intents.getMessagesFromIntent(intent); int id=intent.getIntExtra("phone_id",-1); Uri messageUri = insertMessage(this, msgs, error,id); if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { SmsMessage sms = msgs[0]; Log.v(TAG, "handleSmsReceived" + (sms.isReplace() ? "(replace)" : "") + " messageUri: " + messageUri + ", address: " + sms.getOriginatingAddress() + ", body: " + sms.getMessageBody()); } if (messageUri != null) { // Called off of the UI thread so ok to block. MessagingNotification.blockingUpdateNewMessageIndicator(this, true, false); } } private void handleWapPushReceived(Intent intent, int error) { String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic"; String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc"; String pushBody; String expired = ""; String action = ""; String si_id = ""; String href = ""; byte[] pushData = intent.getByteArrayExtra("data"); WapPushParser push_parser = new WapPushParser(pushData); WapPushMsg pushMsg = null; if (CONTENT_MIME_TYPE_B_PUSH_SI.equals(intent.getType())) { Log.i(TAG,"wap push create SI parser"); pushMsg = push_parser.parse(WapPushMsg.WAP_PUSH_TYPE_SI); if (null == pushMsg) return; href = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_HREF); String sitext = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_SI_TEXT); expired = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_SI_EXPIRED); action = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_PRIOR); si_id = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_SI_ID); Log.i(TAG, "wap push ====> si_id:"+si_id+",action:"+action+",expired:"+expired+",href:"+href+",sitext:"+sitext); pushBody = sitext+"\n Url:"+href; } else if (CONTENT_MIME_TYPE_B_PUSH_SL.equals(intent.getType())){ Log.i(TAG,"wap push create SL parser"); pushMsg = push_parser.parse(WapPushMsg.WAP_PUSH_TYPE_SL); if (null == pushMsg) return; href = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_HREF); action = pushMsg.getAttributeValueString(WapPushMsg.WAP_PUSH_PROJECTION_PRIOR); pushBody = "\n Url:"+href; } else { Log.i(TAG,"wap push non support type:"+intent.getType()); return; } //test // Intent sendintent = new Intent("android.intent.action.ShowWapPush"); // sendintent.setClass(this, WapPushMessageShowActivity.class); // if(tempstr.contains("1")){ // sendintent.putExtra("abc", 1); // }else if(tempstr.contains("2")){ // sendintent.putExtra("abc", 2); // } // // sendintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); // this.startActivity(sendintent); //test // do something depend on action boolean isNeedNotify = false; boolean isNeedStore = false; int actiontype = WapPushParser.getPushAttrValue(action); // actiontype = 11;//for test // si_id = "202.108.22.5"; if( actiontype == WapPushMsg.WAP_PUSH_PRIO_NONE.intValue()){ isNeedNotify = false; isNeedStore = false; } else if(actiontype == WapPushMsg.WAP_PUSH_PRIO_LOW.intValue()||actiontype == WapPushMsg.WAP_PUSH_SL_PRIO_CACHE.intValue()){ isNeedNotify = false; isNeedStore = true; } else if(actiontype == WapPushMsg.WAP_PUSH_PRIO_MEDIUM.intValue()){ isNeedNotify = true; isNeedStore = true; } else if(actiontype == WapPushMsg.WAP_PUSH_PRIO_HIGH.intValue() ||actiontype == WapPushMsg.WAP_PUSH_SL_PRIO_LOW.intValue() ||actiontype == WapPushMsg.WAP_PUSH_SL_PRIO_HIGH.intValue()){ isNeedNotify = true; isNeedStore = true; //need show wap push message on dialog Intent sendintent = new Intent("android.intent.action.ShowWapPush"); sendintent.setClass(this, WapPushMessageShowActivity.class); sendintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); sendintent.putExtra("href", href); sendintent.putExtra("pushBody", pushBody); this.startActivity(sendintent); } else if(actiontype == WapPushMsg.WAP_PUSH_PRIO_DELETE.intValue()){ isNeedNotify = false; isNeedStore = false; deleteWapPushMessageBySIID(si_id); } Log.i(TAG,"wap push body = "+pushBody); Uri messageUri = null; if(isNeedStore){ SmsMessage[] msgs = Intents.getMessagesFromIntent(intent); messageUri = storePushMessage(this, msgs, pushBody, error, expired, si_id); } Log.d(TAG,"wap push messageUri = "+messageUri+",isNeedNotify="+isNeedNotify); if (messageUri != null && isNeedNotify) { // Called off of the UI thread so ok to block. Log.i(TAG,"wap push messageUri = "+messageUri); MessagingNotification.blockingUpdateNewMessageIndicator(this, true, false); } } private void deleteWapPushMessageBySIID(String si_id){ Log.d(TAG, "delete wap push message by si_id ='"+si_id+"'"); if(si_id == null || si_id.trim().length() <= 0){ return; } ContentResolver cr = this.getContentResolver(); int deleteCount = 0; try { deleteCount = cr.delete(Sms.CONTENT_URI, " si_id = '"+si_id+"'", null); } catch (Exception e) { Log.e(TAG, "process deleteWapPushMessageBySIID happened exception!",e); }finally{ } } private Uri storePushMessage(Context context, SmsMessage[] msgs, String pushBody, int error, String expired, String si_id) { SmsMessage sms = msgs[0]; // Store the message in the content provider. ContentValues values = extractContentValues(sms); values.put(Sms.ERROR_CODE, error); // int pduCount = msgs.length; // if (pduCount == 1) { // // There is only one part, so grab the body directly. // values.put(Inbox.BODY, sms.getDisplayMessageBody()); // } else { // // Build up the body from the parts. // StringBuilder body = new StringBuilder(); // for (int i = 0; i < pduCount; i++) { // sms = msgs[i]; // body.append(sms.getDisplayMessageBody()); // } // values.put(Inbox.BODY, body.toString()); // } values.put(Inbox.BODY, pushBody); values.put("wap_push", 1); values.put("expired", expired); values.put("si_id", si_id); // Make sure we've got a thread id so after the insert we'll be able to delete // excess messages. Long threadId = values.getAsLong(Sms.THREAD_ID); String address = values.getAsString(Sms.ADDRESS); String name = ""; Contact cacheContact = Contact.get(address,true); if (cacheContact != null) { address = cacheContact.getNumber(); name = cacheContact.getName(); } if (((threadId == null) || (threadId == 0)) && (address != null)) { threadId = Threads.getOrCreateThreadId(context, address, name); values.put(Sms.THREAD_ID, threadId); } ContentResolver resolver = context.getContentResolver(); Uri insertedUri = SqliteWrapper.insert(context, resolver, Inbox.CONTENT_URI, values); // Now make sure we're not over the limit in stored messages Recycler.getSmsRecycler().deleteOldMessagesByThreadId(getApplicationContext(), threadId); return insertedUri; } private void handleBootCompleted() { moveOutboxMessagesToQueuedBox(); sendFirstQueuedMessage(); // Called off of the UI thread so ok to block. MessagingNotification.blockingUpdateNewMessageIndicator(this, true, false); } private void moveOutboxMessagesToQueuedBox() { ContentValues values = new ContentValues(1); values.put(Sms.TYPE, Sms.MESSAGE_TYPE_QUEUED); SqliteWrapper.update( getApplicationContext(), getContentResolver(), Outbox.CONTENT_URI, values, "type = " + Sms.MESSAGE_TYPE_OUTBOX, null); } public static final String CLASS_ZERO_BODY_KEY = "CLASS_ZERO_BODY"; // This must match the column IDs below. private final static String[] REPLACE_PROJECTION = new String[] { Sms._ID, Sms.ADDRESS, Sms.PROTOCOL }; // This must match REPLACE_PROJECTION. private static final int REPLACE_COLUMN_ID = 0; /** * If the message is a class-zero message, display it immediately * and return null. Otherwise, store it using the * <code>ContentResolver</code> and return the * <code>Uri</code> of the thread containing this message * so that we can use it for notification. */ private Uri insertMessage(Context context, SmsMessage[] msgs, int error,int phoneId) { // Build the helper classes to parse the messages. SmsMessage sms = msgs[0]; if (sms.getMessageClass() == SmsMessage.MessageClass.CLASS_0) { displayClassZeroMessage(context, sms); return null; } else if (sms.isReplace()) { Log.d(TAG, "[cmgw]enter replaceMessage"); return replaceMessage(context, msgs, error,phoneId); } else { Log.d(TAG, "[cmgw]enter storeMessage"); return storeMessage(context, msgs, error,phoneId); } } /** * This method is used if this is a "replace short message" SMS. * We find any existing message that matches the incoming * message's originating address and protocol identifier. If * there is one, we replace its fields with those of the new * message. Otherwise, we store the new message as usual. * * See TS 23.040 9.2.3.9. */ private Uri replaceMessage(Context context, SmsMessage[] msgs, int error,int phoneId) { SmsMessage sms = msgs[0]; ContentValues values = extractContentValues(sms); values.put(Inbox.BODY, sms.getMessageBody()); values.put(Sms.ERROR_CODE, error); values.put(Sms.PHONE_ID, phoneId); ContentResolver resolver = context.getContentResolver(); String originatingAddress = sms.getOriginatingAddress(); int protocolIdentifier = sms.getProtocolIdentifier(); String selection = Sms.ADDRESS + " = ? AND " + Sms.PROTOCOL + " = ?"; String[] selectionArgs = new String[] { originatingAddress, Integer.toString(protocolIdentifier) }; Cursor cursor = SqliteWrapper.query(context, resolver, Inbox.CONTENT_URI, REPLACE_PROJECTION, selection, selectionArgs, null); if (cursor != null) { try { if (cursor.moveToFirst()) { long messageId = cursor.getLong(REPLACE_COLUMN_ID); Uri messageUri = ContentUris.withAppendedId( Sms.CONTENT_URI, messageId); SqliteWrapper.update(context, resolver, messageUri, values, null, null); return messageUri; } } finally { cursor.close(); } } return storeMessage(context, msgs, error,phoneId); } private Uri storeMessage(Context context, SmsMessage[] msgs, int error,int phoneId) { SmsMessage sms = msgs[0]; boolean isClass2 = (sms.getMessageClass() == SmsMessage.MessageClass.CLASS_2) ? true : false; // Store the message in the content provider. ContentValues values = extractContentValues(sms); values.put(Sms.ERROR_CODE, error); values.put(Sms.PHONE_ID, phoneId); int pduCount = msgs.length; if (pduCount == 1) { // There is only one part, so grab the body directly. values.put(Inbox.BODY, sms.getDisplayMessageBody()); } else { // Build up the body from the parts. StringBuilder body = new StringBuilder(); for (int i = 0; i < pduCount; i++) { sms = msgs[i]; body.append(sms.getDisplayMessageBody()); } values.put(Inbox.BODY, body.toString()); } // Make sure we've got a thread id so after the insert we'll be able to delete // excess messages. Long threadId = values.getAsLong(Sms.THREAD_ID); String address = values.getAsString(Sms.ADDRESS); String name = ""; if (!TextUtils.isEmpty(address)) { Contact cacheContact = Contact.get(address,true); if (cacheContact != null) { address = cacheContact.getNumber(); name = cacheContact.getName(); } } else { address = getString(R.string.unknown_sender); values.put(Sms.ADDRESS, address); } if (((threadId == null) || (threadId == 0)) && (address != null)) { threadId = Threads.getOrCreateThreadId(context, address, name); values.put(Sms.THREAD_ID, threadId); } // Check needs to be saved to SIM card SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean saveSimcard = prefs.getBoolean( MessagingPreferenceActivity.SMS_SAVE_TO_SIMCARD, false); Log.d(TAG, "[cmgw]need to save sim card =" + saveSimcard + " isClass2 = " + isClass2); if (saveSimcard && !isClass2) { if (!MessageUtils.isSimMemFull(phoneId)) { Log.d(TAG, "[cmgw]save to sim memory"); saveMessageToSim(address, values.getAsString(Sms.BODY), values.getAsLong(Sms.DATE),phoneId); } else { Log.d(TAG, "[cmgw]sim mem full"); Intent intent = new Intent(Intents.SIM_FULL_ACTION); intent.putExtra(Phone.PHONE_ID, phoneId); context.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); } } ContentResolver resolver = context.getContentResolver(); Uri insertedUri = SqliteWrapper.insert(context, resolver, Inbox.CONTENT_URI, values); // Now make sure we're not over the limit in stored messages Recycler.getSmsRecycler().deleteOldMessagesByThreadId(getApplicationContext(), threadId); // added for MessageFolderActivity to refresh Intent intent = new Intent("android.permission.SAVE_SMS_TO_DATABASE"); intent.putExtra(Phone.PHONE_ID, phoneId); context.sendBroadcast(intent); return insertedUri; } private void saveMessageToSim(String address, String mbody, long date,int phoneId) { boolean result = false; String timeString = null; String bcdtimeString = null; SmsManager smsManager = SmsManager.getDefault(phoneId); ArrayList<String> messages = null; messages = smsManager.divideMessage(mbody); Time t = new Time(); t.set(date); timeString = t.format("%g%m%d%H%M%S"); Log.d(TAG, "[cmgw]timeString =" + timeString); // byte[] timebcd = PhoneNumberUtils.numberToCalledPartyBCD(timeString); // Log.d(TAG, "[cmgw]bcdlen ="+timebcd.length+" timelen =" + timeString.length()); // if (timebcd.length > timeString.length()/2) { // int dataIndex = 1; // byte[] data = new byte[timebcd.length - dataIndex]; // System.arraycopy(timebcd, dataIndex, data, 0, data.length); // bcdtimeString = IccUtils.bytesToHexString(data) + "00"; // } else { // bcdtimeString = IccUtils.bytesToHexString(timebcd) + "00"; // } byte[] timebcd = MessageUtils.GetSctsTime(t); bcdtimeString = IccUtils.bytesToHexString(timebcd); Log.d(TAG, "[cmgw]bcd timeString = " + bcdtimeString); result = smsManager.saveMultipartTextMessage(address, messages, false, bcdtimeString); Log.d(TAG, "[cmgw]save result =" + result); /*****************begin*************/ //if result = true represent successful store to the simcard,then send a broadcast to update UI if (result) { final String mSimStoreAction = "android.provide.STORE_SIMCARD_SUCESS"; Intent mIntent = new Intent(mSimStoreAction); this.sendBroadcast(mIntent); } /******************** end **********/ } /** * Extract all the content values except the body from an SMS * message. */ private ContentValues extractContentValues(SmsMessage sms) { // Store the message in the content provider. ContentValues values = new ContentValues(); values.put(Inbox.ADDRESS, sms.getDisplayOriginatingAddress()); // Use now for the timestamp to avoid confusion with clock // drift between the handset and the SMSC. values.put(Inbox.DATE, new Long(System.currentTimeMillis())); values.put(Inbox.PROTOCOL, sms.getProtocolIdentifier()); values.put(Inbox.READ, 0); values.put(Inbox.SEEN, 0); if (sms.getPseudoSubject().length() > 0) { values.put(Inbox.SUBJECT, sms.getPseudoSubject()); } values.put(Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0); values.put(Inbox.SERVICE_CENTER, sms.getServiceCenterAddress()); return values; } /** * Displays a class-zero message immediately in a pop-up window * with the number from where it received the Notification with * the body of the message * */ private void displayClassZeroMessage(Context context, SmsMessage sms) { // Using NEW_TASK here is necessary because we're calling // startActivity from outside an activity. Intent smsDialogIntent = new Intent(context, ClassZeroActivity.class) .putExtra("pdu", sms.getPdu()) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); context.startActivity(smsDialogIntent); MessagingNotification.classZeroNotification(context); } private void registerForServiceStateChanges() { Context context = getApplicationContext(); unRegisterForServiceStateChanges(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "registerForServiceStateChanges"); } context.registerReceiver(SmsReceiver.getInstance(), intentFilter); } private void unRegisterForServiceStateChanges() { if (DEBUG || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "unRegisterForServiceStateChanges"); } try { Context context = getApplicationContext(); context.unregisterReceiver(SmsReceiver.getInstance()); } catch (IllegalArgumentException e) { // Allow un-matched register-unregister calls } } }