1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mms;
18
19import java.io.IOException;
20
21import org.xmlpull.v1.XmlPullParser;
22import org.xmlpull.v1.XmlPullParserException;
23
24import android.content.Context;
25import android.content.Intent;
26import android.content.SharedPreferences;
27import android.content.res.XmlResourceParser;
28import android.preference.PreferenceManager;
29import android.provider.Telephony;
30import android.text.TextUtils;
31import android.util.Log;
32
33import com.android.internal.telephony.TelephonyProperties;
34import com.android.mms.ui.MessageUtils;
35import com.android.mms.ui.MessagingPreferenceActivity;
36
37public class MmsConfig {
38    private static final String TAG = LogTag.TAG;
39    private static final boolean DEBUG = true;
40    private static final boolean LOCAL_LOGV = false;
41
42    private static final String DEFAULT_HTTP_KEY_X_WAP_PROFILE = "x-wap-profile";
43    private static final String DEFAULT_USER_AGENT = "Android-Mms/2.0";
44
45    private static final String MMS_APP_PACKAGE = "com.android.mms";
46
47    private static final String SMS_PROMO_DISMISSED_KEY = "sms_promo_dismissed_key";
48
49    private static final int MAX_IMAGE_HEIGHT = 480;
50    private static final int MAX_IMAGE_WIDTH = 640;
51    private static final int MAX_TEXT_LENGTH = 2000;
52
53    /**
54     * Whether to hide MMS functionality from the user (i.e. SMS only).
55     */
56    private static boolean mTransIdEnabled = false;
57    private static int mMmsEnabled = 1;                         // default to true
58    private static int mMaxMessageSize = 300 * 1024;            // default to 300k max size
59    private static String mUserAgent = DEFAULT_USER_AGENT;
60    private static String mUaProfTagName = DEFAULT_HTTP_KEY_X_WAP_PROFILE;
61    private static String mUaProfUrl = null;
62    private static String mHttpParams = null;
63    private static String mHttpParamsLine1Key = null;
64    private static String mEmailGateway = null;
65    private static int mMaxImageHeight = MAX_IMAGE_HEIGHT;      // default value
66    private static int mMaxImageWidth = MAX_IMAGE_WIDTH;        // default value
67    private static int mRecipientLimit = Integer.MAX_VALUE;     // default value
68    private static int mDefaultSMSMessagesPerThread = 10000;    // default value
69    private static int mDefaultMMSMessagesPerThread = 1000;     // default value
70    private static int mMinMessageCountPerThread = 2;           // default value
71    private static int mMaxMessageCountPerThread = 5000;        // default value
72    private static int mHttpSocketTimeout = 60*1000;            // default to 1 min
73    private static int mMinimumSlideElementDuration = 7;        // default to 7 sec
74    private static boolean mNotifyWapMMSC = false;
75    private static boolean mAllowAttachAudio = true;
76
77    // If mEnableMultipartSMS is true, long sms messages are always sent as multi-part sms
78    // messages, with no checked limit on the number of segments.
79    // If mEnableMultipartSMS is false, then as soon as the user types a message longer
80    // than a single segment (i.e. 140 chars), then the message will turn into and be sent
81    // as an mms message. This feature exists for carriers that don't support multi-part sms's.
82    private static boolean mEnableMultipartSMS = true;
83
84    // If mEnableMultipartSMS is true and mSmsToMmsTextThreshold > 1, then multi-part SMS messages
85    // will be converted into a single mms message. For example, if the mms_config.xml file
86    // specifies <int name="smsToMmsTextThreshold">4</int>, then on the 5th sms segment, the
87    // message will be converted to an mms.
88    private static int mSmsToMmsTextThreshold = -1;
89
90    private static boolean mEnableSlideDuration = true;
91    private static boolean mEnableMMSReadReports = true;        // key: "enableMMSReadReports"
92    private static boolean mEnableSMSDeliveryReports = true;    // key: "enableSMSDeliveryReports"
93    private static boolean mEnableMMSDeliveryReports = true;    // key: "enableMMSDeliveryReports"
94    private static int mMaxTextLength = -1;
95
96    // This is the max amount of storage multiplied by mMaxMessageSize that we
97    // allow of unsent messages before blocking the user from sending any more
98    // MMS's.
99    private static int mMaxSizeScaleForPendingMmsAllowed = 4;       // default value
100
101    // Email gateway alias support, including the master switch and different rules
102    private static boolean mAliasEnabled = false;
103    private static int mAliasRuleMinChars = 2;
104    private static int mAliasRuleMaxChars = 48;
105
106    private static int mMaxSubjectLength = 40;  // maximum number of characters allowed for mms
107                                                // subject
108
109    // If mEnableGroupMms is true, a message with multiple recipients, regardless of contents,
110    // will be sent as a single MMS message with multiple "TO" fields set for each recipient.
111    // If mEnableGroupMms is false, the group MMS setting/preference will be hidden in the settings
112    // activity.
113    private static boolean mEnableGroupMms = true;
114
115    public static void init(Context context) {
116        if (LOCAL_LOGV) {
117            Log.v(TAG, "MmsConfig.init()");
118        }
119        // Always put the mnc/mcc in the log so we can tell which mms_config.xml was loaded.
120        Log.v(TAG, "mnc/mcc: " +
121                android.os.SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC));
122
123        loadMmsSettings(context);
124    }
125
126    public static boolean isSmsEnabled(Context context) {
127        String defaultSmsApplication = Telephony.Sms.getDefaultSmsPackage(context);
128
129        if (defaultSmsApplication != null && defaultSmsApplication.equals(MMS_APP_PACKAGE)) {
130            return true;
131        }
132        return false;
133    }
134
135    public static boolean isSmsPromoDismissed(Context context) {
136        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
137        return preferences.getBoolean(SMS_PROMO_DISMISSED_KEY, false);
138    }
139
140    public static void setSmsPromoDismissed(Context context) {
141        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
142        SharedPreferences.Editor editor = preferences.edit();
143        editor.putBoolean(SMS_PROMO_DISMISSED_KEY, true);
144        editor.apply();
145    }
146
147    public static Intent getRequestDefaultSmsAppActivity() {
148        final Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
149        intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, MMS_APP_PACKAGE);
150        return intent;
151    }
152
153    public static int getSmsToMmsTextThreshold() {
154        return mSmsToMmsTextThreshold;
155    }
156
157    public static boolean getMmsEnabled() {
158        return mMmsEnabled == 1 ? true : false;
159    }
160
161    public static int getMaxMessageSize() {
162        if (LOCAL_LOGV) {
163            Log.v(TAG, "MmsConfig.getMaxMessageSize(): " + mMaxMessageSize);
164        }
165       return mMaxMessageSize;
166    }
167
168    /**
169     * This function returns the value of "enabledTransID" present in mms_config file.
170     * In case of single segment wap push message, this "enabledTransID" indicates whether
171     * TransactionID should be appended to URI or not.
172     */
173    public static boolean getTransIdEnabled() {
174        return mTransIdEnabled;
175    }
176
177    public static String getUserAgent() {
178        return mUserAgent;
179    }
180
181    public static String getUaProfTagName() {
182        return mUaProfTagName;
183    }
184
185    public static String getUaProfUrl() {
186        return mUaProfUrl;
187    }
188
189    public static String getHttpParams() {
190        return mHttpParams;
191    }
192
193    public static String getHttpParamsLine1Key() {
194        return mHttpParamsLine1Key;
195    }
196
197    public static String getEmailGateway() {
198        return mEmailGateway;
199    }
200
201    public static int getMaxImageHeight() {
202        return mMaxImageHeight;
203    }
204
205    public static int getMaxImageWidth() {
206        return mMaxImageWidth;
207    }
208
209    public static int getRecipientLimit() {
210        return mRecipientLimit;
211    }
212
213    public static int getMaxTextLimit() {
214        return mMaxTextLength > -1 ? mMaxTextLength : MAX_TEXT_LENGTH;
215    }
216
217    public static int getDefaultSMSMessagesPerThread() {
218        return mDefaultSMSMessagesPerThread;
219    }
220
221    public static int getDefaultMMSMessagesPerThread() {
222        return mDefaultMMSMessagesPerThread;
223    }
224
225    public static int getMinMessageCountPerThread() {
226        return mMinMessageCountPerThread;
227    }
228
229    public static int getMaxMessageCountPerThread() {
230        return mMaxMessageCountPerThread;
231    }
232
233    public static int getHttpSocketTimeout() {
234        return mHttpSocketTimeout;
235    }
236
237    public static int getMinimumSlideElementDuration() {
238        return mMinimumSlideElementDuration;
239    }
240
241    public static boolean getMultipartSmsEnabled() {
242        return mEnableMultipartSMS;
243    }
244
245    public static boolean getSlideDurationEnabled() {
246        return mEnableSlideDuration;
247    }
248
249    public static boolean getMMSReadReportsEnabled() {
250        return mEnableMMSReadReports;
251    }
252
253    public static boolean getSMSDeliveryReportsEnabled() {
254        return mEnableSMSDeliveryReports;
255    }
256
257    public static boolean getMMSDeliveryReportsEnabled() {
258        return mEnableMMSDeliveryReports;
259    }
260
261    public static boolean getNotifyWapMMSC() {
262        return mNotifyWapMMSC;
263    }
264
265    public static int getMaxSizeScaleForPendingMmsAllowed() {
266        return mMaxSizeScaleForPendingMmsAllowed;
267    }
268
269    public static boolean isAliasEnabled() {
270        return mAliasEnabled;
271    }
272
273    public static int getAliasMinChars() {
274        return mAliasRuleMinChars;
275    }
276
277    public static int getAliasMaxChars() {
278        return mAliasRuleMaxChars;
279    }
280
281    public static boolean getAllowAttachAudio() {
282        return mAllowAttachAudio;
283    }
284
285    public static int getMaxSubjectLength() {
286        return mMaxSubjectLength;
287    }
288
289    public static boolean getGroupMmsEnabled() {
290        return mEnableGroupMms;
291    }
292
293    public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
294    {
295        int type;
296        while ((type=parser.next()) != XmlPullParser.START_TAG
297                   && type != XmlPullParser.END_DOCUMENT) {
298            ;
299        }
300
301        if (type != XmlPullParser.START_TAG) {
302            throw new XmlPullParserException("No start tag found");
303        }
304
305        if (!parser.getName().equals(firstElementName)) {
306            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
307                    ", expected " + firstElementName);
308        }
309    }
310
311    public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
312    {
313        int type;
314        while ((type=parser.next()) != XmlPullParser.START_TAG
315                   && type != XmlPullParser.END_DOCUMENT) {
316            ;
317        }
318    }
319
320    private static void loadMmsSettings(Context context) {
321        XmlResourceParser parser = context.getResources().getXml(R.xml.mms_config);
322
323        try {
324            beginDocument(parser, "mms_config");
325
326            while (true) {
327                nextElement(parser);
328                String tag = parser.getName();
329                if (tag == null) {
330                    break;
331                }
332                String name = parser.getAttributeName(0);
333                String value = parser.getAttributeValue(0);
334                String text = null;
335                if (parser.next() == XmlPullParser.TEXT) {
336                    text = parser.getText();
337                }
338
339                if (DEBUG) {
340                    Log.v(TAG, "tag: " + tag + " value: " + value + " - " +
341                            text);
342                }
343                if ("name".equalsIgnoreCase(name)) {
344                    if ("bool".equals(tag)) {
345                        // bool config tags go here
346                        if ("enabledMMS".equalsIgnoreCase(value)) {
347                            mMmsEnabled = "true".equalsIgnoreCase(text) ? 1 : 0;
348                        } else if ("enabledTransID".equalsIgnoreCase(value)) {
349                            mTransIdEnabled = "true".equalsIgnoreCase(text);
350                        } else if ("enabledNotifyWapMMSC".equalsIgnoreCase(value)) {
351                            mNotifyWapMMSC = "true".equalsIgnoreCase(text);
352                        } else if ("aliasEnabled".equalsIgnoreCase(value)) {
353                            mAliasEnabled = "true".equalsIgnoreCase(text);
354                        } else if ("allowAttachAudio".equalsIgnoreCase(value)) {
355                            mAllowAttachAudio = "true".equalsIgnoreCase(text);
356                        } else if ("enableMultipartSMS".equalsIgnoreCase(value)) {
357                            mEnableMultipartSMS = "true".equalsIgnoreCase(text);
358                        } else if ("enableSlideDuration".equalsIgnoreCase(value)) {
359                            mEnableSlideDuration = "true".equalsIgnoreCase(text);
360                        } else if ("enableMMSReadReports".equalsIgnoreCase(value)) {
361                            mEnableMMSReadReports = "true".equalsIgnoreCase(text);
362                        } else if ("enableSMSDeliveryReports".equalsIgnoreCase(value)) {
363                            mEnableSMSDeliveryReports = "true".equalsIgnoreCase(text);
364                        } else if ("enableMMSDeliveryReports".equalsIgnoreCase(value)) {
365                            mEnableMMSDeliveryReports = "true".equalsIgnoreCase(text);
366                        } else if ("enableGroupMms".equalsIgnoreCase(value)) {
367                            mEnableGroupMms = "true".equalsIgnoreCase(text);
368                        }
369                    } else if ("int".equals(tag)) {
370                        // int config tags go here
371                        if ("maxMessageSize".equalsIgnoreCase(value)) {
372                            mMaxMessageSize = Integer.parseInt(text);
373                        } else if ("maxImageHeight".equalsIgnoreCase(value)) {
374                            mMaxImageHeight = Integer.parseInt(text);
375                        } else if ("maxImageWidth".equalsIgnoreCase(value)) {
376                            mMaxImageWidth = Integer.parseInt(text);
377                        } else if ("defaultSMSMessagesPerThread".equalsIgnoreCase(value)) {
378                            mDefaultSMSMessagesPerThread = Integer.parseInt(text);
379                        } else if ("defaultMMSMessagesPerThread".equalsIgnoreCase(value)) {
380                            mDefaultMMSMessagesPerThread = Integer.parseInt(text);
381                        } else if ("minMessageCountPerThread".equalsIgnoreCase(value)) {
382                            mMinMessageCountPerThread = Integer.parseInt(text);
383                        } else if ("maxMessageCountPerThread".equalsIgnoreCase(value)) {
384                            mMaxMessageCountPerThread = Integer.parseInt(text);
385                        } else if ("recipientLimit".equalsIgnoreCase(value)) {
386                            mRecipientLimit = Integer.parseInt(text);
387                            if (mRecipientLimit < 0) {
388                                mRecipientLimit = Integer.MAX_VALUE;
389                            }
390                        } else if ("httpSocketTimeout".equalsIgnoreCase(value)) {
391                            mHttpSocketTimeout = Integer.parseInt(text);
392                        } else if ("minimumSlideElementDuration".equalsIgnoreCase(value)) {
393                            mMinimumSlideElementDuration = Integer.parseInt(text);
394                        } else if ("maxSizeScaleForPendingMmsAllowed".equalsIgnoreCase(value)) {
395                            mMaxSizeScaleForPendingMmsAllowed = Integer.parseInt(text);
396                        } else if ("aliasMinChars".equalsIgnoreCase(value)) {
397                            mAliasRuleMinChars = Integer.parseInt(text);
398                        } else if ("aliasMaxChars".equalsIgnoreCase(value)) {
399                            mAliasRuleMaxChars = Integer.parseInt(text);
400                        } else if ("smsToMmsTextThreshold".equalsIgnoreCase(value)) {
401                            mSmsToMmsTextThreshold = Integer.parseInt(text);
402                        } else if ("maxMessageTextSize".equalsIgnoreCase(value)) {
403                            mMaxTextLength = Integer.parseInt(text);
404                        } else if ("maxSubjectLength".equalsIgnoreCase(value)) {
405                            mMaxSubjectLength = Integer.parseInt(text);
406                        }
407                    } else if ("string".equals(tag)) {
408                        // string config tags go here
409                        if ("userAgent".equalsIgnoreCase(value)) {
410                            mUserAgent = text;
411                        } else if ("uaProfTagName".equalsIgnoreCase(value)) {
412                            mUaProfTagName = text;
413                        } else if ("uaProfUrl".equalsIgnoreCase(value)) {
414                            mUaProfUrl = text;
415                        } else if ("httpParams".equalsIgnoreCase(value)) {
416                            mHttpParams = text;
417                        } else if ("httpParamsLine1Key".equalsIgnoreCase(value)) {
418                            mHttpParamsLine1Key = text;
419                        } else if ("emailGatewayNumber".equalsIgnoreCase(value)) {
420                            mEmailGateway = text;
421                        }
422                    }
423                }
424            }
425        } catch (XmlPullParserException e) {
426            Log.e(TAG, "loadMmsSettings caught ", e);
427        } catch (NumberFormatException e) {
428            Log.e(TAG, "loadMmsSettings caught ", e);
429        } catch (IOException e) {
430            Log.e(TAG, "loadMmsSettings caught ", e);
431        } finally {
432            parser.close();
433        }
434
435        String errorStr = null;
436
437        if (getMmsEnabled() && mUaProfUrl == null) {
438            errorStr = "uaProfUrl";
439        }
440
441        if (errorStr != null) {
442            String err =
443                String.format("MmsConfig.loadMmsSettings mms_config.xml missing %s setting",
444                        errorStr);
445            Log.e(TAG, err);
446        }
447    }
448
449}
450