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