diff options
author | Mike Dodd <mdodd@google.com> | 2015-08-11 11:16:59 -0700 |
---|---|---|
committer | Mike Dodd <mdodd@google.com> | 2015-08-12 12:47:26 -0700 |
commit | d3b009ae55651f1e60950342468e3c37fdeb0796 (patch) | |
tree | bc4b489af52d0e2521e21167d2ad76a47256f348 /src/com/android/messaging/receiver/SmsReceiver.java | |
parent | ef8c7abbcfc9c770385d6609a4b4bc70240ebdc4 (diff) | |
download | android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.tar.gz android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.tar.bz2 android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.zip |
Initial checkin of AOSP Messaging app.
b/23110861
Change-Id: I11db999bd10656801e618f78ab2b2ef74136fff1
Diffstat (limited to 'src/com/android/messaging/receiver/SmsReceiver.java')
-rw-r--r-- | src/com/android/messaging/receiver/SmsReceiver.java | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/com/android/messaging/receiver/SmsReceiver.java b/src/com/android/messaging/receiver/SmsReceiver.java new file mode 100644 index 0000000..db9b4bb --- /dev/null +++ b/src/com/android/messaging/receiver/SmsReceiver.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2015 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.messaging.receiver; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.provider.Telephony; +import android.provider.Telephony.Sms; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; +import android.support.v4.app.NotificationCompat.Style; +import android.support.v4.app.NotificationManagerCompat; + +import java.util.ArrayList; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import com.android.messaging.Factory; +import com.android.messaging.R; +import com.android.messaging.datamodel.BugleNotifications; +import com.android.messaging.datamodel.MessageNotificationState; +import com.android.messaging.datamodel.NoConfirmationSmsSendService; +import com.android.messaging.datamodel.action.ReceiveSmsMessageAction; +import com.android.messaging.sms.MmsUtils; +import com.android.messaging.ui.UIIntents; +import com.android.messaging.util.BugleGservices; +import com.android.messaging.util.BugleGservicesKeys; +import com.android.messaging.util.DebugUtils; +import com.android.messaging.util.LogUtil; +import com.android.messaging.util.OsUtil; +import com.android.messaging.util.PendingIntentConstants; +import com.android.messaging.util.PhoneUtils; + +/** + * Class that receives incoming SMS messages through android.provider.Telephony.SMS_RECEIVED + * + * This class serves two purposes: + * - Process phone verification SMS messages + * - Handle SMS messages when the user has enabled us to be the default SMS app (Pre-KLP) + */ +public final class SmsReceiver extends BroadcastReceiver { + private static final String TAG = LogUtil.BUGLE_TAG; + + private static ArrayList<Pattern> sIgnoreSmsPatterns; + + /** + * Enable or disable the SmsReceiver as appropriate. Pre-KLP we use this receiver for + * receiving incoming SMS messages. For KLP+ this receiver is not used when running as the + * primary user and the SmsDeliverReceiver is used for receiving incoming SMS messages. + * When running as a secondary user, this receiver is still used to trigger the incoming + * notification. + */ + public static void updateSmsReceiveHandler(final Context context) { + boolean smsReceiverEnabled; + boolean mmsWapPushReceiverEnabled; + boolean respondViaMessageEnabled; + boolean broadcastAbortEnabled; + + if (OsUtil.isAtLeastKLP()) { + // When we're running as the secondary user, we don't get the new SMS_DELIVER intent, + // only the primary user receives that. As secondary, we need to go old-school and + // listen for the SMS_RECEIVED intent. For the secondary user, use this SmsReceiver + // for both sms and mms notification. For the primary user on KLP (and above), we don't + // use the SmsReceiver. + smsReceiverEnabled = OsUtil.isSecondaryUser(); + // On KLP use the new deliver event for mms + mmsWapPushReceiverEnabled = false; + // On KLP we need to always enable this handler to show in the list of sms apps + respondViaMessageEnabled = true; + // On KLP we don't need to abort the broadcast + broadcastAbortEnabled = false; + } else { + // On JB we use the sms receiver for both sms/mms delivery + final boolean carrierSmsEnabled = PhoneUtils.getDefault().isSmsEnabled(); + smsReceiverEnabled = carrierSmsEnabled; + + // On JB we use the mms receiver when sms/mms is enabled + mmsWapPushReceiverEnabled = carrierSmsEnabled; + // On JB this is dynamic to make sure we don't show in dialer if sms is disabled + respondViaMessageEnabled = carrierSmsEnabled; + // On JB we need to abort broadcasts if SMS is enabled + broadcastAbortEnabled = carrierSmsEnabled; + } + + final PackageManager packageManager = context.getPackageManager(); + final boolean logv = LogUtil.isLoggable(TAG, LogUtil.VERBOSE); + if (smsReceiverEnabled) { + if (logv) { + LogUtil.v(TAG, "Enabling SMS message receiving"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, SmsReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + + } else { + if (logv) { + LogUtil.v(TAG, "Disabling SMS message receiving"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, SmsReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } + if (mmsWapPushReceiverEnabled) { + if (logv) { + LogUtil.v(TAG, "Enabling MMS message receiving"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, MmsWapPushReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + } else { + if (logv) { + LogUtil.v(TAG, "Disabling MMS message receiving"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, MmsWapPushReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } + if (broadcastAbortEnabled) { + if (logv) { + LogUtil.v(TAG, "Enabling SMS/MMS broadcast abort"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, AbortSmsReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + packageManager.setComponentEnabledSetting( + new ComponentName(context, AbortMmsWapPushReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + } else { + if (logv) { + LogUtil.v(TAG, "Disabling SMS/MMS broadcast abort"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, AbortSmsReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + packageManager.setComponentEnabledSetting( + new ComponentName(context, AbortMmsWapPushReceiver.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } + if (respondViaMessageEnabled) { + if (logv) { + LogUtil.v(TAG, "Enabling respond via message intent"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, NoConfirmationSmsSendService.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + } else { + if (logv) { + LogUtil.v(TAG, "Disabling respond via message intent"); + } + packageManager.setComponentEnabledSetting( + new ComponentName(context, NoConfirmationSmsSendService.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } + } + + private static final String EXTRA_ERROR_CODE = "errorCode"; + private static final String EXTRA_SUB_ID = "subscription"; + + public static void deliverSmsIntent(final Context context, final Intent intent) { + final android.telephony.SmsMessage[] messages = getMessagesFromIntent(intent); + + // Check messages for validity + if (messages == null || messages.length < 1) { + LogUtil.e(TAG, "processReceivedSms: null or zero or ignored message"); + return; + } + + final int errorCode = intent.getIntExtra(EXTRA_ERROR_CODE, 0); + // Always convert negative subIds into -1 + int subId = PhoneUtils.getDefault().getEffectiveIncomingSubIdFromSystem( + intent, EXTRA_SUB_ID); + deliverSmsMessages(context, subId, errorCode, messages); + if (MmsUtils.isDumpSmsEnabled()) { + final String format = null; + DebugUtils.dumpSms(messages[0].getTimestampMillis(), messages, format); + } + } + + public static void deliverSmsMessages(final Context context, final int subId, + final int errorCode, final android.telephony.SmsMessage[] messages) { + final ContentValues messageValues = + MmsUtils.parseReceivedSmsMessage(context, messages, errorCode); + + LogUtil.v(TAG, "SmsReceiver.deliverSmsMessages"); + + final long nowInMillis = System.currentTimeMillis(); + final long receivedTimestampMs = MmsUtils.getMessageDate(messages[0], nowInMillis); + + messageValues.put(Sms.Inbox.DATE, receivedTimestampMs); + // Default to unread and unseen for us but ReceiveSmsMessageAction will override + // seen for the telephony db. + messageValues.put(Sms.Inbox.READ, 0); + messageValues.put(Sms.Inbox.SEEN, 0); + if (OsUtil.isAtLeastL_MR1()) { + messageValues.put(Sms.SUBSCRIPTION_ID, subId); + } + + if (messages[0].getMessageClass() == android.telephony.SmsMessage.MessageClass.CLASS_0 || + DebugUtils.debugClassZeroSmsEnabled()) { + Factory.get().getUIIntents().launchClassZeroActivity(context, messageValues); + } else { + final ReceiveSmsMessageAction action = new ReceiveSmsMessageAction(messageValues); + action.start(); + } + } + + @Override + public void onReceive(final Context context, final Intent intent) { + LogUtil.v(TAG, "SmsReceiver.onReceive " + intent); + // On KLP+ we only take delivery of SMS messages in SmsDeliverReceiver. + if (PhoneUtils.getDefault().isSmsEnabled()) { + final String action = intent.getAction(); + if (OsUtil.isSecondaryUser() && + (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action) || + // TODO: update this with the actual constant from Telephony + "android.provider.Telephony.MMS_DOWNLOADED".equals(action))) { + postNewMessageSecondaryUserNotification(); + } else if (!OsUtil.isAtLeastKLP()) { + deliverSmsIntent(context, intent); + } + } + } + + private static class SecondaryUserNotificationState extends MessageNotificationState { + SecondaryUserNotificationState() { + super(null); + } + + @Override + protected Style build(Builder builder) { + return null; + } + + @Override + public boolean getNotificationVibrate() { + return true; + } + } + + public static void postNewMessageSecondaryUserNotification() { + final Context context = Factory.get().getApplicationContext(); + final Resources resources = context.getResources(); + final PendingIntent pendingIntent = UIIntents.get() + .getPendingIntentForSecondaryUserNewMessageNotification(context); + + final NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + builder.setContentTitle(resources.getString(R.string.secondary_user_new_message_title)) + .setTicker(resources.getString(R.string.secondary_user_new_message_ticker)) + .setSmallIcon(R.drawable.ic_sms_light) + // Returning PRIORITY_HIGH causes L to put up a HUD notification. Without it, the ticker + // isn't displayed. + .setPriority(Notification.PRIORITY_HIGH) + .setContentIntent(pendingIntent); + + final NotificationCompat.BigTextStyle bigTextStyle = + new NotificationCompat.BigTextStyle(builder); + bigTextStyle.bigText(resources.getString(R.string.secondary_user_new_message_title)); + final Notification notification = bigTextStyle.build(); + + final NotificationManagerCompat notificationManager = + NotificationManagerCompat.from(Factory.get().getApplicationContext()); + + int defaults = Notification.DEFAULT_LIGHTS; + if (BugleNotifications.shouldVibrate(new SecondaryUserNotificationState())) { + defaults |= Notification.DEFAULT_VIBRATE; + } + notification.defaults = defaults; + + notificationManager.notify(getNotificationTag(), + PendingIntentConstants.SMS_SECONDARY_USER_NOTIFICATION_ID, notification); + } + + /** + * Cancel the notification + */ + public static void cancelSecondaryUserNotification() { + final NotificationManagerCompat notificationManager = + NotificationManagerCompat.from(Factory.get().getApplicationContext()); + notificationManager.cancel(getNotificationTag(), + PendingIntentConstants.SMS_SECONDARY_USER_NOTIFICATION_ID); + } + + private static String getNotificationTag() { + return Factory.get().getApplicationContext().getPackageName() + ":secondaryuser"; + } + + /** + * Compile all of the patterns we check for to ignore system SMS messages. + */ + private static void compileIgnoreSmsPatterns() { + // Get the pattern set from GServices + final String smsIgnoreRegex = BugleGservices.get().getString( + BugleGservicesKeys.SMS_IGNORE_MESSAGE_REGEX, + BugleGservicesKeys.SMS_IGNORE_MESSAGE_REGEX_DEFAULT); + if (smsIgnoreRegex != null) { + final String[] ignoreSmsExpressions = smsIgnoreRegex.split("\n"); + if (ignoreSmsExpressions.length != 0) { + sIgnoreSmsPatterns = new ArrayList<Pattern>(); + for (int i = 0; i < ignoreSmsExpressions.length; i++) { + try { + sIgnoreSmsPatterns.add(Pattern.compile(ignoreSmsExpressions[i])); + } catch (PatternSyntaxException e) { + LogUtil.e(TAG, "compileIgnoreSmsPatterns: Skipping bad expression: " + + ignoreSmsExpressions[i]); + } + } + } + } + } + + /** + * Get the SMS messages from the specified SMS intent. + * @return the messages. If there is an error or the message should be ignored, return null. + */ + public static android.telephony.SmsMessage[] getMessagesFromIntent(Intent intent) { + final android.telephony.SmsMessage[] messages = Sms.Intents.getMessagesFromIntent(intent); + + // Check messages for validity + if (messages == null || messages.length < 1) { + 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. + try { + final String messageBody = messages[0].getDisplayMessageBody(); + if (messageBody != null) { + // Compile patterns if necessary + if (sIgnoreSmsPatterns == null) { + compileIgnoreSmsPatterns(); + } + // Check against filters + for (final Pattern pattern : sIgnoreSmsPatterns) { + if (pattern.matcher(messageBody).matches()) { + return null; + } + } + } + } catch (final NullPointerException e) { + LogUtil.e(TAG, "shouldIgnoreMessage: NPE inside SmsMessage"); + return null; + } + return messages; + } + + + /** + * Check the specified SMS intent to see if the message should be ignored + * @return true if the message should be ignored + */ + public static boolean shouldIgnoreMessage(Intent intent) { + return getMessagesFromIntent(intent) == null; + } +} |