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.content.ContentResolver; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentUris; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentValues; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Intent; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.AssetFileDescriptor; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.Resources; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteDatabase; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteException; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaMetadataRetriever; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.ConnectivityManager; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.NetworkInfo; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Bundle; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Settings; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony.Mms; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony.Sms; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony.Threads; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.telephony.SmsManager; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.telephony.SmsMessage; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.util.Rfc822Token; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.util.Rfc822Tokenizer; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.MediaScratchFileProvider; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.action.DownloadMmsAction; 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.action.SendMessageAction; 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessageData; 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ParticipantData; 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.InvalidHeaderValueException; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.MmsException; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.SqliteWrapper; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.CharacterSets; 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.EncodedStringValue; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.GenericPdu; 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.NotificationInd; 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduBody; 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduComposer; 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduHeaders; 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduParser; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduPart; 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.PduPersister; 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.RetrieveConf; 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.SendConf; 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.pdu.SendReq; 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.SmsSender.SendResult; 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservices; 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservicesKeys; 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BuglePrefs; 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ContentType; 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.DebugUtils; 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.EmailAddress; 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ImageUtils; 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ImageUtils.ImageResizer; 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.MediaMetadataRetrieverWrapper; 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil; 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.PhoneUtils; 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.base.Joiner; 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.BufferedOutputStream; 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.File; 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.FileNotFoundException; 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.FileOutputStream; 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.IOException; 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.InputStream; 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.UnsupportedEncodingException; 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Calendar; 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.GregorianCalendar; 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet; 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Locale; 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Set; 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.UUID; 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Utils for sending sms/mms messages. 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class MmsUtils { 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String TAG = LogUtil.BUGLE_TAG; 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final boolean DEFAULT_DELIVERY_REPORT_MODE = false; 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final boolean DEFAULT_READ_REPORT_MODE = false; 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final long DEFAULT_EXPIRY_TIME_IN_SECONDS = 7 * 24 * 60 * 60; 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL; 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MAX_SMS_RETRY = 3; 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS request succeeded 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MMS_REQUEST_SUCCEEDED = 0; 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS request failed with a transient error and can be retried automatically 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MMS_REQUEST_AUTO_RETRY = 1; 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS request failed with an error and can be retried manually 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MMS_REQUEST_MANUAL_RETRY = 2; 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS request failed with a specific error and should not be retried 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MMS_REQUEST_NO_RETRY = 3; 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final String getRequestStatusDescription(final int status) { 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (status) { 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case MMS_REQUEST_SUCCEEDED: 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return "SUCCEEDED"; 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case MMS_REQUEST_AUTO_RETRY: 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return "AUTO_RETRY"; 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case MMS_REQUEST_MANUAL_RETRY: 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return "MANUAL_RETRY"; 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case MMS_REQUEST_NO_RETRY: 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return "NO_RETRY"; 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd default: 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return String.valueOf(status) + " (check MmsUtils)"; 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int PDU_HEADER_VALUE_UNDEFINED = 0; 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int DEFAULT_DURATION = 5000; //ms 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // amount of space to leave in a MMS for text and overhead. 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int MMS_MAX_SIZE_SLOP = 1024; 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final long INVALID_TIMESTAMP = 0L; 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String[] sNoSubjectStrings; 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class MmsInfo { 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public Uri mUri; 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int mMessageSize; 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public PduBody mPduBody; 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Sync all remote messages apart from drafts 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String REMOTE_SMS_SELECTION = String.format( 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "(%s IN (%d, %d, %d, %d, %d))", 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.TYPE, 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.MESSAGE_TYPE_INBOX, 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.MESSAGE_TYPE_OUTBOX, 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.MESSAGE_TYPE_QUEUED, 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.MESSAGE_TYPE_FAILED, 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.MESSAGE_TYPE_SENT); 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String REMOTE_MMS_SELECTION = String.format( 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "((%s IN (%d, %d, %d, %d)) AND (%s IN (%d, %d, %d)))", 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_BOX, 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_BOX_INBOX, 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_BOX_OUTBOX, 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_BOX_SENT, 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_BOX_FAILED, 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.MESSAGE_TYPE, 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PduHeaders.MESSAGE_TYPE_SEND_REQ, 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND, 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Type selection for importing sms messages. 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The SQL selection for importing sms messages 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getSmsTypeSelectionSql() { 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return REMOTE_SMS_SELECTION; 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Type selection for importing mms messages. 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The SQL selection for importing mms messages. This selects the message type, 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * not including the selection on timestamp. 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getMmsTypeSelectionSql() { 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return REMOTE_MMS_SELECTION; 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // SMIL spec: http://www.w3.org/TR/SMIL3 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilImagePart = 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<par dur=\"" + DEFAULT_DURATION + "ms\">" + 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<img src=\"%s\" region=\"Image\" />" + 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</par>"; 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilVideoPart = 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<par dur=\"%2$dms\">" + 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<video src=\"%1$s\" dur=\"%2$dms\" region=\"Image\" />" + 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</par>"; 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilAudioPart = 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<par dur=\"%2$dms\">" + 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<audio src=\"%1$s\" dur=\"%2$dms\" />" + 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</par>"; 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilTextPart = 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<par dur=\"" + DEFAULT_DURATION + "ms\">" + 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<text src=\"%s\" region=\"Text\" />" + 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</par>"; 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilPart = 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<par dur=\"" + DEFAULT_DURATION + "ms\">" + 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<ref src=\"%s\" />" + 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</par>"; 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilTextOnly = 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<smil>" + 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<head>" + 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<layout>" + 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<root-layout/>" + 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<region id=\"Text\" top=\"0\" left=\"0\" " 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "height=\"100%%\" width=\"100%%\"/>" + 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</layout>" + 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</head>" + 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<body>" + 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s" + // constructed body goes here 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</body>" + 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</smil>"; 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilVisualAttachmentsOnly = 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<smil>" + 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<head>" + 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<layout>" + 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<root-layout/>" + 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<region id=\"Image\" fit=\"meet\" top=\"0\" left=\"0\" " 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "height=\"100%%\" width=\"100%%\"/>" + 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</layout>" + 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</head>" + 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<body>" + 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s" + // constructed body goes here 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</body>" + 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</smil>"; 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilVisualAttachmentsWithText = 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<smil>" + 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<head>" + 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<layout>" + 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<root-layout/>" + 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<region id=\"Image\" fit=\"meet\" top=\"0\" left=\"0\" " 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "height=\"80%%\" width=\"100%%\"/>" + 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<region id=\"Text\" top=\"80%%\" left=\"0\" height=\"20%%\" " 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "width=\"100%%\"/>" + 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</layout>" + 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</head>" + 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<body>" + 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s" + // constructed body goes here 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</body>" + 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</smil>"; 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilNonVisualAttachmentsOnly = 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<smil>" + 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<head>" + 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<layout>" + 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<root-layout/>" + 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</layout>" + 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</head>" + 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "<body>" + 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s" + // constructed body goes here 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</body>" + 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "</smil>"; 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String sSmilNonVisualAttachmentsWithText = sSmilTextOnly; 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final String MMS_DUMP_PREFIX = "mmsdump-"; 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final String SMS_DUMP_PREFIX = "smsdump-"; 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MIN_VIDEO_BYTES_PER_SECOND = 4 * 1024; 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MIN_IMAGE_BYTE_SIZE = 16 * 1024; 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MAX_VIDEO_ATTACHMENT_COUNT = 1; 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MmsInfo makePduBody(final Context context, final MessageData message, 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int subId) { 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduBody pb = new PduBody(); 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Compute data size requirements for this message: count up images and total size of 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // non-image attachments. 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int totalLength = 0; 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int countImage = 0; 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : message.getParts()) { 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment()) { 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentType = part.getContentType(); 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (ContentType.isImageType(contentType)) { 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd countImage++; 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (ContentType.isVCardType(contentType)) { 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += getDataLength(context, part.getContentUri()); 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += getMediaFileSize(part.getContentUri()); 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long minSize = countImage * MIN_IMAGE_BYTE_SIZE; 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int byteBudget = MmsConfig.get(subId).getMaxMessageSize() - totalLength 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd - MMS_MAX_SIZE_SLOP; 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final double budgetFactor = 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd minSize > 0 ? Math.max(1.0, byteBudget / ((double) minSize)) : 1; 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int bytesPerImage = (int) (budgetFactor * MIN_IMAGE_BYTE_SIZE); 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int widthLimit = MmsConfig.get(subId).getMaxImageWidth(); 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int heightLimit = MmsConfig.get(subId).getMaxImageHeight(); 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Actually add the attachments, shrinking images appropriately. 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int index = 0; 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength = 0; 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean hasVisualAttachment = false; 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean hasNonVisualAttachment = false; 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean hasText = false; 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder smilBody = new StringBuilder(); 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : message.getParts()) { 333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String srcName; 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment()) { 335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String contentType = part.getContentType(); 336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (ContentType.isImageType(contentType)) { 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // There's a good chance that if we selected the image from our media picker the 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // content type is image/*. Fix the content type here for gifs so that we only 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // need to open the input stream once. All other gif vs static image checks will 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // only have to do a string comparison which is much cheaper. 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isGif = ImageUtils.isGif(contentType, part.getContentUri()); 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentType = isGif ? ContentType.IMAGE_GIF : contentType; 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd srcName = String.format(isGif ? "image%06d.gif" : "image%06d.jpg", index); 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilImagePart, srcName)); 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += addPicturePart(context, pb, index, part, 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd widthLimit, heightLimit, bytesPerImage, srcName, contentType); 347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasVisualAttachment = true; 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (ContentType.isVideoType(contentType)) { 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd srcName = String.format("video%06d.mp4", index); 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int length = addVideoPart(context, pb, part, srcName); 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += length; 352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilVideoPart, srcName, 353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getMediaDurationMs(context, part, DEFAULT_DURATION))); 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasVisualAttachment = true; 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (ContentType.isVCardType(contentType)) { 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd srcName = String.format("contact%06d.vcf", index); 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += addVCardPart(context, pb, part, srcName); 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilPart, srcName)); 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasNonVisualAttachment = true; 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (ContentType.isAudioType(contentType)) { 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd srcName = String.format("recording%06d.amr", index); 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += addOtherPart(context, pb, part, srcName); 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int duration = getMediaDurationMs(context, part, -1); 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(duration != -1); 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilAudioPart, srcName, duration)); 366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasNonVisualAttachment = true; 367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd srcName = String.format("other%06d.dat", index); 369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += addOtherPart(context, pb, part, srcName); 370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilPart, srcName)); 371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd index++; 373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(part.getText())) { 375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasText = true; 376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (hasText) { 380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String srcName = String.format("text.%06d.txt", index); 381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String text = message.getMessageText(); 382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd totalLength += addTextPart(context, pb, text, srcName); 383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Append appropriate SMIL to the body. 385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilBody.append(String.format(sSmilTextPart, srcName)); 386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smilTemplate = getSmilTemplate(hasVisualAttachment, 389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasNonVisualAttachment, hasText); 390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addSmilPart(pb, smilTemplate, smilBody.toString()); 391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MmsInfo mmsInfo = new MmsInfo(); 393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mmsInfo.mPduBody = pb; 394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mmsInfo.mMessageSize = totalLength; 395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mmsInfo; 397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int getMediaDurationMs(final Context context, final MessagePartData part, 400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int defaultDurationMs) { 401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(context); 402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(part); 403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(ContentType.isAudioType(part.getContentType()) || 404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.isVideoType(part.getContentType())); 405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MediaMetadataRetrieverWrapper retriever = new MediaMetadataRetrieverWrapper(); 407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retriever.setDataSource(part.getContentUri()); 409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return retriever.extractInteger( 410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MediaMetadataRetriever.METADATA_KEY_DURATION, defaultDurationMs); 411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(LogUtil.BUGLE_TAG, "Error extracting duration from " + part.getContentUri(), e); 413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return defaultDurationMs; 414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retriever.release(); 416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void setPartContentLocationAndId(final PduPart part, final String srcName) { 420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set Content-Location. 421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentLocation(srcName.getBytes()); 422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set Content-Id. 424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int index = srcName.lastIndexOf("."); 425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentId = (index == -1) ? srcName : srcName.substring(0, index); 426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentId(contentId.getBytes()); 427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int addTextPart(final Context context, final PduBody pb, 430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String text, final String srcName) { 431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPart part = new PduPart(); 432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set Charset if it's a text media. 434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setCharset(CharacterSets.UTF_8); 435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set Content-Type. 437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentType(ContentType.TEXT_PLAIN.getBytes()); 438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set Content-Location. 440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setPartContentLocationAndId(part, srcName); 441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setData(text.getBytes()); 443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pb.addPart(part); 445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return part.getData().length; 447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int addPicturePart(final Context context, final PduBody pb, final int index, 450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart, int widthLimit, int heightLimit, 451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxPartSize, final String srcName, final String contentType) { 452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri imageUri = messagePart.getContentUri(); 453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int width = messagePart.getWidth(); 454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int height = messagePart.getHeight(); 455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Swap the width and height limits to match the orientation of the image so we scale the 457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // picture as little as possible. 458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if ((height > width) != (heightLimit > widthLimit)) { 459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int temp = widthLimit; 460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd widthLimit = heightLimit; 461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd heightLimit = temp; 462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int orientation = ImageUtils.getOrientation(context, imageUri); 465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int imageSize = getDataLength(context, imageUri); 466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (imageSize <= 0) { 467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Can't get image", new Exception()); 468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addPicturePart size: " + imageSize + " width: " 473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + width + " widthLimit: " + widthLimit 474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " height: " + height 475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " heightLimit: " + heightLimit); 476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PduPart part; 479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check if we're already within the limits - in which case we don't need to resize. 480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The size can be zero here, even when the media has content. See the comment in 481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // MediaModel.initMediaSize. Sometimes it'll compute zero and it's costly to read the 482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // whole stream to compute the size. When we call getResizedImageAsPart(), we'll correctly 483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // set the size. 484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (imageSize <= maxPartSize && 485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd width <= widthLimit && 486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd height <= heightLimit && 487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (orientation == android.media.ExifInterface.ORIENTATION_UNDEFINED || 488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd orientation == android.media.ExifInterface.ORIENTATION_NORMAL)) { 489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addPicturePart - already sized"); 491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part = new PduPart(); 493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setDataUri(imageUri); 494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentType(contentType.getBytes()); 495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part = getResizedImageAsPart(widthLimit, heightLimit, maxPartSize, 497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd width, height, orientation, imageUri, context, contentType); 498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part == null) { 499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final OutOfMemoryError e = new OutOfMemoryError(); 500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Can't resize image: not enough memory?", e); 501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw e; 502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd imageSize = part.getData().length; 504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setPartContentLocationAndId(part, srcName); 507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pb.addPart(index, part); 509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addPicturePart size: " + imageSize); 512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return imageSize; 515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void addPartForUri(final Context context, final PduBody pb, 518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String srcName, final Uri uri, final String contentType) { 519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPart part = new PduPart(); 520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setDataUri(uri); 521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentType(contentType.getBytes()); 522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setPartContentLocationAndId(part, srcName); 524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pb.addPart(part); 526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int addVCardPart(final Context context, final PduBody pb, 529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart, final String srcName) { 530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri vcardUri = messagePart.getContentUri(); 531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentType = messagePart.getContentType(); 532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int vcardSize = getDataLength(context, vcardUri); 533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (vcardSize <= 0) { 534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Can't get vcard", new Exception()); 535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addPartForUri(context, pb, srcName, vcardUri, contentType); 539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addVCardPart size: " + vcardSize); 542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return vcardSize; 545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Add video part recompressing video if necessary. If recompression fails, part is not 549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * added. 550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int addVideoPart(final Context context, final PduBody pb, 552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart, final String srcName) { 553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri attachmentUri = messagePart.getContentUri(); 554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String contentType = messagePart.getContentType(); 555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addPart attachmentUrl: " + attachmentUri.toString()); 558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(contentType)) { 561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentType = ContentType.VIDEO_3G2; 562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addPartForUri(context, pb, srcName, attachmentUri, contentType); 565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (int) getMediaFileSize(attachmentUri); 566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int addOtherPart(final Context context, final PduBody pb, 569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart, final String srcName) { 570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri attachmentUri = messagePart.getContentUri(); 571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentType = messagePart.getContentType(); 572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "addPart attachmentUrl: " + attachmentUri.toString()); 575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int dataSize = (int) getMediaFileSize(attachmentUri); 578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addPartForUri(context, pb, srcName, attachmentUri, contentType); 580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return dataSize; 582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void addSmilPart(final PduBody pb, final String smilTemplate, 585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smilBody) { 586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPart smilPart = new PduPart(); 587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilPart.setContentId("smil".getBytes()); 588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilPart.setContentLocation("smil.xml".getBytes()); 589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilPart.setContentType(ContentType.APP_SMIL.getBytes()); 590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smil = String.format(smilTemplate, smilBody); 591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smilPart.setData(smil.getBytes()); 592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pb.addPart(0, smilPart); 593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getSmilTemplate(final boolean hasVisualAttachments, 596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean hasNonVisualAttachments, final boolean hasText) { 597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (hasVisualAttachments) { 598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return hasText ? sSmilVisualAttachmentsWithText : sSmilVisualAttachmentsOnly; 599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (hasNonVisualAttachments) { 601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return hasText ? sSmilNonVisualAttachmentsWithText : sSmilNonVisualAttachmentsOnly; 602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sSmilTextOnly; 604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int getDataLength(final Context context, final Uri uri) { 607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd InputStream is = null; 608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd is = context.getContentResolver().openInputStream(uri); 610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return is == null ? 0 : is.available(); 612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "getDataLength couldn't stream: " + uri, e); 614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final FileNotFoundException e) { 616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "getDataLength couldn't open: " + uri, e); 617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (is != null) { 619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd is.close(); 621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "getDataLength couldn't close: " + uri, e); 623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Returns {@code true} if group mms is turned on, 631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@code false} otherwise. 632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * For the group mms feature to be enabled, the following must be true: 634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1. the feature is enabled in mms_config.xml (currently on by default) 635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2. the feature is enabled in the SMS settings page 636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if group mms is supported 638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean groupMmsEnabled(final int subId) { 640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Resources resources = context.getResources(); 642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getSubscriptionPrefs(subId); 643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String groupMmsKey = resources.getString(R.string.group_mms_pref_key); 644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean groupMmsEnabledDefault = resources.getBoolean(R.bool.group_mms_pref_default); 645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean groupMmsPrefOn = prefs.getBoolean(groupMmsKey, groupMmsEnabledDefault); 646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return MmsConfig.get(subId).getGroupMmsEnabled() && groupMmsPrefOn; 647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get a version of this image resized to fit the given dimension and byte-size limits. Note 651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * that the content type of the resulting PduPart may not be the same as the content type of 652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * this UriImage; always call {@link PduPart#getContentType()} to get the new content type. 653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param widthLimit The width limit, in pixels 655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param heightLimit The height limit, in pixels 656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param byteLimit The binary size limit, in bytes 657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param width The image width, in pixels 658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param height The image height, in pixels 659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param orientation Orientation constant from ExifInterface for rotating or flipping the 660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * image 661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param imageUri Uri to the image data 662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param context Needed to open the image 663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return A new PduPart containing the resized image data 664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static PduPart getResizedImageAsPart(final int widthLimit, 666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int heightLimit, final int byteLimit, final int width, final int height, 667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int orientation, final Uri imageUri, final Context context, final String contentType) { 668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPart part = new PduPart(); 669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] data = ImageResizer.getResizedImageData(width, height, orientation, 671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd widthLimit, heightLimit, byteLimit, imageUri, context, contentType); 672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (data == null) { 673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "Resize image failed."); 675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setData(data); 680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Any static images will be compressed into a jpeg 681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentTypeOfResizedImage = ImageUtils.isGif(contentType, imageUri) 682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ? ContentType.IMAGE_GIF : ContentType.IMAGE_JPEG; 683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.setContentType(contentTypeOfResizedImage.getBytes()); 684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return part; 686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get media file size 690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getMediaFileSize(final Uri uri) { 692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AssetFileDescriptor fd = null; 694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fd = context.getContentResolver().openAssetFileDescriptor(uri, "r"); 696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (fd != null) { 697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return fd.getParcelFileDescriptor().getStatSize(); 698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final FileNotFoundException e) { 700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.getMediaFileSize: cound not find media file: " + e, e); 701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (fd != null) { 703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fd.close(); 705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.getMediaFileSize: failed to close " + e, e); 707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0L; 711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Code for extracting the actual phone numbers for the participants in a conversation, 714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // given a thread id. 715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final Uri ALL_THREADS_URI = 717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); 718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String[] RECIPIENTS_PROJECTION = { 720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Threads._ID, 721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Threads.RECIPIENT_IDS 722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int RECIPIENT_IDS = 1; 725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static List<String> getRecipientsByThread(final long threadId) { 727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String spaceSepIds = getRawRecipientIdsForThread(threadId); 728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(spaceSepIds)) { 729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getAddresses(context, spaceSepIds); 731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // NOTE: There are phones on which you can't get the recipients from the thread id for SMS 736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // until you have a message in the conversation! 737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getRawRecipientIdsForThread(final long threadId) { 738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (threadId <= 0) { 739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver cr = context.getContentResolver(); 743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor thread = cr.query( 744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ALL_THREADS_URI, 745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd RECIPIENTS_PROJECTION, "_id=?", new String[] { String.valueOf(threadId) }, null); 746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (thread != null) { 747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 748d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (thread.moveToFirst()) { 749d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // recipientIds will be a space-separated list of ids into the 750d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // canonical addresses table. 751d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return thread.getString(RECIPIENT_IDS); 752d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 753d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 754d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd thread.close(); 755d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 756d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 757d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 758d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 759d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 760d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final Uri SINGLE_CANONICAL_ADDRESS_URI = 761d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri.parse("content://mms-sms/canonical-address"); 762d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 763d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static List<String> getAddresses(final Context context, final String spaceSepIds) { 764d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<String> numbers = new ArrayList<String>(); 765d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] ids = spaceSepIds.split(" "); 766d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String id : ids) { 767d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long longId; 768d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 769d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 770d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd longId = Long.parseLong(id); 771d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (longId < 0) { 772d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.getAddresses: invalid id " + longId); 773d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd continue; 774d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 775d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final NumberFormatException ex) { 776d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.getAddresses: invalid id. " + ex, ex); 777d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // skip this id 778d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd continue; 779d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 780d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 781d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: build a single query where we get all the addresses at once. 782d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor c = null; 783d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 784d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd c = context.getContentResolver().query( 785d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentUris.withAppendedId(SINGLE_CANONICAL_ADDRESS_URI, longId), 786d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null, null); 787d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception e) { 788d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.getAddresses: query failed for id " + longId, e); 789d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 790d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (c != null) { 791d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 792d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (c.moveToFirst()) { 793d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String number = c.getString(0); 794d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(number)) { 795d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd numbers.add(number); 796d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 797d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "Canonical MMS/SMS address is empty for id: " + longId); 798d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 799d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 800d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 801d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd c.close(); 802d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 803d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 804d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 805d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (numbers.isEmpty()) { 806d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "No MMS addresses found from ids string [" + spaceSepIds + "]"); 807d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 808d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return numbers; 809d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 810d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 811d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Get telephony SMS thread ID 812d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getOrCreateSmsThreadId(final Context context, final String dest) { 813d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // use destinations to determine threadId 814d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Set<String> recipients = new HashSet<String>(); 815d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(dest); 816d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 817d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return MmsSmsUtils.Threads.getOrCreateThreadId(context, recipients); 818d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 819d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: getting thread id failed: " + e); 820d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 821d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 822d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 823d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 824d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Get telephony SMS thread ID 825d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getOrCreateThreadId(final Context context, final List<String> dests) { 826d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (dests == null || dests.size() == 0) { 827d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 828d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 829d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // use destinations to determine threadId 830d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Set<String> recipients = new HashSet<String>(dests); 831d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 832d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return MmsSmsUtils.Threads.getOrCreateThreadId(context, recipients); 833d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 834d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: getting thread id failed: " + e); 835d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 836d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 837d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 838d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 839d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 840d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Add an SMS to the given URI with thread_id specified. 841d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 842d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param resolver the content resolver to use 843d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param uri the URI to add the message to 844d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param subId subId for the receiving sim 845d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param address the address of the sender 846d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param body the body of the message 847d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param subject the psuedo-subject of the message 848d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param date the timestamp for the message 849d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param read true if the message has been read, false if not 850d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId the thread_id of the message 851d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return the URI for the new message 852d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 853d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Uri addMessageToUri(final ContentResolver resolver, 854d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri uri, final int subId, final String address, final String body, 855d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String subject, final Long date, final boolean read, final boolean seen, 856d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int status, final int type, final long threadId) { 857d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(7); 858d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 859d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.ADDRESS, address); 860d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (date != null) { 861d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.DATE, date); 862d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 863d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.READ, read ? 1 : 0); 864d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.SEEN, seen ? 1 : 0); 865d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.SUBJECT, subject); 866d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.BODY, body); 867d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL_MR1()) { 868d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.SUBSCRIPTION_ID, subId); 869d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 870d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (status != Telephony.Sms.STATUS_NONE) { 871d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.STATUS, status); 872d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 873d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (type != Telephony.Sms.MESSAGE_TYPE_ALL) { 874d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.TYPE, type); 875d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 876d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (threadId != -1L) { 877d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.THREAD_ID, threadId); 878d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 879d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return resolver.insert(uri, values); 880d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 881d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 882d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert an SMS message to telephony 883d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static Uri insertSmsMessage(final Context context, final Uri uri, final int subId, 884d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String dest, final String text, final long timestamp, final int status, 885d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int type, final long threadId) { 886d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri response = null; 887d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 888d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response = addMessageToUri(context.getContentResolver(), uri, subId, dest, 889d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd text, null /* subject */, timestamp, true /* read */, 890d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd true /* seen */, status, type, threadId); 891d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 892d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "Mmsutils: Inserted SMS message into telephony (type = " + type + ")" 893d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", uri: " + response); 894d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 895d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 896d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: persist sms message failure " + e, e); 897d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 898d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: persist sms message failure " + e, e); 899d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 900d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return response; 901d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 902d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 903d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update SMS message type in telephony; returns true if it succeeded. 904d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updateSmsMessageSendingStatus(final Context context, final Uri uri, 905d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int type, final long date) { 906d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 907d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = context.getContentResolver(); 908d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(2); 909d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 910d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.TYPE, type); 911d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Sms.DATE, date); 912d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int cnt = resolver.update(uri, values, null, null); 913d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cnt == 1) { 914d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 915d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "Mmsutils: Updated sending SMS " + uri + "; type = " + type 916d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", date = " + date + " (millis since epoch)"); 917d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 918d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 919d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 920d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 921d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: update sms message failure " + e, e); 922d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 923d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: update sms message failure " + e, e); 924d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 925d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 926d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 927d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 928d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Persist a sent MMS message in telephony 929d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Uri insertSendReq(final Context context, final GenericPdu pdu, final int subId, 930d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String subPhoneNumber) { 931d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPersister persister = PduPersister.getPduPersister(context); 932d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri uri = null; 933d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 934d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Persist the PDU 935d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uri = persister.persist( 936d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pdu, 937d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Sent.CONTENT_URI, 938d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subId, 939d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subPhoneNumber, 940d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*preOpenedFiles*/); 941d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update mms table to reflect sent messages are always seen and read 942d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(1); 943d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.READ, 1); 944d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.SEEN, 1); 945d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SqliteWrapper.update(context, context.getContentResolver(), uri, values, null, null); 946d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsException e) { 947d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: persist mms sent message failure " + e, e); 948d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 949d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return uri; 950d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 951d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 952d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Persist a received MMS message in telephony 953d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static Uri insertReceivedMmsMessage(final Context context, 954d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RetrieveConf retrieveConf, final int subId, final String subPhoneNumber, 955d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long receivedTimestampInSeconds, final String contentLocation) { 956d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPersister persister = PduPersister.getPduPersister(context); 957d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri uri = null; 958d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 959d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uri = persister.persist( 960d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrieveConf, 961d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Inbox.CONTENT_URI, 962d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subId, 963d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subPhoneNumber, 964d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*preOpenedFiles*/); 965d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 966d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(2); 967d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update mms table with local time instead of PDU time 968d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.DATE, receivedTimestampInSeconds); 969d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Also update the content location field from NotificationInd so that 970d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // wap push dedup would work even after the wap push is deleted 971d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.CONTENT_LOCATION, contentLocation); 972d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SqliteWrapper.update(context, context.getContentResolver(), uri, values, null, null); 973d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 974d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "MmsUtils: Inserted MMS message into telephony, uri: " + uri); 975d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 976d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsException e) { 977d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: persist mms received message failure " + e, e); 978d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Just returns empty uri to RetrieveMmsRequest, which triggers a permanent failure 979d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 980d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: update mms received message failure " + e, e); 981d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Time update failure is ignored. 982d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 983d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return uri; 984d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 985d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 986d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update MMS message type in telephony; returns true if it succeeded. 987d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updateMmsMessageSendingStatus(final Context context, final Uri uri, 988d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int box, final long timestampInMillis) { 989d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 990d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = context.getContentResolver(); 991d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 992d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 993d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timestampInSeconds = timestampInMillis / 1000L; 994d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Mms.MESSAGE_BOX, box); 995d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Telephony.Mms.DATE, timestampInSeconds); 996d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int cnt = resolver.update(uri, values, null, null); 997d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cnt == 1) { 998d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 999d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "Mmsutils: Updated sending MMS " + uri + "; box = " + box 1000d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", date = " + timestampInSeconds + " (secs since epoch)"); 1001d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1002d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1003d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1004d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 1005d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: update mms message failure " + e, e); 1006d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 1007d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: update mms message failure " + e, e); 1008d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1009d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1010d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1011d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1012d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1013d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Parse values from a received sms message 1014d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1015d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param context 1016d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param msgs The received sms message content 1017d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param error The received sms error 1018d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Parsed values from the message 1019d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1020d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ContentValues parseReceivedSmsMessage( 1021d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context, final SmsMessage[] msgs, final int error) { 1022d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SmsMessage sms = msgs[0]; 1023d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1024d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1025d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.ADDRESS, sms.getDisplayOriginatingAddress()); 1026d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.BODY, buildMessageBodyFromPdus(msgs)); 1027d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (MmsUtils.hasSmsDateSentColumn()) { 1028d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO:: The boxing here seems unnecessary. 1029d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.DATE_SENT, Long.valueOf(sms.getTimestampMillis())); 1030d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1031d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.PROTOCOL, sms.getProtocolIdentifier()); 1032d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sms.getPseudoSubject().length() > 0) { 1033d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.SUBJECT, sms.getPseudoSubject()); 1034d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1035d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0); 1036d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.SERVICE_CENTER, sms.getServiceCenterAddress()); 1037d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Error code 1038d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.ERROR_CODE, error); 1039d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1040d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return values; 1041d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1042d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1043d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Some providers send formfeeds in their messages. Convert those formfeeds to newlines. 1044d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String replaceFormFeeds(final String s) { 1045d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return s == null ? "" : s.replace('\f', '\n'); 1046d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1047d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1048d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Parse the message body from message PDUs 1049d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String buildMessageBodyFromPdus(final SmsMessage[] msgs) { 1050d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (msgs.length == 1) { 1051d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // There is only one part, so grab the body directly. 1052d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return replaceFormFeeds(msgs[0].getDisplayMessageBody()); 1053d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1054d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Build up the body from the parts. 1055d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder body = new StringBuilder(); 1056d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final SmsMessage msg : msgs) { 1057d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1058d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // getDisplayMessageBody() can NPE if mWrappedMessage inside is null. 1059d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd body.append(msg.getDisplayMessageBody()); 1060d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final NullPointerException e) { 1061d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nothing to do 1062d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1063d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1064d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return replaceFormFeeds(body.toString()); 1065d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1066d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1067d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1068d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Parse the message date 1069d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static Long getMessageDate(final SmsMessage sms, long now) { 1070d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Use now for the timestamp to avoid confusion with clock 1071d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // drift between the handset and the SMSC. 1072d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to make sure the system is giving us a non-bogus time. 1073d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Calendar buildDate = new GregorianCalendar(2011, 8, 18); // 18 Sep 2011 1074d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Calendar nowDate = new GregorianCalendar(); 1075d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd nowDate.setTimeInMillis(now); 1076d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (nowDate.before(buildDate)) { 1077d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // It looks like our system clock isn't set yet because the current time right now 1078d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // is before an arbitrary time we made this build. Instead of inserting a bogus 1079d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // receive time in this case, use the timestamp of when the message was sent. 1080d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd now = sms.getTimestampMillis(); 1081d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1082d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return now; 1083d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1084d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1085d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1086d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * cleanseMmsSubject will take a subject that's says, "<Subject: no subject>", and return 1087d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * a null string. Otherwise it will return the original subject string. 1088d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param resources So the function can grab string resources 1089d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param subject the raw subject 1090d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return 1091d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1092d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String cleanseMmsSubject(final Resources resources, final String subject) { 1093d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(subject)) { 1094d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1095d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1096d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sNoSubjectStrings == null) { 1097d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sNoSubjectStrings = 1098d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resources.getStringArray(R.array.empty_subject_strings); 1099d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String noSubjectString : sNoSubjectStrings) { 1101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (subject.equalsIgnoreCase(noSubjectString)) { 1102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return subject; 1106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // return a semicolon separated list of phone numbers from a smsto: uri. 1109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getSmsRecipients(final Uri uri) { 1110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String recipients = uri.getSchemeSpecificPart(); 1111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int pos = recipients.indexOf('?'); 1112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (pos != -1) { 1113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients = recipients.substring(0, pos); 1114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients = replaceUnicodeDigits(recipients).replace(',', ';'); 1116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return recipients; 1117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This function was lifted from Telephony.PhoneNumberUtils because it was @hide 1120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Replace arabic/unicode digits with decimal digits. 1122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param number 1123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the number to be normalized. 1124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return the replaced number. 1125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String replaceUnicodeDigits(final String number) { 1127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder normalizedDigits = new StringBuilder(number.length()); 1128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final char c : number.toCharArray()) { 1129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int digit = Character.digit(c, 10); 1130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (digit != -1) { 1131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd normalizedDigits.append(digit); 1132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd normalizedDigits.append(c); 1134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return normalizedDigits.toString(); 1137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Whether the data roaming is enabled 1141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static boolean isDataRoamingEnabled() { 1143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean dataRoamingEnabled = false; 1144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver cr = Factory.get().getApplicationContext().getContentResolver(); 1145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastJB_MR1()) { 1146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dataRoamingEnabled = (Settings.Global.getInt(cr, Settings.Global.DATA_ROAMING, 0) != 0); 1147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dataRoamingEnabled = (Settings.System.getInt(cr, Settings.System.DATA_ROAMING, 0) != 0); 1149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return dataRoamingEnabled; 1151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Whether to auto retrieve MMS 1155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean allowMmsAutoRetrieve(final int subId) { 1157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Resources resources = context.getResources(); 1159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getSubscriptionPrefs(subId); 1160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean autoRetrieve = prefs.getBoolean( 1161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resources.getString(R.string.auto_retrieve_mms_pref_key), 1162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resources.getBoolean(R.bool.auto_retrieve_mms_pref_default)); 1163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (autoRetrieve) { 1164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean autoRetrieveInRoaming = prefs.getBoolean( 1165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resources.getString(R.string.auto_retrieve_mms_when_roaming_pref_key), 1166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resources.getBoolean(R.bool.auto_retrieve_mms_when_roaming_pref_default)); 1167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PhoneUtils phoneUtils = PhoneUtils.get(subId); 1168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if ((autoRetrieveInRoaming && phoneUtils.isDataRoamingEnabled()) 1169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd || !phoneUtils.isRoaming()) { 1170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Parse the message row id from a message Uri. 1178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param messageUri The input Uri 1180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The message row id if valid, otherwise -1 1181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long parseRowIdFromMessageUri(final Uri messageUri) { 1183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageUri != null) { 1185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return ContentUris.parseId(messageUri); 1186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final UnsupportedOperationException e) { 1188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nothing to do 1189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final NumberFormatException e) { 1190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nothing to do 1191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 1193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static SmsMessage getSmsMessageFromDeliveryReport(final Intent intent) { 1196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] pdu = intent.getByteArrayExtra("pdu"); 1197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return SmsMessage.createFromPdu(pdu); 1198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update the status and date_sent column of sms message in telephony provider 1202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param smsMessageUri 1204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param status 1205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param timeSentInMillis 1206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateSmsStatusAndDateSent(final Uri smsMessageUri, final int status, 1208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timeSentInMillis) { 1209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (smsMessageUri == null) { 1210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 1211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.STATUS, status); 1214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (MmsUtils.hasSmsDateSentColumn()) { 1215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Sms.DATE_SENT, timeSentInMillis); 1216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 1218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver.update(smsMessageUri, values, null/*where*/, null/*selectionArgs*/); 1219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the SQL selection statement for matching messages with media. 1223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Example for MMS part table: 1225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * "((ct LIKE 'image/%') 1226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * OR (ct LIKE 'video/%') 1227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * OR (ct LIKE 'audio/%') 1228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * OR (ct='application/ogg')) 1229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param contentTypeColumn The content-type column name 1231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The SQL selection statement for matching media types: image, video, audio 1232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getMediaTypeSelectionSql(final String contentTypeColumn) { 1234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return String.format( 1235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "((%s LIKE '%s') OR (%s LIKE '%s') OR (%s LIKE '%s') OR (%s='%s'))", 1237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentTypeColumn, 1238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "image/%", 1239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentTypeColumn, 1240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "video/%", 1241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentTypeColumn, 1242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "audio/%", 1243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentTypeColumn, 1244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.AUDIO_OGG); 1245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Max number of operands per SQL query for deleting SMS messages 1248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MAX_IDS_PER_QUERY = 128; 1249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete MMS messages with media parts. 1252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Because the telephony provider constraints, we can't use JOIN and delete messages in one 1254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * shot. We have to do a query first and then batch delete the messages based on IDs. 1255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The count of messages deleted. 1257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int deleteMediaMessages() { 1259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Do a query first 1260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // 1261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The WHERE clause has two parts: 1262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The first part is to select the exact same types of MMS messages as when we import them 1263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // (so that we don't delete messages that are not in local database) 1264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The second part is to select MMS with media parts, including image, video and audio 1265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selection = String.format( 1266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s AND (%s IN (SELECT %s FROM part WHERE %s))", 1268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getMmsTypeSelectionSql(), 1269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms._ID, 1270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Part.MSG_ID, 1271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getMediaTypeSelectionSql(Mms.Part.CONTENT_TYPE)); 1272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 1273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = resolver.query(Mms.CONTENT_URI, 1274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[]{ Mms._ID }, 1275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection, 1276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/, 1277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*sortOrder*/); 1278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int deleted = 0; 1279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long[] messageIds = new long[cursor.getCount()]; 1281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int i = 0; 1283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 1284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageIds[i++] = cursor.getLong(0); 1285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int totalIds = messageIds.length; 1290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (totalIds > 0) { 1291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Batch delete the messages using IDs 1292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We don't want to send all IDs at once since there is a limit on SQL statement 1293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int start = 0; start < totalIds; start += MAX_IDS_PER_QUERY) { 1294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int end = Math.min(start + MAX_IDS_PER_QUERY, totalIds); // excluding 1295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = end - start; 1296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String batchSelection = String.format( 1297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s IN %s", 1299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms._ID, 1300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getSqlInOperand(count)); 1301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] batchSelectionArgs = 1302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getSqlInOperandArgs(messageIds, start, count); 1303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int deletedForBatch = resolver.delete( 1304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.CONTENT_URI, 1305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batchSelection, 1306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batchSelectionArgs); 1307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 1308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "deleteMediaMessages: deleting IDs = " 1309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + Joiner.on(',').skipNulls().join(batchSelectionArgs) 1310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", deleted = " + deletedForBatch); 1311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deleted += deletedForBatch; 1313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return deleted; 1317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the (?,?,...) thing for the SQL IN operator by a count 1321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param count 1323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return 1324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getSqlInOperand(final int count) { 1326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (count <= 0) { 1327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder sb = new StringBuilder(); 1330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append("(?"); 1331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < count - 1; i++) { 1332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(",?"); 1333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(")"); 1335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sb.toString(); 1336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the args for SQL IN operator from a long ID array 1340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param ids The original long id array 1342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param start Start of the ids to fill the args 1343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param count Number of ids to pack 1344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The long array with the id args 1345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String[] getSqlInOperandArgs( 1347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long[] ids, final int start, final int count) { 1348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (count <= 0) { 1349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] args = new String[count]; 1352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < count; i++) { 1353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd args[i] = Long.toString(ids[start + i]); 1354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return args; 1356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete SMS and MMS messages that are earlier than a specific timestamp 1360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param cutOffTimestampInMillis The cut-off timestamp 1362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Total number of messages deleted. 1363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int deleteMessagesOlderThan(final long cutOffTimestampInMillis) { 1365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int deleted = 0; 1366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 1367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete old SMS 1368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smsSelection = String.format( 1369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s AND (%s<=%d)", 1371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getSmsTypeSelectionSql(), 1372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.DATE, 1373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cutOffTimestampInMillis); 1374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deleted += resolver.delete(Sms.CONTENT_URI, smsSelection, null/*selectionArgs*/); 1375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete old MMS 1376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String mmsSelection = String.format( 1377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s AND (%s<=%d)", 1379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getMmsTypeSelectionSql(), 1380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.DATE, 1381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cutOffTimestampInMillis / 1000L); 1382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deleted += resolver.delete(Mms.CONTENT_URI, mmsSelection, null/*selectionArgs*/); 1383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return deleted; 1384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update the read status of SMS/MMS messages by thread and timestamp 1388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The thread of sms/mms to change 1390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param timestampInMillis Change the status before this timestamp 1391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateSmsReadStatus(final long threadId, final long timestampInMillis) { 1393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 1394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put("read", 1); 1396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put("seen", 1); /* If you read it you saw it */ 1397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smsSelection = String.format( 1398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s=%d AND %s<=%d AND %s=0", 1400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.THREAD_ID, 1401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd threadId, 1402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.DATE, 1403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd timestampInMillis, 1404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.READ); 1405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver.update( 1406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.CONTENT_URI, 1407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values, 1408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smsSelection, 1409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/); 1410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String mmsSelection = String.format( 1411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s=%d AND %s<=%d AND %s=0", 1413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.THREAD_ID, 1414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd threadId, 1415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.DATE, 1416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd timestampInMillis / 1000L, 1417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.READ); 1418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver.update( 1419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.CONTENT_URI, 1420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values, 1421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mmsSelection, 1422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/); 1423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update the read status of a single MMS message by its URI 1427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param mmsUri 1429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param read 1430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateReadStatusForMmsMessage(final Uri mmsUri, final boolean read) { 1432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 1433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.READ, read ? 1 : 0); 1435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver.update(mmsUri, values, null/*where*/, null/*selectionArgs*/); 1436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class AttachmentInfo { 1439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public String mUrl; 1440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public String mContentType; 1441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int mWidth; 1442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int mHeight; 1443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Convert byte array to Java String using a charset name 1447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param bytes 1449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param charsetName 1450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return 1451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String bytesToString(final byte[] bytes, final String charsetName) { 1453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (bytes == null) { 1454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new String(bytes, charsetName); 1458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final UnsupportedEncodingException e) { 1459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.bytesToString: " + e, e); 1460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new String(bytes); 1461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Convert a Java String to byte array using a charset name 1466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param string 1468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param charsetName 1469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return 1470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static byte[] stringToBytes(final String string, final String charsetName) { 1472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (string == null) { 1473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return string.getBytes(charsetName); 1477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final UnsupportedEncodingException e) { 1478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils.stringToBytes: " + e, e); 1479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return string.getBytes(); 1480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String[] TEST_DATE_SENT_PROJECTION = new String[] { Sms.DATE_SENT }; 1484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Boolean sHasSmsDateSentColumn = null; 1485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Check if date_sent column exists on ICS and above devices. We need to do a test 1487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * query to figure that out since on some ICS+ devices, somehow the date_sent column does 1488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * not exist. http://b/17629135 tracks the associated compliance test. 1489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Whether "date_sent" column exists in sms table 1491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean hasSmsDateSentColumn() { 1493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sHasSmsDateSentColumn == null) { 1494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = context.getContentResolver(); 1498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = SqliteWrapper.query( 1499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 1500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver, 1501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.CONTENT_URI, 1502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd TEST_DATE_SENT_PROJECTION, 1503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selection*/, 1504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/, 1505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Sms.DATE_SENT + " ASC LIMIT 1"); 1506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sHasSmsDateSentColumn = true; 1507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 1508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "date_sent in sms table does not exist", e); 1509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sHasSmsDateSentColumn = false; 1510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sHasSmsDateSentColumn; 1517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String[] TEST_CARRIERS_PROJECTION = 1520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Telephony.Carriers.MMSC }; 1521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static Boolean sUseSystemApn = null; 1522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Check if we can access the APN data in the Telephony provider. Access was restricted in 1524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * JB MR1 (and some JB MR2) devices. If we can't access the APN, we have to fall back and use 1525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * a private table in our own app. 1526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Whether we can access the system APN table 1528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean useSystemApnTable() { 1530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sUseSystemApn == null) { 1531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = context.getContentResolver(); 1535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = SqliteWrapper.query( 1536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 1537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resolver, 1538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Telephony.Carriers.CONTENT_URI, 1539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd TEST_CARRIERS_PROJECTION, 1540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selection*/, 1541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/, 1542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null); 1543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sUseSystemApn = true; 1544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SecurityException e) { 1545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "Can't access system APN, using internal table", e); 1546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sUseSystemApn = false; 1547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sUseSystemApn; 1554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For the internal debugger only 1557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void setUseSystemApnTable(final boolean turnOn) { 1558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!turnOn) { 1559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We're not turning on to the system table. Instead, we're using our internal table. 1560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int osVersion = OsUtil.getApiVersion(); 1561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (osVersion != android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 1562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We're turning on local APNs on a device where we wouldn't normally have the 1563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // local APN table. Build it here. 1564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase(); 1566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Do we already have the table? 1568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = database.query(ApnDatabase.APN_TABLE, 1571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ApnDatabase.APN_PROJECTION, 1572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null, null, null, null); 1573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception e) { 1574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Apparently there's no table, create it now. 1575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ApnDatabase.forceBuildAndLoadApnTables(); 1576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sUseSystemApn = turnOn; 1584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Checks if we should dump sms, based on both the setting and the global debug 1588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * flag 1589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return if dump sms is enabled 1591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isDumpSmsEnabled() { 1593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!DebugUtils.isDebugEnabled()) { 1594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getDumpSmsOrMmsPref(R.string.dump_sms_pref_key, R.bool.dump_sms_pref_default); 1597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Checks if we should dump mms, based on both the setting and the global debug 1601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * flag 1602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return if dump mms is enabled 1604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isDumpMmsEnabled() { 1606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!DebugUtils.isDebugEnabled()) { 1607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getDumpSmsOrMmsPref(R.string.dump_mms_pref_key, R.bool.dump_mms_pref_default); 1610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Load the value of dump sms or mms setting preference 1614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static boolean getDumpSmsOrMmsPref(final int prefKeyRes, final int defaultKeyRes) { 1616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Resources resources = context.getResources(); 1618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getApplicationPrefs(); 1619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String key = resources.getString(prefKeyRes); 1620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean defaultValue = resources.getBoolean(defaultKeyRes); 1621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return prefs.getBoolean(key, defaultValue); 1622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final Uri MMS_PART_CONTENT_URI = Uri.parse("content://mms/part"); 1625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Load MMS from telephony 1628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param mmsUri The MMS pdu Uri 1630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return A memory copy of the MMS pdu including parts (but not addresses) 1631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static DatabaseMessages.MmsMessage loadMms(final Uri mmsUri) { 1633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = context.getContentResolver(); 1635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsMessage mms = null; 1636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Load pdu first 1638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = SqliteWrapper.query(context, resolver, 1640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mmsUri, 1641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsMessage.getProjection(), 1642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selection*/, null/*selectionArgs*/, null/*sortOrder*/); 1643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null && cursor.moveToFirst()) { 1644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms = DatabaseMessages.MmsMessage.get(cursor); 1645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 1647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsLoader: query pdu failure: " + e, e); 1648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mms == null) { 1654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Load parts except SMIL 1657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: we may need to load SMIL part in the future. 1658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long rowId = MmsUtils.parseRowIdFromMessageUri(mmsUri); 1659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selection = String.format( 1660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 1661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s != '%s' AND %s = ?", 1662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Part.CONTENT_TYPE, 1663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.APP_SMIL, 1664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Part.MSG_ID); 1665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = null; 1666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = SqliteWrapper.query(context, resolver, 1668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MMS_PART_CONTENT_URI, 1669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsPart.PROJECTION, 1670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection, 1671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Long.toString(rowId) }, 1672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*sortOrder*/); 1673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 1675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.addPart(DatabaseMessages.MmsPart.get(cursor, true/*loadMedia*/)); 1676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 1679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsLoader: query parts failure: " + e, e); 1680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mms; 1686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the sender of an MMS message 1690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param recipients The recipient list of the message 1692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param mmsUri The pdu uri of the MMS 1693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The sender phone number of the MMS 1694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getMmsSender(final List<String> recipients, final String mmsUri) { 1696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 1697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We try to avoid the database query. 1698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If this is a 1v1 conv., then the other party is the sender 1699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (recipients != null && recipients.size() == 1) { 1700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return recipients.get(0); 1701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Otherwise, we have to query the MMS addr table for sender address 1703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This should only be done for a received group mms message 1704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = SqliteWrapper.query( 1705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 1706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context.getContentResolver(), 1707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri.withAppendedPath(Uri.parse(mmsUri), "addr"), 1708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Mms.Addr.ADDRESS, Mms.Addr.CHARSET }, 1709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Addr.TYPE + "=" + PduHeaders.FROM, 1710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*selectionArgs*/, 1711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*sortOrder*/); 1712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return DatabaseMessages.MmsAddr.get(cursor); 1716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int bugleStatusForMms(final boolean isOutgoing, final boolean isNotification, 1725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int messageBox) { 1726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int bugleStatus = MessageData.BUGLE_STATUS_UNKNOWN; 1727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For a message we sync either 1728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isOutgoing) { 1729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageBox == Mms.MESSAGE_BOX_OUTBOX || messageBox == Mms.MESSAGE_BOX_FAILED) { 1730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Not sent counts as failed and available for manual resend 1731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bugleStatus = MessageData.BUGLE_STATUS_OUTGOING_FAILED; 1732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Otherwise outgoing message is complete 1734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bugleStatus = MessageData.BUGLE_STATUS_OUTGOING_COMPLETE; 1735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (isNotification) { 1737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Incoming MMS notifications we sync count as failed and available for manual download 1738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bugleStatus = MessageData.BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD; 1739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Other incoming MMS messages are complete 1741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bugleStatus = MessageData.BUGLE_STATUS_INCOMING_COMPLETE; 1742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return bugleStatus; 1744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData createMmsMessage(final DatabaseMessages.MmsMessage mms, 1747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final String participantId, final String selfId, 1748d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int bugleStatus) { 1749d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(mms); 1750d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isNotification = (mms.mMmsMessageType == 1751d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND); 1752d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int rawMmsStatus = (bugleStatus < MessageData.BUGLE_STATUS_FIRST_INCOMING 1753d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ? mms.mRetrieveStatus : mms.mResponseStatus); 1754d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1755d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message = MessageData.createMmsMessage(mms.getUri(), 1756d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId, selfId, conversationId, isNotification, bugleStatus, 1757d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.mContentLocation, mms.mTransactionId, mms.mPriority, mms.mSubject, 1758d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.mSeen, mms.mRead, mms.getSize(), rawMmsStatus, 1759d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.mExpiryInMillis, mms.mSentTimestampInMillis, mms.mTimestampInMillis); 1760d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1761d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final DatabaseMessages.MmsPart part : mms.mParts) { 1762d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart = MmsUtils.createMmsMessagePart(part); 1763d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Import media and text parts (skip SMIL and others) 1764d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messagePart != null) { 1765d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.addPart(messagePart); 1766d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1767d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1768d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1769d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!message.getParts().iterator().hasNext()) { 1770d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.addPart(MessagePartData.createEmptyMessagePart()); 1771d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1772d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1773d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 1774d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1775d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1776d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessagePartData createMmsMessagePart(final DatabaseMessages.MmsPart part) { 1777d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData messagePart = null; 1778d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isText()) { 1779d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int mmsTextLengthLimit = 1780d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservices.get().getInt(BugleGservicesKeys.MMS_TEXT_LIMIT, 1781d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.MMS_TEXT_LIMIT_DEFAULT); 1782d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String text = part.mText; 1783d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (text != null && text.length() > mmsTextLengthLimit) { 1784d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Limit the text to a reasonable value. We ran into a situation where a vcard 1785d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // with a photo was sent as plain text. The massive amount of text caused the 1786d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // app to hang, ANR, and eventually crash in native text code. 1787d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd text = text.substring(0, mmsTextLengthLimit); 1788d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1789d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart = MessagePartData.createTextMessagePart(text); 1790d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (part.isMedia()) { 1791d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart = MessagePartData.createMediaMessagePart(part.mContentType, 1792d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.getDataUri(), MessagePartData.UNSPECIFIED_SIZE, 1793d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData.UNSPECIFIED_SIZE); 1794d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1795d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return messagePart; 1796d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1797d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1798d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class StatusPlusUri { 1799d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The request status to be as the result of the operation 1800d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // e.g. MMS_REQUEST_MANUAL_RETRY 1801d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final int status; 1802d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The raw telephony status 1803d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final int rawStatus; 1804d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The raw telephony URI 1805d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final Uri uri; 1806d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The operation result code from system api invocation (sent by system) 1807d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // or mapped from internal exception (sent by app) 1808d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final int resultCode; 1809d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1810d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public StatusPlusUri(final int status, final int rawStatus, final Uri uri) { 1811d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.status = status; 1812d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.rawStatus = rawStatus; 1813d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.uri = uri; 1814d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd resultCode = MessageData.UNKNOWN_RESULT_CODE; 1815d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1816d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1817d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public StatusPlusUri(final int status, final int rawStatus, final Uri uri, 1818d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int resultCode) { 1819d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.status = status; 1820d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.rawStatus = rawStatus; 1821d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.uri = uri; 1822d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.resultCode = resultCode; 1823d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1824d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1825d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1826d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class SendReqResp { 1827d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SendReq mSendReq; 1828d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SendConf mSendConf; 1829d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1830d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SendReqResp(final SendReq sendReq, final SendConf sendConf) { 1831d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSendReq = sendReq; 1832d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSendConf = sendConf; 1833d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1834d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1835d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1836d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1837d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Returned when sending/downloading MMS via platform APIs. In that case, we have to wait to 1838d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * receive the pending intent to determine status. 1839d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1840d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final StatusPlusUri STATUS_PENDING = new StatusPlusUri(-1, -1, null); 1841d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1842d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static StatusPlusUri downloadMmsMessage(final Context context, final Uri notificationUri, 1843d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int subId, final String subPhoneNumber, final String transactionId, 1844d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentLocation, final boolean autoDownload, 1845d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long receivedTimestampInSeconds, Bundle extras) { 1846d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(contentLocation)) { 1847d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: Download from empty content location URL"); 1848d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri( 1849d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MMS_REQUEST_NO_RETRY, MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, null); 1850d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1851d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isMmsDataAvailable(subId)) { 1852d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, 1853d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "MmsUtils: failed to download message, no data available"); 1854d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(MMS_REQUEST_MANUAL_RETRY, 1855d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, 1856d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, 1857d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SmsManager.MMS_ERROR_NO_DATA_NETWORK); 1858d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1859d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int status = MMS_REQUEST_MANUAL_RETRY; 1860d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1861d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd RetrieveConf retrieveConf = null; 1862d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (DebugUtils.isDebugEnabled() && 1863d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MediaScratchFileProvider 1864d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .isMediaScratchSpaceUri(Uri.parse(contentLocation))) { 1865d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 1866d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "MmsUtils: Reading MMS from dump file: " + contentLocation); 1867d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1868d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String fileName = Uri.parse(contentLocation).getPathSegments().get(1); 1869d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] data = DebugUtils.receiveFromDumpFile(fileName); 1870d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrieveConf = receiveFromDumpFile(data); 1871d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1872d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 1873d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "MmsUtils: Downloading MMS via MMS lib API; notification " 1874d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "message: " + notificationUri); 1875d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1876d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL_MR1()) { 1877d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (subId < 0) { 1878d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: Incoming MMS came from unknown SIM"); 1879d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new MmsFailureException(MMS_REQUEST_NO_RETRY, 1880d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "Message from unknown SIM"); 1881d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1882d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1883d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(subId == ParticipantData.DEFAULT_SELF_SUB_ID); 1884d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1885d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (extras == null) { 1886d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras = new Bundle(); 1887d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1888d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putParcelable(DownloadMmsAction.EXTRA_NOTIFICATION_URI, notificationUri); 1889d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putInt(DownloadMmsAction.EXTRA_SUB_ID, subId); 1890d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putString(DownloadMmsAction.EXTRA_SUB_PHONE_NUMBER, subPhoneNumber); 1891d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putString(DownloadMmsAction.EXTRA_TRANSACTION_ID, transactionId); 1892d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putString(DownloadMmsAction.EXTRA_CONTENT_LOCATION, contentLocation); 1893d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putBoolean(DownloadMmsAction.EXTRA_AUTO_DOWNLOAD, autoDownload); 1894d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putLong(DownloadMmsAction.EXTRA_RECEIVED_TIMESTAMP, 1895d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd receivedTimestampInSeconds); 1896d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1897d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsSender.downloadMms(context, subId, contentLocation, extras); 1898d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return STATUS_PENDING; // Download happens asynchronously; no status to return 1899d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1900d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return insertDownloadedMessageAndSendResponse(context, notificationUri, subId, 1901d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subPhoneNumber, transactionId, contentLocation, autoDownload, 1902d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd receivedTimestampInSeconds, retrieveConf); 1903d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1904d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsFailureException e) { 1905d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to download message " + notificationUri, e); 1906d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = e.retryHint; 1907d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 1908d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to download message " + notificationUri, e); 1909d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1910d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(status, PDU_HEADER_VALUE_UNDEFINED, null); 1911d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1912d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1913d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static StatusPlusUri insertDownloadedMessageAndSendResponse(final Context context, 1914d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri notificationUri, final int subId, final String subPhoneNumber, 1915d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String transactionId, final String contentLocation, 1916d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean autoDownload, final long receivedTimestampInSeconds, 1917d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RetrieveConf retrieveConf) { 1918d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] transactionIdBytes = stringToBytes(transactionId, "UTF-8"); 1919d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri messageUri = null; 1920d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int status = MMS_REQUEST_MANUAL_RETRY; 1921d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int retrieveStatus = PDU_HEADER_VALUE_UNDEFINED; 1922d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1923d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrieveStatus = retrieveConf.getRetrieveStatus(); 1924d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (retrieveStatus == PduHeaders.RETRIEVE_STATUS_OK) { 1925d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_SUCCEEDED; 1926d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (retrieveStatus >= PduHeaders.RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE && 1927d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrieveStatus < PduHeaders.RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE) { 1928d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_AUTO_RETRY; 1929d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1930d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // else not meant to retry download 1931d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_NO_RETRY; 1932d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to retrieve message; retrieveStatus: " 1933d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + retrieveStatus); 1934d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1935d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(1); 1936d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.RETRIEVE_STATUS, retrieveConf.getRetrieveStatus()); 1937d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SqliteWrapper.update(context, context.getContentResolver(), 1938d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notificationUri, values, null, null); 1939d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1940d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (status == MMS_REQUEST_SUCCEEDED) { 1941d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Send response of the notification 1942d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (autoDownload) { 1943d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendNotifyResponseForMmsDownload(context, subId, transactionIdBytes, 1944d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentLocation, PduHeaders.STATUS_RETRIEVED); 1945d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1946d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendAcknowledgeForMmsDownload(context, subId, transactionIdBytes, contentLocation); 1947d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1948d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1949d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert downloaded message into telephony 1950d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri inboxUri = MmsUtils.insertReceivedMmsMessage(context, retrieveConf, subId, 1951d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subPhoneNumber, receivedTimestampInSeconds, contentLocation); 1952d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, ContentUris.parseId(inboxUri)); 1953d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (status == MMS_REQUEST_AUTO_RETRY) { 1954d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For a retry do nothing 1955d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (status == MMS_REQUEST_MANUAL_RETRY && autoDownload) { 1956d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Failure from autodownload - just treat like manual download 1957d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendNotifyResponseForMmsDownload(context, subId, transactionIdBytes, 1958d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentLocation, PduHeaders.STATUS_DEFERRED); 1959d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1960d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(status, retrieveStatus, messageUri); 1961d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1962d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1963d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1964d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Send response for MMS download - catches and ignores errors 1965d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1966d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void sendNotifyResponseForMmsDownload(final Context context, final int subId, 1967d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] transactionId, final String contentLocation, final int status) { 1968d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1969d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 1970d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "MmsUtils: Sending M-NotifyResp.ind for received MMS, status: " 1971d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + String.format("0x%X", status)); 1972d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1973d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (contentLocation == null) { 1974d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send NotifyResp; contentLocation is null"); 1975d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 1976d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1977d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (transactionId == null) { 1978d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send NotifyResp; transaction id is null"); 1979d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 1980d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1981d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isMmsDataAvailable(subId)) { 1982d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send NotifyResp; no data available"); 1983d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 1984d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1985d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsSender.sendNotifyResponseForMmsDownload( 1986d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, subId, transactionId, contentLocation, status); 1987d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsFailureException e) { 1988d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "sendNotifyResponseForMmsDownload: failed to retrieve message " + e, e); 1989d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 1990d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "sendNotifyResponseForMmsDownload: failed to retrieve message " + e, e); 1991d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1992d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1993d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1994d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1995d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Send acknowledge for mms download - catched and ignores errors 1996d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1997d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void sendAcknowledgeForMmsDownload(final Context context, final int subId, 1998d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] transactionId, final String contentLocation) { 1999d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2000d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 2001d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "MmsUtils: Sending M-Acknowledge.ind for received MMS"); 2002d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2003d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (contentLocation == null) { 2004d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send AckInd; contentLocation is null"); 2005d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 2006d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2007d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (transactionId == null) { 2008d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send AckInd; transaction id is null"); 2009d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 2010d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2011d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isMmsDataAvailable(subId)) { 2012d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Can't send AckInd; no data available"); 2013d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 2014d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2015d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsSender.sendAcknowledgeForMmsDownload(context, subId, transactionId, contentLocation); 2016d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsFailureException e) { 2017d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "sendAcknowledgeForMmsDownload: failed to retrieve message " + e, e); 2018d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 2019d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "sendAcknowledgeForMmsDownload: failed to retrieve message " + e, e); 2020d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2021d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2022d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2023d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2024d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Try parsing a PDU without knowing the carrier. This is useful for importing 2025d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS or storing draft when carrier info is not available 2026d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2027d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param data The PDU data 2028d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Parsed PDU, null if failed to parse 2029d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2030d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static GenericPdu parsePduForAnyCarrier(final byte[] data) { 2031d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd GenericPdu pdu = null; 2032d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2033d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pdu = (new PduParser(data, true/*parseContentDisposition*/)).parse(); 2034d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final RuntimeException e) { 2035d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "parsePduForAnyCarrier: Failed to parse PDU with content disposition", 2036d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd e); 2037d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2038d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (pdu == null) { 2039d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2040d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pdu = (new PduParser(data, false/*parseContentDisposition*/)).parse(); 2041d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final RuntimeException e) { 2042d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, 2043d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "parsePduForAnyCarrier: Failed to parse PDU without content disposition", 2044d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd e); 2045d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2046d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2047d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return pdu; 2048d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2049d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2050d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static RetrieveConf receiveFromDumpFile(final byte[] data) throws MmsFailureException { 2051d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final GenericPdu pdu = parsePduForAnyCarrier(data); 2052d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (pdu == null || !(pdu instanceof RetrieveConf)) { 2053d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "receiveFromDumpFile: Parsing retrieved PDU failure"); 2054d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new MmsFailureException(MMS_REQUEST_MANUAL_RETRY, "Failed reading dump file"); 2055d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2056d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (RetrieveConf) pdu; 2057d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2058d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2059d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static boolean isMmsDataAvailable(final int subId) { 2060d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL_MR1()) { 2061d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // L_MR1 above may support sending mms via wifi 2062d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 2063d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2064d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PhoneUtils phoneUtils = PhoneUtils.get(subId); 2065d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return !phoneUtils.isAirplaneModeOn() && phoneUtils.isMobileDataEnabled(); 2066d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2067d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2068d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static boolean isSmsDataAvailable(final int subId) { 2069d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL_MR1()) { 2070d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // L_MR1 above may support sending sms via wifi 2071d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 2072d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2073d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PhoneUtils phoneUtils = PhoneUtils.get(subId); 2074d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return !phoneUtils.isAirplaneModeOn(); 2075d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2076d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2077d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isMobileDataEnabled(final int subId) { 2078d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PhoneUtils phoneUtils = PhoneUtils.get(subId); 2079d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return phoneUtils.isMobileDataEnabled(); 2080d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2081d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2082d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isAirplaneModeOn(final int subId) { 2083d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PhoneUtils phoneUtils = PhoneUtils.get(subId); 2084d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return phoneUtils.isAirplaneModeOn(); 2085d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2086d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2087d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static StatusPlusUri sendMmsMessage(final Context context, final int subId, 2088d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri messageUri, final Bundle extras) { 2089d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int status = MMS_REQUEST_MANUAL_RETRY; 2090d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int rawStatus = MessageData.RAW_TELEPHONY_STATUS_UNDEFINED; 2091d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isMmsDataAvailable(subId)) { 2092d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: failed to send message, no data available"); 2093d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(MMS_REQUEST_MANUAL_RETRY, 2094d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, 2095d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri, 2096d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SmsManager.MMS_ERROR_NO_DATA_NETWORK); 2097d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2098d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPersister persister = PduPersister.getPduPersister(context); 2099d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendReq sendReq = (SendReq) persister.load(messageUri); 2101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sendReq == null) { 2102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: Sending MMS was deleted; uri = " + messageUri); 2103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(MMS_REQUEST_NO_RETRY, 2104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, messageUri); 2105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 2107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, String.format("MmsUtils: Sending MMS, message uri: %s", messageUri)); 2108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd extras.putInt(SendMessageAction.KEY_SUB_ID, subId); 2110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsSender.sendMms(context, subId, messageUri, sendReq, extras); 2111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return STATUS_PENDING; 2112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsFailureException e) { 2113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = e.retryHint; 2114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd rawStatus = e.rawStatus; 2115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to send message " + e, e); 2116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 2117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to send message " + e, e); 2118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IllegalArgumentException e) { 2119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: invalid message to send " + e, e); 2120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsException e) { 2121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to send message " + e, e); 2122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If we get here, some exception occurred 2124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(status, rawStatus, messageUri); 2125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static StatusPlusUri updateSentMmsMessageStatus(final Context context, 2128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri messageUri, final SendConf sendConf) { 2129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int status = MMS_REQUEST_MANUAL_RETRY; 2130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int respStatus = sendConf.getResponseStatus(); 2131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(2); 2133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.RESPONSE_STATUS, respStatus); 2134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] messageId = sendConf.getMessageId(); 2135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageId != null && messageId.length > 0) { 2136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(Mms.MESSAGE_ID, PduPersister.toIsoString(messageId)); 2137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SqliteWrapper.update(context, context.getContentResolver(), 2139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri, values, null, null); 2140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (respStatus == PduHeaders.RESPONSE_STATUS_OK) { 2141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_SUCCEEDED; 2142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE || 2143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM || 2144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) { 2145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_AUTO_RETRY; 2146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // else permanent failure 2148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to send message; respStatus = " 2149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + String.format("0x%X", respStatus)); 2150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new StatusPlusUri(status, respStatus, messageUri); 2152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void clearMmsStatus(final Context context, final Uri uri) { 2155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Messaging application can leave invalid values in STATUS field of M-Notification.ind 2156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // messages. Take this opportunity to clear it. 2157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Downloading status just kept in local db and not reflected into telephony. 2158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(1); 2159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.putNull(Mms.STATUS); 2160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SqliteWrapper.update(context, context.getContentResolver(), 2161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uri, values, null, null); 2162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Selection for new dedup algorithm: 2165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // ((m_type<>130) OR (exp>NOW)) AND (date>NOW-7d) AND (date<NOW+7d) AND (ct_l=xxxxxx) 2166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // i.e. If it is NotificationInd and not expired or not NotificationInd 2167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // AND message is received with +/- 7 days from now 2168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // AND content location is the input URL 2169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String DUP_NOTIFICATION_QUERY_SELECTION = 2170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "((" + Mms.MESSAGE_TYPE + "<>?) OR (" + Mms.EXPIRY + ">?)) AND (" 2171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + Mms.DATE + ">?) AND (" + Mms.DATE + "<?) AND (" + Mms.CONTENT_LOCATION + 2172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "=?)"; 2173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Selection for old behavior: only checks NotificationInd and its content location 2174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String DUP_NOTIFICATION_QUERY_SELECTION_OLD = 2175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "(" + Mms.MESSAGE_TYPE + "=?) AND (" + Mms.CONTENT_LOCATION + "=?)"; 2176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int MAX_RETURN = 32; 2178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String[] getDupNotifications(final Context context, final NotificationInd nInd) { 2179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] rawLocation = nInd.getContentLocation(); 2180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (rawLocation != null) { 2181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String location = new String(rawLocation); 2182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We can not be sure if the content location of an MMS is globally and historically 2183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // unique. So we limit the dedup time within the last 7 days 2184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // (or configured by gservices remotely). If the same content location shows up after 2185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // that, we will download regardless. Duplicated message is better than no message. 2186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String selection; 2187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String[] selectionArgs; 2188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timeLimit = BugleGservices.get().getLong( 2189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS, 2190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS_DEFAULT); 2191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (timeLimit > 0) { 2192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // New dedup algorithm 2193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection = DUP_NOTIFICATION_QUERY_SELECTION; 2194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long nowSecs = System.currentTimeMillis() / 1000; 2195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timeLowerBoundSecs = nowSecs - timeLimit; 2196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Need upper bound to protect against clock change so that a message has a time 2197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // stamp in the future 2198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timeUpperBoundSecs = nowSecs + timeLimit; 2199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectionArgs = new String[] { 2200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), 2201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Long.toString(nowSecs), 2202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Long.toString(timeLowerBoundSecs), 2203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Long.toString(timeUpperBoundSecs), 2204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd location 2205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 2206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If time limit is 0, we revert back to old behavior in case the new 2208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // dedup algorithm behaves badly 2209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection = DUP_NOTIFICATION_QUERY_SELECTION_OLD; 2210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectionArgs = new String[] { 2211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), 2212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd location 2213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 2214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 2216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = SqliteWrapper.query( 2218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, context.getContentResolver(), 2219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.CONTENT_URI, new String[] { Mms._ID }, 2220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection, selectionArgs, null); 2221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int dupCount = cursor.getCount(); 2222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (dupCount > 0) { 2223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We already received the same notification before. 2224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't want to return too many dups. It is only for debugging. 2225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int returnCount = dupCount < MAX_RETURN ? dupCount : MAX_RETURN; 2226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] dups = new String[returnCount]; 2227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; cursor.moveToNext() && i < returnCount; i++) { 2228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dups[i] = cursor.getString(0); 2229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return dups; 2231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 2233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "query failure: " + e, e); 2234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 2235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 2236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 2239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Try parse the address using RFC822 format. If it fails to parse, then return the 2243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * original address 2244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param address The MMS ind sender address to parse 2246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The real address. If in RFC822 format, returns the correct email. 2247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String parsePotentialRfc822EmailAddress(final String address) { 2249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (address == null || !address.contains("@") || !address.contains("<")) { 2250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return address; 2251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address); 2253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (tokens != null && tokens.length > 0) { 2254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final Rfc822Token token : tokens) { 2255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (token != null && !TextUtils.isEmpty(token.getAddress())) { 2256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return token.getAddress(); 2257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return address; 2261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static DatabaseMessages.MmsMessage processReceivedPdu(final Context context, 2264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] pushData, final int subId, final String subPhoneNumber) { 2265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Parse data 2266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert placeholder row to telephony and local db 2268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Get raw PDU push-data from the message and parse it 2269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduParser parser = new PduParser(pushData, 2270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsConfig.get(subId).getSupportMmsContentDisposition()); 2271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final GenericPdu pdu = parser.parse(); 2272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (null == pdu) { 2274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Invalid PUSH data"); 2275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 2276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final PduPersister p = PduPersister.getPduPersister(context); 2279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int type = pdu.getMessageType(); 2280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri messageUri = null; 2282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (type) { 2283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: 2284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: { 2285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Should this be commented out? 2286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// threadId = findThreadId(context, pdu, type); 2287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// if (threadId == -1) { 2288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// // The associated SendReq isn't found, therefore skip 2289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// // processing this PDU. 2290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// break; 2291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// } 2292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// Uri uri = p.persist(pdu, Inbox.CONTENT_URI, true, 2294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null); 2295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// // Update thread ID for ReadOrigInd & DeliveryInd. 2296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// ContentValues values = new ContentValues(1); 2297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// values.put(Mms.THREAD_ID, threadId); 2298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd// SqliteWrapper.update(mContext, cr, uri, values, null, null); 2299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "Received unsupported WAP Push, type=" + type); 2300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: { 2303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final NotificationInd nInd = (NotificationInd) pdu; 2304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (MmsConfig.get(subId).getTransIdEnabled()) { 2306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte [] contentLocationTemp = nInd.getContentLocation(); 2307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if ('=' == contentLocationTemp[contentLocationTemp.length - 1]) { 2308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte [] transactionIdTemp = nInd.getTransactionId(); 2309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte [] contentLocationWithId = 2310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new byte [contentLocationTemp.length 2311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + transactionIdTemp.length]; 2312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd System.arraycopy(contentLocationTemp, 0, contentLocationWithId, 2313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0, contentLocationTemp.length); 2314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd System.arraycopy(transactionIdTemp, 0, contentLocationWithId, 2315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentLocationTemp.length, transactionIdTemp.length); 2316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd nInd.setContentLocation(contentLocationWithId); 2317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] dups = getDupNotifications(context, nInd); 2320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (dups == null) { 2321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Do we handle Rfc822 Email Addresses? 2322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final String contentLocation = 2323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // MmsUtils.bytesToString(nInd.getContentLocation(), "UTF-8"); 2324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final byte[] transactionId = nInd.getTransactionId(); 2325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final long messageSize = nInd.getMessageSize(); 2326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final long expiry = nInd.getExpiry(); 2327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final String transactionIdString = 2328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // MmsUtils.bytesToString(transactionId, "UTF-8"); 2329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final EncodedStringValue fromEncoded = nInd.getFrom(); 2331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // An mms ind received from email address will have from address shown as 2332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // "John Doe <johndoe@foobar.com>" but the actual received message will only 2333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // have the email address. So let's try to parse the RFC822 format to get the 2334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // real email. Otherwise we will create two conversations for the MMS 2335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // notification and the actual MMS message if auto retrieve is disabled. 2336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //final String from = parsePotentialRfc822EmailAddress( 2337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // fromEncoded != null ? fromEncoded.getString() : null); 2338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri inboxUri = null; 2340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd inboxUri = p.persist(pdu, Mms.Inbox.CONTENT_URI, subId, subPhoneNumber, 2342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null); 2343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, 2344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentUris.parseId(inboxUri)); 2345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsException e) { 2346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Failed to save the data from PUSH: type=" + type, e); 2347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "Received WAP Push is a dup: " + Joiner.on(',').join(dups)); 2350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 2351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "Dup WAP Push url=" + new String(nInd.getContentLocation())); 2352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd default: 2357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Received unrecognized WAP Push, type=" + type); 2358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsMessage mms = null; 2361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageUri != null) { 2362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms = MmsUtils.loadMms(messageUri); 2363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mms; 2365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static Uri insertSendingMmsMessage(final Context context, final List<String> recipients, 2368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData content, final int subId, final String subPhoneNumber, 2369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timestamp) { 2370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendReq sendReq = createMmsSendReq( 2371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, subId, recipients.toArray(new String[recipients.size()]), content, 2372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DEFAULT_DELIVERY_REPORT_MODE, 2373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DEFAULT_READ_REPORT_MODE, 2374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DEFAULT_EXPIRY_TIME_IN_SECONDS, 2375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DEFAULT_PRIORITY, 2376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd timestamp); 2377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Uri messageUri = null; 2378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sendReq != null) { 2379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri outboxUri = MmsUtils.insertSendReq(context, sendReq, subId, subPhoneNumber); 2380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (outboxUri != null) { 2381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageUri = ContentUris.withAppendedId(Telephony.Mms.CONTENT_URI, 2382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentUris.parseId(outboxUri)); 2383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 2384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "Mmsutils: Inserted sending MMS message into telephony, uri: " 2385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + outboxUri); 2386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "insertSendingMmsMessage: failed to persist message into telephony"); 2389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return messageUri; 2392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData readSendingMmsMessage(final Uri messageUri, 2395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final String participantId, final String selfId) { 2396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData message = null; 2397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageUri != null) { 2398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseMessages.MmsMessage mms = MmsUtils.loadMms(messageUri); 2399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Make sure that the message has not been deleted from the Telephony DB 2401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mms != null) { 2402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Transform the message 2403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = MmsUtils.createMmsMessage(mms, conversationId, participantId, selfId, 2404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.BUGLE_STATUS_OUTGOING_RESENDING); 2405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 2408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Create an MMS message with subject, text and image 2412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Both the M-Send.req and the M-Send.conf for processing in the caller 2414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @throws MmsException 2415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static SendReq createMmsSendReq(final Context context, final int subId, 2417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] recipients, final MessageData message, 2418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean requireDeliveryReport, final boolean requireReadReport, 2419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long expiryTime, final int priority, final long timestampMillis) { 2420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(context); 2421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (recipients == null || recipients.length < 1) { 2422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new IllegalArgumentException("MMS sendReq no recipient"); 2423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Make a copy so we don't propagate changes to recipients to outside of this method 2426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] recipientsCopy = new String[recipients.length]; 2427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't send phone number as is since some received phone number is malformed 2428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // for sending. We need to strip the separators. 2429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < recipients.length; i++) { 2430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String recipient = recipients[i]; 2431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (EmailAddress.isValidEmail(recipients[i])) { 2432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't do stripping for emails 2433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipientsCopy[i] = recipient; 2434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipientsCopy[i] = stripPhoneNumberSeparators(recipient); 2436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SendReq sendReq = null; 2440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendReq = createSendReq(context, subId, recipientsCopy, 2442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message, requireDeliveryReport, 2443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requireReadReport, expiryTime, priority, timestampMillis); 2444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 2445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "InvalidHeaderValue creating sendReq PDU"); 2446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final OutOfMemoryError e) { 2447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "Out of memory error creating sendReq PDU"); 2448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sendReq; 2450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Stripping out the invalid characters in a phone number before sending 2454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * MMS. We only keep alphanumeric and '*', '#', '+'. 2455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String stripPhoneNumberSeparators(final String phoneNumber) { 2457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (phoneNumber == null) { 2458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 2459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int len = phoneNumber.length(); 2461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder ret = new StringBuilder(len); 2462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < len; i++) { 2463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final char c = phoneNumber.charAt(i); 2464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (Character.isLetterOrDigit(c) || c == '+' || c == '*' || c == '#') { 2465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ret.append(c); 2466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return ret.toString(); 2469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Create M-Send.req for the MMS message to be sent. 2473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return the M-Send.req 2475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @throws InvalidHeaderValueException if there is any error in parsing the input 2476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static SendReq createSendReq(final Context context, final int subId, 2478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] recipients, final MessageData message, 2479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean requireDeliveryReport, 2480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean requireReadReport, final long expiryTime, final int priority, 2481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timestampMillis) 2482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throws InvalidHeaderValueException { 2483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendReq req = new SendReq(); 2484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // From, per spec 2485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String lineNumber = PhoneUtils.get(subId).getCanonicalForSelf(true/*allowOverride*/); 2486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(lineNumber)) { 2487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setFrom(new EncodedStringValue(lineNumber)); 2488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // To 2490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients); 2491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (encodedNumbers != null) { 2492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setTo(encodedNumbers); 2493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Subject 2495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(message.getMmsSubject())) { 2496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setSubject(new EncodedStringValue(message.getMmsSubject())); 2497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Date 2499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setDate(timestampMillis / 1000L); 2500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Body 2501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MmsInfo bodyInfo = MmsUtils.makePduBody(context, message, subId); 2502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setBody(bodyInfo.mPduBody); 2503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Message size 2504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setMessageSize(bodyInfo.mMessageSize); 2505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Message class 2506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes()); 2507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Expiry 2508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setExpiry(expiryTime); 2509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Priority 2510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setPriority(priority); 2511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delivery report 2512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setDeliveryReport(requireDeliveryReport ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO); 2513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Read report 2514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd req.setReadReport(requireReadReport ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO); 2515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return req; 2516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isDeliveryReportRequired(final int subId) { 2519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!MmsConfig.get(subId).getSMSDeliveryReportsEnabled()) { 2520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 2521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 2523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Resources res = context.getResources(); 2524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getSubscriptionPrefs(subId); 2525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String deliveryReportKey = res.getString(R.string.delivery_reports_pref_key); 2526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean defaultValue = res.getBoolean(R.bool.delivery_reports_pref_default); 2527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return prefs.getBoolean(deliveryReportKey, defaultValue); 2528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int sendSmsMessage(final String recipient, final String messageText, 2531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri requestUri, final int subId, 2532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String smsServiceCenter, final boolean requireDeliveryReport) { 2533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isSmsDataAvailable(subId)) { 2534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "MmsUtils: can't send SMS without radio"); 2535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return MMS_REQUEST_MANUAL_RETRY; 2536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 2538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int status = MMS_REQUEST_MANUAL_RETRY; 2539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Send a single message 2541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SendResult result = SmsSender.sendMessage( 2542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 2543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subId, 2544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipient, 2545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageText, 2546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smsServiceCenter, 2547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requireDeliveryReport, 2548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestUri); 2549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!result.hasPending()) { 2550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // not timed out, check failures 2551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int failureLevel = result.getHighestFailureLevel(); 2552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (failureLevel) { 2553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SendResult.FAILURE_LEVEL_NONE: 2554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_SUCCEEDED; 2555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SendResult.FAILURE_LEVEL_TEMPORARY: 2557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd status = MMS_REQUEST_AUTO_RETRY; 2558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: SMS temporary failure"); 2559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case SendResult.FAILURE_LEVEL_PERMANENT: 2561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: SMS permanent failure"); 2562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Timed out 2566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: sending SMS timed out"); 2567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception e) { 2569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "MmsUtils: failed to send SMS " + e, e); 2570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return status; 2572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete SMS and MMS messages in a particular thread 2576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return the number of messages deleted 2578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int deleteThread(final long threadId, final long cutOffTimestampInMillis) { 2580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 2581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri threadUri = ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, threadId); 2582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cutOffTimestampInMillis < Long.MAX_VALUE) { 2583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return resolver.delete(threadUri, Sms.DATE + "<=?", 2584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Long.toString(cutOffTimestampInMillis) }); 2585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return resolver.delete(threadUri, null /* smsSelection */, null /* selectionArgs */); 2587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete single SMS and MMS message 2592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return number of rows deleted (should be 1 or 0) 2594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int deleteMessage(final Uri messageUri) { 2596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); 2597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return resolver.delete(messageUri, null /* selection */, null /* selectionArgs */); 2598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static byte[] createDebugNotificationInd(final String fileName) { 2601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd byte[] pduData = null; 2602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 2604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Load the message file 2605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final byte[] data = DebugUtils.receiveFromDumpFile(fileName); 2606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RetrieveConf retrieveConf = receiveFromDumpFile(data); 2607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Create the notification 2608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final NotificationInd notification = new NotificationInd(); 2609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long expiry = System.currentTimeMillis() / 1000 + 600; 2610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setTransactionId(fileName.getBytes()); 2611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setMmsVersion(retrieveConf.getMmsVersion()); 2612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setFrom(retrieveConf.getFrom()); 2613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setSubject(retrieveConf.getSubject()); 2614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setExpiry(expiry); 2615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setMessageSize(data.length); 2616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setMessageClass(retrieveConf.getMessageClass()); 2617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri.Builder builder = MediaScratchFileProvider.getUriBuilder(); 2619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.appendPath(fileName); 2620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri contentLocation = builder.build(); 2621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd notification.setContentLocation(contentLocation.toString().getBytes()); 2622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Serialize 2624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd pduData = new PduComposer(context, notification).make(); 2625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (pduData == null || pduData.length < 1) { 2626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd throw new IllegalArgumentException("Empty or zero length PDU data"); 2627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final MmsFailureException e) { 2629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nothing to do 2630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final InvalidHeaderValueException e) { 2631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Nothing to do 2632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return pduData; 2634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int mapRawStatusToErrorResourceId(final int bugleStatus, final int rawStatus) { 2637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int stringResId = R.string.message_status_send_failed; 2638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (rawStatus) { 2639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_SERVICE_DENIED: 2640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED: 2641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET: 2642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED: 2643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED: 2644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED: 2645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED: 2646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID: 2647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_service; 2648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED: 2650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED: 2651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED: 2652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_address; 2653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT: 2655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT: 2656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_corrupt; 2657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED: 2659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED: 2660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_content; 2661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case PduHeaders.RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE: 2663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND: 2664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd //case PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND: 2665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_unsupported; 2666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case MessageData.RAW_TELEPHONY_STATUS_MESSAGE_TOO_BIG: 2668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd stringResId = R.string.mms_failure_outgoing_too_large; 2669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 2670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return stringResId; 2672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * The absence of a connection type. 2676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int TYPE_NONE = -1; 2678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int getConnectivityEventNetworkType(final Context context, final Intent intent) { 2680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConnectivityManager connMgr = (ConnectivityManager) 2681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context.getSystemService(Context.CONNECTIVITY_SERVICE); 2682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastJB_MR1()) { 2683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, TYPE_NONE); 2684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 2685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 2686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConnectivityManager.EXTRA_NETWORK_INFO); 2687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (info != null) { 2688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return info.getType(); 2689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return TYPE_NONE; 2692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Dump the raw MMS data into a file 2696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param rawPdu The raw pdu data 2698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param pdu The parsed pdu, used to construct a dump file name 2699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void dumpPdu(final byte[] rawPdu, final GenericPdu pdu) { 2701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (rawPdu == null || rawPdu.length < 1) { 2702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 2703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String dumpFileName = MmsUtils.MMS_DUMP_PREFIX + getDumpFileId(pdu); 2705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final File dumpFile = DebugUtils.getDebugFile(dumpFileName, true); 2706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (dumpFile != null) { 2707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final FileOutputStream fos = new FileOutputStream(dumpFile); 2709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BufferedOutputStream bos = new BufferedOutputStream(fos); 2710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 2711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bos.write(rawPdu); 2712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bos.flush(); 2713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 2714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd bos.close(); 2715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DebugUtils.ensureReadable(dumpFile); 2717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 2718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "dumpPdu: " + e, e); 2719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 2723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 2724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the dump file id based on the parsed PDU 2725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1. Use message id if not empty 2726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2. Use transaction id if message id is empty 2727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 3. If all above is empty, use random UUID 2728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param pdu the parsed PDU 2730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return the id of the dump file 2731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 2732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getDumpFileId(final GenericPdu pdu) { 2733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String fileId = null; 2734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (pdu != null && pdu instanceof RetrieveConf) { 2735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RetrieveConf retrieveConf = (RetrieveConf) pdu; 2736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (retrieveConf.getMessageId() != null) { 2737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fileId = new String(retrieveConf.getMessageId()); 2738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (retrieveConf.getTransactionId() != null) { 2739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fileId = new String(retrieveConf.getTransactionId()); 2740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(fileId)) { 2743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fileId = UUID.randomUUID().toString(); 2744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return fileId; 2746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 2747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 2748