SmsReceiverServicepublic 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 |
Methods Summary |
---|
private void | displayClassZeroMessage(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.ContentValues | extractContentValues(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 void | handleBootCompleted()
moveOutboxMessagesToQueuedBox();
sendFirstQueuedMessage();
MessagingNotification.updateNewMessageIndicator(this);
| private void | handleServiceStateChanged(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 void | handleSmsReceived(android.content.Intent intent)
SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
Uri messageUri = insertMessage(this, msgs);
if (messageUri != null) {
MessagingNotification.updateNewMessageIndicator(this, true);
}
| private void | handleSmsSent(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.Uri | insertMessage(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 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 android.os.IBinder | onBind(android.content.Intent intent)
return null;
| public void | onCreate()
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 void | onDestroy()
if (Log.isLoggable(MmsApp.LOG_TAG, Log.VERBOSE)) {
Log.v(TAG, "onDestroy");
}
mServiceLooper.quit();
| public void | onStart(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.Uri | replaceMessage(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 void | sendFirstQueuedMessage()
// 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.Uri | storeMessage(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);
|
|