diff options
Diffstat (limited to 'src/com/android/messaging/sms/SmsSender.java')
-rw-r--r-- | src/com/android/messaging/sms/SmsSender.java | 315 |
1 files changed, 0 insertions, 315 deletions
diff --git a/src/com/android/messaging/sms/SmsSender.java b/src/com/android/messaging/sms/SmsSender.java deleted file mode 100644 index 889973f..0000000 --- a/src/com/android/messaging/sms/SmsSender.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * 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.sms; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.SystemClock; -import android.telephony.PhoneNumberUtils; -import android.telephony.SmsManager; -import android.text.TextUtils; - -import com.android.messaging.Factory; -import com.android.messaging.R; -import com.android.messaging.receiver.SendStatusReceiver; -import com.android.messaging.util.Assert; -import com.android.messaging.util.BugleGservices; -import com.android.messaging.util.BugleGservicesKeys; -import com.android.messaging.util.LogUtil; -import com.android.messaging.util.PhoneUtils; -import com.android.messaging.util.UiUtils; - -import java.util.ArrayList; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Class that sends chat message via SMS. - * - * The interface emulates a blocking sending similar to making an HTTP request. - * It calls the SmsManager to send a (potentially multipart) message and waits - * on the sent status on each part. The waiting has a timeout so it won't wait - * forever. Once the sent status of all parts received, the call returns. - * A successful sending requires success status for all parts. Otherwise, we - * pick the highest level of failure as the error for the whole message, which - * is used to determine if we need to retry the sending. - */ -public class SmsSender { - private static final String TAG = LogUtil.BUGLE_TAG; - - public static final String EXTRA_PART_ID = "part_id"; - - /* - * A map for pending sms messages. The key is the random request UUID. - */ - private static ConcurrentHashMap<Uri, SendResult> sPendingMessageMap = - new ConcurrentHashMap<Uri, SendResult>(); - - private static final Random RANDOM = new Random(); - - // Whether we should send multipart SMS as separate messages - private static Boolean sSendMultipartSmsAsSeparateMessages = null; - - /** - * Class that holds the sent status for all parts of a multipart message sending - */ - public static class SendResult { - // Failure levels, used by the caller of the sender. - // For temporary failures, possibly we could retry the sending - // For permanent failures, we probably won't retry - public static final int FAILURE_LEVEL_NONE = 0; - public static final int FAILURE_LEVEL_TEMPORARY = 1; - public static final int FAILURE_LEVEL_PERMANENT = 2; - - // Tracking the remaining pending parts in sending - private int mPendingParts; - // Tracking the highest level of failure among all parts - private int mHighestFailureLevel; - - public SendResult(final int numOfParts) { - Assert.isTrue(numOfParts > 0); - mPendingParts = numOfParts; - mHighestFailureLevel = FAILURE_LEVEL_NONE; - } - - // Update the sent status of one part - public void setPartResult(final int resultCode) { - mPendingParts--; - setHighestFailureLevel(resultCode); - } - - public boolean hasPending() { - return mPendingParts > 0; - } - - public int getHighestFailureLevel() { - return mHighestFailureLevel; - } - - private int getFailureLevel(final int resultCode) { - switch (resultCode) { - case Activity.RESULT_OK: - return FAILURE_LEVEL_NONE; - case SmsManager.RESULT_ERROR_NO_SERVICE: - return FAILURE_LEVEL_TEMPORARY; - case SmsManager.RESULT_ERROR_RADIO_OFF: - return FAILURE_LEVEL_PERMANENT; - case SmsManager.RESULT_ERROR_GENERIC_FAILURE: - return FAILURE_LEVEL_PERMANENT; - default: { - LogUtil.e(TAG, "SmsSender: Unexpected sent intent resultCode = " + resultCode); - return FAILURE_LEVEL_PERMANENT; - } - } - } - - private void setHighestFailureLevel(final int resultCode) { - final int level = getFailureLevel(resultCode); - if (level > mHighestFailureLevel) { - mHighestFailureLevel = level; - } - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("SendResult:"); - sb.append("Pending=").append(mPendingParts).append(","); - sb.append("HighestFailureLevel=").append(mHighestFailureLevel); - return sb.toString(); - } - } - - public static void setResult(final Uri requestId, final int resultCode, - final int errorCode, final int partId, int subId) { - if (resultCode != Activity.RESULT_OK) { - LogUtil.e(TAG, "SmsSender: failure in sending message part. " - + " requestId=" + requestId + " partId=" + partId - + " resultCode=" + resultCode + " errorCode=" + errorCode); - if (errorCode != SendStatusReceiver.NO_ERROR_CODE) { - final Context context = Factory.get().getApplicationContext(); - UiUtils.showToastAtBottom(getSendErrorToastMessage(context, subId, errorCode)); - } - } else { - if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { - LogUtil.v(TAG, "SmsSender: received sent result. " + " requestId=" + requestId - + " partId=" + partId + " resultCode=" + resultCode); - } - } - if (requestId != null) { - final SendResult result = sPendingMessageMap.get(requestId); - if (result != null) { - synchronized (result) { - result.setPartResult(resultCode); - if (!result.hasPending()) { - result.notifyAll(); - } - } - } else { - LogUtil.e(TAG, "SmsSender: ignoring sent result. " + " requestId=" + requestId - + " partId=" + partId + " resultCode=" + resultCode); - } - } - } - - private static String getSendErrorToastMessage(final Context context, final int subId, - final int errorCode) { - final String carrierName = PhoneUtils.get(subId).getCarrierName(); - if (TextUtils.isEmpty(carrierName)) { - return context.getString(R.string.carrier_send_error_unknown_carrier, errorCode); - } else { - return context.getString(R.string.carrier_send_error, carrierName, errorCode); - } - } - - // This should be called from a RequestWriter queue thread - public static SendResult sendMessage(final Context context, final int subId, String dest, - String message, final String serviceCenter, final boolean requireDeliveryReport, - final Uri messageUri) throws SmsException { - if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { - LogUtil.v(TAG, "SmsSender: sending message. " + - "dest=" + dest + " message=" + message + - " serviceCenter=" + serviceCenter + - " requireDeliveryReport=" + requireDeliveryReport + - " requestId=" + messageUri); - } - if (TextUtils.isEmpty(message)) { - throw new SmsException("SmsSender: empty text message"); - } - // Get the real dest and message for email or alias if dest is email or alias - // Or sanitize the dest if dest is a number - if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway()) && - (MmsSmsUtils.isEmailAddress(dest) || MmsSmsUtils.isAlias(dest, subId))) { - // The original destination (email address) goes with the message - message = dest + " " + message; - // the new address is the email gateway # - dest = MmsConfig.get(subId).getEmailGateway(); - } else { - // remove spaces and dashes from destination number - // (e.g. "801 555 1212" -> "8015551212") - // (e.g. "+8211-123-4567" -> "+82111234567") - dest = PhoneNumberUtils.stripSeparators(dest); - } - if (TextUtils.isEmpty(dest)) { - throw new SmsException("SmsSender: empty destination address"); - } - // Divide the input message by SMS length limit - final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); - final ArrayList<String> messages = smsManager.divideMessage(message); - if (messages == null || messages.size() < 1) { - throw new SmsException("SmsSender: fails to divide message"); - } - // Prepare the send result, which collects the send status for each part - final SendResult pendingResult = new SendResult(messages.size()); - sPendingMessageMap.put(messageUri, pendingResult); - // Actually send the sms - sendInternal( - context, subId, dest, messages, serviceCenter, requireDeliveryReport, messageUri); - // Wait for pending intent to come back - synchronized (pendingResult) { - final long smsSendTimeoutInMillis = BugleGservices.get().getLong( - BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS, - BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS_DEFAULT); - final long beginTime = SystemClock.elapsedRealtime(); - long waitTime = smsSendTimeoutInMillis; - // We could possibly be woken up while still pending - // so make sure we wait the full timeout period unless - // we have the send results of all parts. - while (pendingResult.hasPending() && waitTime > 0) { - try { - pendingResult.wait(waitTime); - } catch (final InterruptedException e) { - LogUtil.e(TAG, "SmsSender: sending wait interrupted"); - } - waitTime = smsSendTimeoutInMillis - (SystemClock.elapsedRealtime() - beginTime); - } - } - // Either we timed out or have all the results (success or failure) - sPendingMessageMap.remove(messageUri); - if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { - LogUtil.v(TAG, "SmsSender: sending completed. " + - "dest=" + dest + " message=" + message + " result=" + pendingResult); - } - return pendingResult; - } - - // Actually sending the message using SmsManager - private static void sendInternal(final Context context, final int subId, String dest, - final ArrayList<String> messages, final String serviceCenter, - final boolean requireDeliveryReport, final Uri messageUri) throws SmsException { - Assert.notNull(context); - final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); - final int messageCount = messages.size(); - final ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount); - final ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount); - for (int i = 0; i < messageCount; i++) { - // Make pending intents different for each message part - final int partId = (messageCount <= 1 ? 0 : i + 1); - if (requireDeliveryReport && (i == (messageCount - 1))) { - // TODO we only care about the delivery status of the last part - // Shall we have better tracking of delivery status of all parts? - deliveryIntents.add(PendingIntent.getBroadcast( - context, - partId, - getSendStatusIntent(context, SendStatusReceiver.MESSAGE_DELIVERED_ACTION, - messageUri, partId, subId), - 0/*flag*/)); - } else { - deliveryIntents.add(null); - } - sentIntents.add(PendingIntent.getBroadcast( - context, - partId, - getSendStatusIntent(context, SendStatusReceiver.MESSAGE_SENT_ACTION, - messageUri, partId, subId), - 0/*flag*/)); - } - if (sSendMultipartSmsAsSeparateMessages == null) { - sSendMultipartSmsAsSeparateMessages = MmsConfig.get(subId) - .getSendMultipartSmsAsSeparateMessages(); - } - try { - if (sSendMultipartSmsAsSeparateMessages) { - // If multipart sms is not supported, send them as separate messages - for (int i = 0; i < messageCount; i++) { - smsManager.sendTextMessage(dest, - serviceCenter, - messages.get(i), - sentIntents.get(i), - deliveryIntents.get(i)); - } - } else { - smsManager.sendMultipartTextMessage( - dest, serviceCenter, messages, sentIntents, deliveryIntents); - } - } catch (final Exception e) { - throw new SmsException("SmsSender: caught exception in sending " + e); - } - } - - private static Intent getSendStatusIntent(final Context context, final String action, - final Uri requestUri, final int partId, final int subId) { - // Encode requestId in intent data - final Intent intent = new Intent(action, requestUri, context, SendStatusReceiver.class); - intent.putExtra(SendStatusReceiver.EXTRA_PART_ID, partId); - intent.putExtra(SendStatusReceiver.EXTRA_SUB_ID, subId); - return intent; - } -} |