FileDocCategorySizeDatePackage
SmsReceiverService.javaAPI DocAndroid 1.5 API15249Wed May 06 22:42:46 BST 2009com.android.mms.transaction

SmsReceiverService

public class SmsReceiverService extends android.app.Service
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.

Fields Summary
private static final String
TAG
private ServiceHandler
mServiceHandler
private android.os.Looper
mServiceLooper
public static final String
MESSAGE_SENT_ACTION
private static final String[]
SEND_PROJECTION
public android.os.Handler
mToastHandler
private static final int
SEND_COLUMN_ID
private static final int
SEND_COLUMN_THREAD_ID
private static final int
SEND_COLUMN_ADDRESS
private static final int
SEND_COLUMN_BODY
private int
mResultCode
public static final String
CLASS_ZERO_BODY_KEY
public static final String
CLASS_ZERO_TITLE_KEY
public static final int
NOTIFICATION_NEW_MESSAGE
private static final String[]
REPLACE_PROJECTION
private static final int
REPLACE_COLUMN_ID
Constructors Summary
Methods Summary
private voiddisplayClassZeroMessage(android.content.Context context, android.telephony.gsm.SmsMessage sms)
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

        // Using NEW_TASK here is necessary because we're calling
        // startActivity from outside an activity.
        Intent smsDialogIntent = new Intent(context, ClassZeroActivity.class)
                .putExtra(CLASS_ZERO_BODY_KEY, sms.getMessageBody())
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                          | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);

        context.startActivity(smsDialogIntent);
    
private android.content.ContentValuesextractContentValues(android.telephony.gsm.SmsMessage sms)
Extract all the content values except the body from an SMS message.

        // 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, Integer.valueOf(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;
    
private voidhandleBootCompleted()

        moveOutboxMessagesToQueuedBox();
        sendFirstQueuedMessage();
        MessagingNotification.updateNewMessageIndicator(this);
    
private voidhandleServiceStateChanged(android.content.Intent intent)

        // If service just returned, start sending out the queued messages
        ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
        if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
            sendFirstQueuedMessage();
        }
    
private voidhandleSmsReceived(android.content.Intent intent)

        SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
        Uri messageUri = insertMessage(this, msgs);

        if (messageUri != null) {
            MessagingNotification.updateNewMessageIndicator(this, true);
        }
    
private voidhandleSmsSent(android.content.Intent intent)

        Uri uri = intent.getData();

        if (mResultCode == Activity.RESULT_OK) {
            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT);
            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)) {
            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED);
            mToastHandler.sendEmptyMessage(1);
        } else {
            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_FAILED);
            MessagingNotification.notifySendFailed(getApplicationContext(), true);
            sendFirstQueuedMessage();
        }
    
private android.net.UriinsertMessage(android.content.Context context, android.telephony.gsm.SmsMessage[] msgs)
If the message is a class-zero message, display it immediately and return null. Otherwise, store it using the ContentResolver and return the Uri of the thread containing this message so that we can use it for notification.


                                               
          
        // 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()) {
            return replaceMessage(context, msgs);
        } else {
            return storeMessage(context, msgs);
        }
    
private voidmoveOutboxMessagesToQueuedBox()

        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 android.os.IBinderonBind(android.content.Intent intent)

        return null;
    
public voidonCreate()


    
       
        if (Log.isLoggable(MmsApp.LOG_TAG, 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.
        HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    
public voidonDestroy()

        if (Log.isLoggable(MmsApp.LOG_TAG, Log.VERBOSE)) {
            Log.v(TAG, "onDestroy");
        }
        mServiceLooper.quit();
    
public voidonStart(android.content.Intent intent, int startId)

        if (Log.isLoggable(MmsApp.LOG_TAG, Log.VERBOSE)) {
            Log.v(TAG, "onStart: #" + startId + ": " + intent.getExtras());
        }

        mResultCode = intent.getIntExtra("result", 0);

        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    
private android.net.UrireplaceMessage(android.content.Context context, android.telephony.gsm.SmsMessage[] msgs)
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.

        SmsMessage sms = msgs[0];
        ContentValues values = extractContentValues(sms);

        values.put(Inbox.BODY, sms.getMessageBody());

        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);
    
public synchronized voidsendFirstQueuedMessage()

        // 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, null);

        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    int msgId = c.getInt(SEND_COLUMN_ID);
                    String msgText = c.getString(SEND_COLUMN_BODY);
                    String[] address = new String[1];
                    address[0] = c.getString(SEND_COLUMN_ADDRESS);
                    int threadId = c.getInt(SEND_COLUMN_THREAD_ID);

                    SmsMessageSender sender = new SmsMessageSender(this,
                            address, msgText, threadId);
                    try {
                        sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);

                        // Since sendMessage adds a new message to the outbox rather than
                        // moving the old one, the old one must be deleted here
                        Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId);
                        SqliteWrapper.delete(this, resolver, msgUri, null, null);
                    } catch (MmsException e) {
                        Log.e(TAG, "Failed to send message: " + e);
                    }
                }
            } finally {
                c.close();
            }
        }
    
private android.net.UristoreMessage(android.content.Context context, android.telephony.gsm.SmsMessage[] msgs)

        SmsMessage sms = msgs[0];

        // Store the message in the content provider.
        ContentValues values = extractContentValues(sms);
        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());
        }

        ContentResolver resolver = context.getContentResolver();

        return SqliteWrapper.insert(context, resolver, Inbox.CONTENT_URI, values);