InboundSmsHandlerpublic abstract class InboundSmsHandler extends com.android.internal.util.StateMachine This class broadcasts incoming SMS messages to interested apps after storing them in
the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
broadcast, its parts are removed from the raw table. If the device crashes after ACKing
but before the broadcast completes, the pending messages will be rebroadcast on the next boot.
The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a
new SMS from the radio, it calls {@link #dispatchNormalMessage},
which sends a message to the state machine, causing the wakelock to be acquired in
{@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message
is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us.
After saving the SMS, if the message is complete (either single-part or the final segment
of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
{@link WaitingState} state to wait for the broadcast to complete. When the local
{@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
to the state machine, causing us to either broadcast the next pending message (if one has
arrived while waiting for the broadcast to complete), or to transition back to the halted state
after all messages are processed. Then the wakelock is released and we wait for the next SMS. |
Fields Summary |
---|
protected static final boolean | DBG | private static final boolean | VDBG | private static final String[] | PDU_PROJECTIONQuery projection for checking for duplicate message segments. | private static final String[] | PDU_SEQUENCE_PORT_PROJECTIONQuery projection for combining concatenated message segments. | static final int | PDU_COLUMN | static final int | SEQUENCE_COLUMN | static final int | DESTINATION_PORT_COLUMN | static final int | DATE_COLUMN | static final int | REFERENCE_NUMBER_COLUMN | static final int | COUNT_COLUMN | static final int | ADDRESS_COLUMN | static final int | ID_COLUMN | static final String | SELECT_BY_ID | static final String | SELECT_BY_REFERENCE | public static final int | EVENT_NEW_SMSNew SMS received as an AsyncResult. | static final int | EVENT_BROADCAST_SMSMessage type containing a {@link InboundSmsTracker} ready to broadcast to listeners. | static final int | EVENT_BROADCAST_COMPLETEMessage from resultReceiver notifying {@link WaitingState} of a completed broadcast. | static final int | EVENT_RETURN_TO_IDLESent on exit from {@link WaitingState} to return to idle after sending all broadcasts. | static final int | EVENT_RELEASE_WAKELOCKRelease wakelock after a short timeout when returning to idle state. | static final int | EVENT_START_ACCEPTING_SMSSent by {@link SmsBroadcastUndelivered} after cleaning the raw table. | static final int | EVENT_UPDATE_PHONE_OBJECTUpdate phone object | public static final int | EVENT_INJECT_SMSNew SMS received as an AsyncResult. | private static final int | WAKELOCK_TIMEOUTWakelock release delay when returning to idle state. | private static final android.net.Uri | sRawUriURI for raw table of SMS provider. | protected final android.content.Context | mContext | private final android.content.ContentResolver | mResolver | private final WapPushOverSms | mWapPushSpecial handler for WAP push messages. | final PowerManager.WakeLock | mWakeLockWake lock to ensure device stays awake while dispatching the SMS intents. | final DefaultState | mDefaultStateDefaultState throws an exception or logs an error for unhandled message types. | final StartupState | mStartupStateStartup state. Waiting for {@link SmsBroadcastUndelivered} to complete. | final IdleState | mIdleStateIdle state. Waiting for messages to process. | final DeliveringState | mDeliveringStateDelivering state. Saves the PDU in the raw table and acknowledges to SMSC. | final WaitingState | mWaitingStateBroadcasting state. Waits for current broadcast to complete before delivering next. | protected SmsStorageMonitor | mStorageMonitorHelper class to check whether storage is available for incoming messages. | private final boolean | mSmsReceiveDisabled | protected PhoneBase | mPhone | protected CellBroadcastHandler | mCellBroadcastHandler | private android.os.UserManager | mUserManager |
Constructors Summary |
---|
protected InboundSmsHandler(String name, android.content.Context context, SmsStorageMonitor storageMonitor, PhoneBase phone, CellBroadcastHandler cellBroadcastHandler)Create a new SMS broadcast helper.
super(name);
mContext = context;
mStorageMonitor = storageMonitor;
mPhone = phone;
mCellBroadcastHandler = cellBroadcastHandler;
mResolver = context.getContentResolver();
mWapPush = new WapPushOverSms(context);
boolean smsCapable = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sms_capable);
mSmsReceiveDisabled = !TelephonyManager.from(mContext).getSmsReceiveCapableForPhone(
mPhone.getPhoneId(), smsCapable);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
mWakeLock.acquire(); // wake lock released after we enter idle state
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
addState(mDefaultState);
addState(mStartupState, mDefaultState);
addState(mIdleState, mDefaultState);
addState(mDeliveringState, mDefaultState);
addState(mWaitingState, mDeliveringState);
setInitialState(mStartupState);
if (DBG) log("created InboundSmsHandler");
|
Methods Summary |
---|
protected abstract void | acknowledgeLastIncomingSms(boolean success, int result, android.os.Message response)Send an acknowledge message to the SMSC.
| private int | addTrackerToRawTable(InboundSmsTracker tracker)Insert a message PDU into the raw table so we can acknowledge it immediately.
If the device crashes before the broadcast to listeners completes, it will be delivered
from the raw table on the next device boot. For single-part messages, the deleteWhere
and deleteWhereArgs fields of the tracker will be set to delete the correct row after
the ordered broadcast completes.
if (tracker.getMessageCount() != 1) {
// check for duplicate message segments
Cursor cursor = null;
try {
// sequence numbers are 1-based except for CDMA WAP, which is 0-based
int sequence = tracker.getSequenceNumber();
// convert to strings for query
String address = tracker.getAddress();
String refNumber = Integer.toString(tracker.getReferenceNumber());
String count = Integer.toString(tracker.getMessageCount());
String seqNumber = Integer.toString(sequence);
// set the delete selection args for multi-part message
String[] deleteWhereArgs = {address, refNumber, count};
tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
// Check for duplicate message segments
cursor = mResolver.query(sRawUri, PDU_PROJECTION,
"address=? AND reference_number=? AND count=? AND sequence=?",
new String[] {address, refNumber, count, seqNumber}, null);
// moveToNext() returns false if no duplicates were found
if (cursor.moveToNext()) {
loge("Discarding duplicate message segment, refNumber=" + refNumber
+ " seqNumber=" + seqNumber);
String oldPduString = cursor.getString(PDU_COLUMN);
byte[] pdu = tracker.getPdu();
byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
if (!Arrays.equals(oldPdu, tracker.getPdu())) {
loge("Warning: dup message segment PDU of length " + pdu.length
+ " is different from existing PDU of length " + oldPdu.length);
}
return Intents.RESULT_SMS_DUPLICATED; // reject message
}
cursor.close();
} catch (SQLException e) {
loge("Can't access multipart SMS database", e);
return Intents.RESULT_SMS_GENERIC_ERROR; // reject message
} finally {
if (cursor != null) {
cursor.close();
}
}
}
ContentValues values = tracker.getContentValues();
if (VDBG) log("adding content values to raw table: " + values.toString());
Uri newUri = mResolver.insert(sRawUri, values);
if (DBG) log("URI of new row -> " + newUri);
try {
long rowId = ContentUris.parseId(newUri);
if (tracker.getMessageCount() == 1) {
// set the delete selection args for single-part message
tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)});
}
return Intents.RESULT_SMS_HANDLED;
} catch (Exception e) {
loge("error parsing URI for new row: " + newUri, e);
return Intents.RESULT_SMS_GENERIC_ERROR;
}
| protected int | addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker)Helper to add the tracker to the raw table and then send a message to broadcast it, if
successful. Returns the SMS intent status to return to the SMSC.
switch(addTrackerToRawTable(tracker)) {
case Intents.RESULT_SMS_HANDLED:
sendMessage(EVENT_BROADCAST_SMS, tracker);
return Intents.RESULT_SMS_HANDLED;
case Intents.RESULT_SMS_DUPLICATED:
return Intents.RESULT_SMS_HANDLED;
case Intents.RESULT_SMS_GENERIC_ERROR:
default:
return Intents.RESULT_SMS_GENERIC_ERROR;
}
| private static java.lang.String | buildMessageBodyFromPdus(android.telephony.SmsMessage[] msgs)Build up the SMS message body from the SmsMessage array of received SMS
if (msgs.length == 1) {
// There is only one part, so grab the body directly.
return replaceFormFeeds(msgs[0].getDisplayMessageBody());
} else {
// Build up the body from the parts.
StringBuilder body = new StringBuilder();
for (SmsMessage msg: msgs) {
// getDisplayMessageBody() can NPE if mWrappedMessage inside is null.
body.append(msg.getDisplayMessageBody());
}
return replaceFormFeeds(body.toString());
}
| void | deleteFromRawTable(java.lang.String deleteWhere, java.lang.String[] deleteWhereArgs)Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table.
int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs);
if (rows == 0) {
loge("No rows were deleted from raw table!");
} else if (DBG) {
log("Deleted " + rows + " rows from raw table.");
}
| protected void | dispatchIntent(android.content.Intent intent, java.lang.String permission, int appOp, android.content.BroadcastReceiver resultReceiver, android.os.UserHandle user)Dispatch the intent with the specified permission, appOp, and result receiver, using
this state machine's handler thread to run the result receiver.
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
if (user.equals(UserHandle.ALL)) {
// Get a list of currently started users.
int[] users = null;
try {
users = ActivityManagerNative.getDefault().getRunningUserIds();
} catch (RemoteException re) {
}
if (users == null) {
users = new int[] {user.getIdentifier()};
}
// Deliver the broadcast only to those running users that are permitted
// by user policy.
for (int i = users.length - 1; i >= 0; i--) {
UserHandle targetUser = new UserHandle(users[i]);
if (users[i] != UserHandle.USER_OWNER) {
// Is the user not allowed to use SMS?
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS, targetUser)) {
continue;
}
// Skip unknown users and managed profiles as well
UserInfo info = mUserManager.getUserInfo(users[i]);
if (info == null || info.isManagedProfile()) {
continue;
}
}
// Only pass in the resultReceiver when the USER_OWNER is processed.
mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp,
users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
getHandler(), Activity.RESULT_OK, null, null);
}
} else {
mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp,
resultReceiver,
getHandler(), Activity.RESULT_OK, null, null);
}
| public int | dispatchMessage(SmsMessageBase smsb)Process an SMS message from the RIL, calling subclass methods to handle 3GPP and
3GPP2-specific message types.
// If sms is null, there was a parsing error.
if (smsb == null) {
loge("dispatchSmsMessage: message is null");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
if (mSmsReceiveDisabled) {
// Device doesn't support receiving SMS,
log("Received short message on device which doesn't support "
+ "receiving SMS. Ignored.");
return Intents.RESULT_SMS_HANDLED;
}
return dispatchMessageRadioSpecific(smsb);
| protected abstract int | dispatchMessageRadioSpecific(SmsMessageBase smsb)Process voicemail notification, SMS-PP data download, CDMA CMAS, CDMA WAP push, and other
3GPP/3GPP2-specific messages. Regular SMS messages are handled by calling the shared
{@link #dispatchNormalMessage} from this class.
| protected int | dispatchNormalMessage(SmsMessageBase sms)Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific}
if no format-specific handling was required. Saves the PDU to the SMS provider raw table,
creates an {@link InboundSmsTracker}, then sends it to the state machine as an
{@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value.
SmsHeader smsHeader = sms.getUserDataHeader();
InboundSmsTracker tracker;
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
// Message is not concatenated.
int destPort = -1;
if (smsHeader != null && smsHeader.portAddrs != null) {
// The message was sent to a port.
destPort = smsHeader.portAddrs.destPort;
if (DBG) log("destination port: " + destPort);
}
tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
is3gpp2(), false);
} else {
// Create a tracker for this message segment.
SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
int destPort = (portAddrs != null ? portAddrs.destPort : -1);
tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber,
concatRef.seqNumber, concatRef.msgCount, false);
}
if (VDBG) log("created tracker: " + tracker);
return addTrackerToRawTableAndSendMessage(tracker);
| void | dispatchSmsDeliveryIntent(byte[][] pdus, java.lang.String format, int destPort, android.content.BroadcastReceiver resultReceiver)Creates and dispatches the intent to the default SMS app or the appropriate port.
Intent intent = new Intent();
intent.putExtra("pdus", pdus);
intent.putExtra("format", format);
if (destPort == -1) {
intent.setAction(Intents.SMS_DELIVER_ACTION);
// Direct the intent to only the default SMS app. If we can't find a default SMS app
// then sent it to all broadcast receivers.
// We are deliberately delivering to the primary user's default SMS App.
ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
if (componentName != null) {
// Deliver SMS message only to this receiver.
intent.setComponent(componentName);
log("Delivering SMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
} else {
intent.setComponent(null);
}
// TODO: Validate that this is the right place to store the SMS.
if (SmsManager.getDefault().getAutoPersisting()) {
final Uri uri = writeInboxMessage(intent);
if (uri != null) {
// Pass this to SMS apps so that they know where it is stored
intent.putExtra("uri", uri.toString());
}
}
} else {
intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION);
Uri uri = Uri.parse("sms://localhost:" + destPort);
intent.setData(uri);
intent.setComponent(null);
}
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
| public void | dispose()Tell the state machine to quit after processing all messages.
quit();
| public PhoneBase | getPhone()
return mPhone;
| private java.util.List | getSystemAppForIntent(android.content.Intent intent)
List<String> packages = new ArrayList<String>();
PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
String carrierFilterSmsPerm = "android.permission.CARRIER_FILTER_SMS";
for (ResolveInfo info : receivers) {
if (info.serviceInfo == null) {
loge("Can't get service information from " + info);
continue;
}
String packageName = info.serviceInfo.packageName;
if (packageManager.checkPermission(carrierFilterSmsPerm, packageName) ==
packageManager.PERMISSION_GRANTED) {
packages.add(packageName);
if (DBG) log("getSystemAppForIntent: added package "+ packageName);
}
}
return packages;
| void | handleInjectSms(android.os.AsyncResult ar)This method is called when a new SMS PDU is injected into application framework.
int result;
PendingIntent receivedIntent = null;
try {
receivedIntent = (PendingIntent) ar.userObj;
SmsMessage sms = (SmsMessage) ar.result;
if (sms == null) {
result = Intents.RESULT_SMS_GENERIC_ERROR;
} else {
result = dispatchMessage(sms.mWrappedSmsMessage);
}
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
result = Intents.RESULT_SMS_GENERIC_ERROR;
}
if (receivedIntent != null) {
try {
receivedIntent.send(result);
} catch (CanceledException e) { }
}
| void | handleNewSms(android.os.AsyncResult ar)
if (ar.exception != null) {
loge("Exception processing incoming SMS: " + ar.exception);
return;
}
int result;
try {
SmsMessage sms = (SmsMessage) ar.result;
result = dispatchMessage(sms.mWrappedSmsMessage);
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
result = Intents.RESULT_SMS_GENERIC_ERROR;
}
// RESULT_OK means that the SMS will be acknowledged by special handling,
// e.g. for SMS-PP data download. Any other result, we should ack here.
if (result != Activity.RESULT_OK) {
boolean handled = (result == Intents.RESULT_SMS_HANDLED);
notifyAndAcknowledgeLastIncomingSms(handled, result, null);
}
| protected abstract boolean | is3gpp2()Return true if this handler is for 3GPP2 messages; false for 3GPP format.
| static boolean | isCurrentFormat3gpp2()Returns whether the default message format for the current radio technology is 3GPP2.
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
return (PHONE_TYPE_CDMA == activePhone);
| protected void | log(java.lang.String s)Log with debug level.
Rlog.d(getName(), s);
| protected void | loge(java.lang.String s)Log with error level.
Rlog.e(getName(), s);
| protected void | loge(java.lang.String s, java.lang.Throwable e)Log with error level.
Rlog.e(getName(), s, e);
| void | notifyAndAcknowledgeLastIncomingSms(boolean success, int result, android.os.Message response)Notify interested apps if the framework has rejected an incoming SMS,
and send an acknowledge message to the network.
if (!success) {
// broadcast SMS_REJECTED_ACTION intent
Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
intent.putExtra("result", result);
mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
}
acknowledgeLastIncomingSms(success, result, response);
| protected void | onQuitting()Dispose of the WAP push object and release the wakelock.
mWapPush.dispose();
while (mWakeLock.isHeld()) {
mWakeLock.release();
}
| protected void | onUpdatePhoneObject(PhoneBase phone)Called when the phone changes the default method updates mPhone
mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
Override if different or other behavior is desired.
mPhone = phone;
mStorageMonitor = mPhone.mSmsStorageMonitor;
log("onUpdatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
| private static android.content.ContentValues | parseSmsMessage(android.telephony.SmsMessage[] msgs)Convert SmsMessage[] into SMS database schema columns
final SmsMessage sms = msgs[0];
final ContentValues values = new ContentValues();
values.put(Telephony.Sms.Inbox.ADDRESS, sms.getDisplayOriginatingAddress());
values.put(Telephony.Sms.Inbox.BODY, buildMessageBodyFromPdus(msgs));
values.put(Telephony.Sms.Inbox.DATE_SENT, sms.getTimestampMillis());
values.put(Telephony.Sms.Inbox.DATE, System.currentTimeMillis());
values.put(Telephony.Sms.Inbox.PROTOCOL, sms.getProtocolIdentifier());
values.put(Telephony.Sms.Inbox.SEEN, 0);
values.put(Telephony.Sms.Inbox.READ, 0);
final String subject = sms.getPseudoSubject();
if (!TextUtils.isEmpty(subject)) {
values.put(Telephony.Sms.Inbox.SUBJECT, subject);
}
values.put(Telephony.Sms.Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0);
values.put(Telephony.Sms.Inbox.SERVICE_CENTER, sms.getServiceCenterAddress());
return values;
| boolean | processMessagePart(InboundSmsTracker tracker)Process the inbound SMS segment. If the message is complete, send it as an ordered
broadcast to interested receivers and return true. If the message is a segment of an
incomplete multi-part SMS, return false.
int messageCount = tracker.getMessageCount();
byte[][] pdus;
int destPort = tracker.getDestPort();
if (messageCount == 1) {
// single-part message
pdus = new byte[][]{tracker.getPdu()};
} else {
// multi-part message
Cursor cursor = null;
try {
// used by several query selection arguments
String address = tracker.getAddress();
String refNumber = Integer.toString(tracker.getReferenceNumber());
String count = Integer.toString(tracker.getMessageCount());
// query for all segments and broadcast message if we have all the parts
String[] whereArgs = {address, refNumber, count};
cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
SELECT_BY_REFERENCE, whereArgs, null);
int cursorCount = cursor.getCount();
if (cursorCount < messageCount) {
// Wait for the other message parts to arrive. It's also possible for the last
// segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
// earlier segments. In that case, the broadcast will be sent as soon as all
// segments are in the table, and any later EVENT_BROADCAST_SMS messages will
// get a row count of 0 and return.
return false;
}
// All the parts are in place, deal with them
pdus = new byte[messageCount][];
while (cursor.moveToNext()) {
// subtract offset to convert sequence to 0-based array index
int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();
pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));
// Read the destination port from the first segment (needed for CDMA WAP PDU).
// It's not a bad idea to prefer the port from the first segment in other cases.
if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
int port = cursor.getInt(DESTINATION_PORT_COLUMN);
// strip format flags and convert to real port number, or -1
port = InboundSmsTracker.getRealDestPort(port);
if (port != -1) {
destPort = port;
}
}
}
} catch (SQLException e) {
loge("Can't access multipart SMS database", e);
return false;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
SmsBroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);
if (destPort == SmsHeader.PORT_WAP_PUSH) {
// Build up the data stream
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (byte[] pdu : pdus) {
// 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this
if (!tracker.is3gpp2()) {
SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
pdu = msg.getUserData();
}
output.write(pdu, 0, pdu.length);
}
int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
if (DBG) log("dispatchWapPdu() returned " + result);
// result is Activity.RESULT_OK if an ordered broadcast was sent
return (result == Activity.RESULT_OK);
}
List<String> carrierPackages = null;
UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
if (card != null) {
carrierPackages = card.getCarrierPackageNamesForIntent(
mContext.getPackageManager(),
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
} else {
loge("UiccCard not initialized.");
}
List<String> systemPackages =
getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
if (carrierPackages != null && carrierPackages.size() == 1) {
log("Found carrier package.");
CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
tracker.getFormat(), resultReceiver);
CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter);
smsFilter.filterSms(carrierPackages.get(0), smsFilterCallback);
} else if (systemPackages != null && systemPackages.size() == 1) {
log("Found system package.");
CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
tracker.getFormat(), resultReceiver);
CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter);
smsFilter.filterSms(systemPackages.get(0), smsFilterCallback);
} else {
logv("Unable to find carrier package: " + carrierPackages
+ ", nor systemPackages: " + systemPackages);
dispatchSmsDeliveryIntent(pdus, tracker.getFormat(), destPort, resultReceiver);
}
return true;
| private static java.lang.String | replaceFormFeeds(java.lang.String s)
return s == null ? "" : s.replace('\f", '\n");
| protected void | storeVoiceMailCount()
// Store the voice mail count in persistent memory.
String imsi = mPhone.getSubscriberId();
int mwi = mPhone.getVoiceMessageCount();
log("Storing Voice Mail Count = " + mwi
+ " for mVmCountKey = " + mPhone.VM_COUNT
+ " vmId = " + mPhone.VM_ID
+ " in preferences.");
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putInt(mPhone.VM_COUNT, mwi);
editor.putString(mPhone.VM_ID, imsi);
editor.commit();
| public void | updatePhoneObject(PhoneBase phone)Update the phone object when it changes.
sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
| private android.net.Uri | writeInboxMessage(android.content.Intent intent)Store a received SMS into Telephony provider
final SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
if (messages == null || messages.length < 1) {
loge("Failed to parse SMS pdu");
return null;
}
// Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access
// the methods on it although the SmsMessage itself is not null. So do this check
// before we do anything on the parsed SmsMessages.
for (final SmsMessage sms : messages) {
try {
sms.getDisplayMessageBody();
} catch (NullPointerException e) {
loge("NPE inside SmsMessage");
return null;
}
}
final ContentValues values = parseSmsMessage(messages);
final long identity = Binder.clearCallingIdentity();
try {
return mContext.getContentResolver().insert(Telephony.Sms.Inbox.CONTENT_URI, values);
} catch (Exception e) {
loge("Failed to persist inbox message", e);
} finally {
Binder.restoreCallingIdentity(identity);
}
return null;
|
|