1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.sms; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.Activity; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.PendingIntent; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Intent; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.SystemClock; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.telephony.PhoneNumberUtils; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.telephony.SmsManager; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.receiver.SendStatusReceiver; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservices; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservicesKeys; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.PhoneUtils; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Random; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.concurrent.ConcurrentHashMap; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Class that sends chat message via SMS. 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * The interface emulates a blocking sending similar to making an HTTP request. 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * It calls the SmsManager to send a (potentially multipart) message and waits 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * on the sent status on each part. The waiting has a timeout so it won't wait 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * forever. Once the sent status of all parts received, the call returns. 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * A successful sending requires success status for all parts. Otherwise, we 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * pick the highest level of failure as the error for the whole message, which 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * is used to determine if we need to retry the sending. 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class SmsSender { 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String TAG = LogUtil.BUGLE_TAG; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final String EXTRA_PART_ID = "part_id"; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /* 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * A map for pending sms messages. The key is the random request UUID. 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static ConcurrentHashMap<Uri, SendResult> sPendingMessageMap = 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new ConcurrentHashMap<Uri, SendResult>(); 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final Random RANDOM = new Random(); 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Whether we should send multipart SMS as separate messages 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Boolean sSendMultipartSmsAsSeparateMessages = null; 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Class that holds the sent status for all parts of a multipart message sending 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class SendResult { 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Failure levels, used by the caller of the sender. 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For temporary failures, possibly we could retry the sending 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For permanent failures, we probably won't retry 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int FAILURE_LEVEL_NONE = 0; 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int FAILURE_LEVEL_TEMPORARY = 1; 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int FAILURE_LEVEL_PERMANENT = 2; 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Tracking the remaining pending parts in sending 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mPendingParts; 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Tracking the highest level of failure among all parts 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mHighestFailureLevel; 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SendResult(final int numOfParts) { 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(numOfParts > 0); 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingParts = numOfParts; 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHighestFailureLevel = FAILURE_LEVEL_NONE; 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update the sent status of one part 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setPartResult(final int resultCode) { 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingParts--; 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setHighestFailureLevel(resultCode); 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean hasPending() { 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mPendingParts > 0; 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int getHighestFailureLevel() { 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mHighestFailureLevel; 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int getFailureLevel(final int resultCode) { 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (resultCode) { 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case Activity.RESULT_OK: 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return FAILURE_LEVEL_NONE; 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SmsManager.RESULT_ERROR_NO_SERVICE: 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return FAILURE_LEVEL_TEMPORARY; 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SmsManager.RESULT_ERROR_RADIO_OFF: 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return FAILURE_LEVEL_PERMANENT; 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SmsManager.RESULT_ERROR_GENERIC_FAILURE: 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return FAILURE_LEVEL_PERMANENT; 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd default: { 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SmsSender: Unexpected sent intent resultCode = " + resultCode); 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return FAILURE_LEVEL_PERMANENT; 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void setHighestFailureLevel(final int resultCode) { 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int level = getFailureLevel(resultCode); 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (level > mHighestFailureLevel) { 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHighestFailureLevel = level; 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public String toString() { 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder sb = new StringBuilder(); 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append("SendResult:"); 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append("Pending=").append(mPendingParts).append(","); 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append("HighestFailureLevel=").append(mHighestFailureLevel); 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sb.toString(); 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void setResult(final Uri requestId, final int resultCode, 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int errorCode, final int partId, int subId) { 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (resultCode != Activity.RESULT_OK) { 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SmsSender: failure in sending message part. " 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " requestId=" + requestId + " partId=" + partId 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " resultCode=" + resultCode + " errorCode=" + errorCode); 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (errorCode != SendStatusReceiver.NO_ERROR_CODE) { 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showToastAtBottom(getSendErrorToastMessage(context, subId, errorCode)); 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "SmsSender: received sent result. " + " requestId=" + requestId 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " partId=" + partId + " resultCode=" + resultCode); 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (requestId != null) { 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendResult result = sPendingMessageMap.get(requestId); 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (result != null) { 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (result) { 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd result.setPartResult(resultCode); 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!result.hasPending()) { 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd result.notifyAll(); 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SmsSender: ignoring sent result. " + " requestId=" + requestId 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " partId=" + partId + " resultCode=" + resultCode); 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getSendErrorToastMessage(final Context context, final int subId, 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int errorCode) { 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String carrierName = PhoneUtils.get(subId).getCarrierName(); 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(carrierName)) { 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return context.getString(R.string.carrier_send_error_unknown_carrier, errorCode); 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return context.getString(R.string.carrier_send_error, carrierName, errorCode); 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This should be called from a RequestWriter queue thread 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static SendResult sendMessage(final Context context, final int subId, String dest, 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String message, final String serviceCenter, final boolean requireDeliveryReport, 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri messageUri) throws SmsException { 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "SmsSender: sending message. " + 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "dest=" + dest + " message=" + message + 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " serviceCenter=" + serviceCenter + 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " requireDeliveryReport=" + requireDeliveryReport + 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " requestId=" + messageUri); 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(message)) { 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new SmsException("SmsSender: empty text message"); 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Get the real dest and message for email or alias if dest is email or alias 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Or sanitize the dest if dest is a number 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway()) && 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (MmsSmsUtils.isEmailAddress(dest) || MmsSmsUtils.isAlias(dest, subId))) { 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The original destination (email address) goes with the message 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = dest + " " + message; 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the new address is the email gateway # 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dest = MmsConfig.get(subId).getEmailGateway(); 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // remove spaces and dashes from destination number 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // (e.g. "801 555 1212" -> "8015551212") 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // (e.g. "+8211-123-4567" -> "+82111234567") 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dest = PhoneNumberUtils.stripSeparators(dest); 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(dest)) { 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new SmsException("SmsSender: empty destination address"); 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Divide the input message by SMS length limit 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> messages = smsManager.divideMessage(message); 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messages == null || messages.size() < 1) { 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new SmsException("SmsSender: fails to divide message"); 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Prepare the send result, which collects the send status for each part 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendResult pendingResult = new SendResult(messages.size()); 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sPendingMessageMap.put(messageUri, pendingResult); 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Actually send the sms 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendInternal( 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, subId, dest, messages, serviceCenter, requireDeliveryReport, messageUri); 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Wait for pending intent to come back 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (pendingResult) { 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long smsSendTimeoutInMillis = BugleGservices.get().getLong( 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS, 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS_DEFAULT); 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long beginTime = SystemClock.elapsedRealtime(); 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long waitTime = smsSendTimeoutInMillis; 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We could possibly be woken up while still pending 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // so make sure we wait the full timeout period unless 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // we have the send results of all parts. 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (pendingResult.hasPending() && waitTime > 0) { 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pendingResult.wait(waitTime); 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InterruptedException e) { 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SmsSender: sending wait interrupted"); 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd waitTime = smsSendTimeoutInMillis - (SystemClock.elapsedRealtime() - beginTime); 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Either we timed out or have all the results (success or failure) 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sPendingMessageMap.remove(messageUri); 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "SmsSender: sending completed. " + 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "dest=" + dest + " message=" + message + " result=" + pendingResult); 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return pendingResult; 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Actually sending the message using SmsManager 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void sendInternal(final Context context, final int subId, String dest, 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> messages, final String serviceCenter, 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean requireDeliveryReport, final Uri messageUri) throws SmsException { 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(context); 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int messageCount = messages.size(); 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount); 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount); 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < messageCount; i++) { 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Make pending intents different for each message part 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int partId = (messageCount <= 1 ? 0 : i + 1); 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (requireDeliveryReport && (i == (messageCount - 1))) { 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO we only care about the delivery status of the last part 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Shall we have better tracking of delivery status of all parts? 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deliveryIntents.add(PendingIntent.getBroadcast( 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd partId, 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getSendStatusIntent(context, SendStatusReceiver.MESSAGE_DELIVERED_ACTION, 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri, partId, subId), 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0/*flag*/)); 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deliveryIntents.add(null); 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sentIntents.add(PendingIntent.getBroadcast( 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd partId, 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getSendStatusIntent(context, SendStatusReceiver.MESSAGE_SENT_ACTION, 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri, partId, subId), 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0/*flag*/)); 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sSendMultipartSmsAsSeparateMessages == null) { 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sSendMultipartSmsAsSeparateMessages = MmsConfig.get(subId) 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .getSendMultipartSmsAsSeparateMessages(); 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sSendMultipartSmsAsSeparateMessages) { 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If multipart sms is not supported, send them as separate messages 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < messageCount; i++) { 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smsManager.sendTextMessage(dest, 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd serviceCenter, 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messages.get(i), 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sentIntents.get(i), 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deliveryIntents.get(i)); 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smsManager.sendMultipartTextMessage( 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dest, serviceCenter, messages, sentIntents, deliveryIntents); 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception e) { 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new SmsException("SmsSender: caught exception in sending " + e); 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Intent getSendStatusIntent(final Context context, final String action, 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri requestUri, final int partId, final int subId) { 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Encode requestId in intent data 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Intent intent = new Intent(action, requestUri, context, SendStatusReceiver.class); 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intent.putExtra(SendStatusReceiver.EXTRA_PART_ID, partId); 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intent.putExtra(SendStatusReceiver.EXTRA_SUB_ID, subId); 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return intent; 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 316