SmsApplication.java revision 6dc23862ef345df2acd3ab36a432292d264d6a73
1/*
2 * Copyright (C) 2013 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.internal.telephony;
18
19import android.Manifest.permission;
20import android.app.AppOpsManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.ActivityInfo;
26import android.content.pm.PackageInfo;
27import android.content.pm.PackageManager;
28import android.content.pm.ResolveInfo;
29import android.content.pm.ServiceInfo;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.res.Resources;
32import android.net.Uri;
33import android.provider.Settings;
34import android.provider.Telephony.Sms.Intents;
35import android.telephony.Rlog;
36import android.telephony.SmsManager;
37import android.telephony.TelephonyManager;
38import com.android.internal.content.PackageMonitor;
39
40import java.util.Collection;
41import java.util.HashMap;
42import java.util.List;
43
44/**
45 * Class for managing the primary application that we will deliver SMS/MMS messages to
46 *
47 * {@hide}
48 */
49public final class SmsApplication {
50    static final String LOG_TAG = "SmsApplication";
51    private static final String PHONE_PACKAGE_NAME = "com.android.phone";
52    private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
53    private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
54
55    private static final String SCHEME_SMS = "sms";
56    private static final String SCHEME_SMSTO = "smsto";
57    private static final String SCHEME_MMS = "mms";
58    private static final String SCHEME_MMSTO = "mmsto";
59
60    private static SmsPackageMonitor sSmsPackageMonitor = null;
61
62    public static class SmsApplicationData {
63        /**
64         * Name of this SMS app for display.
65         */
66        public String mApplicationName;
67
68        /**
69         * Package name for this SMS app.
70         */
71        public String mPackageName;
72
73        /**
74         * The class name of the SMS_DELIVER_ACTION receiver in this app.
75         */
76        public String mSmsReceiverClass;
77
78        /**
79         * The class name of the WAP_PUSH_DELIVER_ACTION receiver in this app.
80         */
81        public String mMmsReceiverClass;
82
83        /**
84         * The class name of the ACTION_RESPOND_VIA_MESSAGE intent in this app.
85         */
86        public String mRespondViaMessageClass;
87
88        /**
89         * The class name of the ACTION_SENDTO intent in this app.
90         */
91        public String mSendToClass;
92
93        /**
94         * The user-id for this application
95         */
96        public int mUid;
97
98        /**
99         * Returns true if this SmsApplicationData is complete (all intents handled).
100         * @return
101         */
102        public boolean isComplete() {
103            return (mSmsReceiverClass != null && mMmsReceiverClass != null
104                    && mRespondViaMessageClass != null && mSendToClass != null);
105        }
106
107        public SmsApplicationData(String applicationName, String packageName, int uid) {
108            mApplicationName = applicationName;
109            mPackageName = packageName;
110            mUid = uid;
111        }
112    }
113
114    /**
115     * Returns the list of available SMS apps defined as apps that are registered for both the
116     * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast
117     * receivers are enabled)
118     *
119     * Requirements to be an SMS application:
120     * Implement SMS_DELIVER_ACTION broadcast receiver.
121     * Require BROADCAST_SMS permission.
122     *
123     * Implement WAP_PUSH_DELIVER_ACTION broadcast receiver.
124     * Require BROADCAST_WAP_PUSH permission.
125     *
126     * Implement RESPOND_VIA_MESSAGE intent.
127     * Support smsto Uri scheme.
128     * Require SEND_RESPOND_VIA_MESSAGE permission.
129     *
130     * Implement ACTION_SENDTO intent.
131     * Support smsto Uri scheme.
132     */
133    public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
134        PackageManager packageManager = context.getPackageManager();
135
136        // Get the list of apps registered for SMS
137        Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
138        List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceivers(intent, 0);
139
140        HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
141
142        // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers)
143        for (ResolveInfo resolveInfo : smsReceivers) {
144            final ActivityInfo activityInfo = resolveInfo.activityInfo;
145            if (activityInfo == null) {
146                continue;
147            }
148            if (!permission.BROADCAST_SMS.equals(activityInfo.permission)) {
149                continue;
150            }
151            final String packageName = activityInfo.packageName;
152            if (!receivers.containsKey(packageName)) {
153                final String applicationName = resolveInfo.loadLabel(packageManager).toString();
154                final SmsApplicationData smsApplicationData = new SmsApplicationData(
155                        applicationName, packageName, activityInfo.applicationInfo.uid);
156                smsApplicationData.mSmsReceiverClass = activityInfo.name;
157                receivers.put(packageName, smsApplicationData);
158            }
159        }
160
161        // Update any existing entries with mms receiver class
162        intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
163        intent.setDataAndType(null, "application/vnd.wap.mms-message");
164        List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceivers(intent, 0);
165        for (ResolveInfo resolveInfo : mmsReceivers) {
166            final ActivityInfo activityInfo = resolveInfo.activityInfo;
167            if (activityInfo == null) {
168                continue;
169            }
170            if (!permission.BROADCAST_WAP_PUSH.equals(activityInfo.permission)) {
171                continue;
172            }
173            final String packageName = activityInfo.packageName;
174            final SmsApplicationData smsApplicationData = receivers.get(packageName);
175            if (smsApplicationData != null) {
176                smsApplicationData.mMmsReceiverClass = activityInfo.name;
177            }
178        }
179
180        // Update any existing entries with respond via message intent class.
181        intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
182                Uri.fromParts(SCHEME_SMSTO, "", null));
183        List<ResolveInfo> respondServices = packageManager.queryIntentServices(intent, 0);
184        for (ResolveInfo resolveInfo : respondServices) {
185            final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
186            if (serviceInfo == null) {
187                continue;
188            }
189            if (!permission.SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
190                continue;
191            }
192            final String packageName = serviceInfo.packageName;
193            final SmsApplicationData smsApplicationData = receivers.get(packageName);
194            if (smsApplicationData != null) {
195                smsApplicationData.mRespondViaMessageClass = serviceInfo.name;
196            }
197        }
198
199        // Update any existing entries with supports send to.
200        intent = new Intent(Intent.ACTION_SENDTO,
201                Uri.fromParts(SCHEME_SMSTO, "", null));
202        List<ResolveInfo> sendToActivities = packageManager.queryIntentActivities(intent, 0);
203        for (ResolveInfo resolveInfo : sendToActivities) {
204            final ActivityInfo activityInfo = resolveInfo.activityInfo;
205            if (activityInfo == null) {
206                continue;
207            }
208            final String packageName = activityInfo.packageName;
209            final SmsApplicationData smsApplicationData = receivers.get(packageName);
210            if (smsApplicationData != null) {
211                smsApplicationData.mSendToClass = activityInfo.name;
212            }
213        }
214
215        // Remove any entries for which we did not find all required intents.
216        for (ResolveInfo resolveInfo : smsReceivers) {
217            final ActivityInfo activityInfo = resolveInfo.activityInfo;
218            if (activityInfo == null) {
219                continue;
220            }
221            final String packageName = activityInfo.packageName;
222            final SmsApplicationData smsApplicationData = receivers.get(packageName);
223            if (smsApplicationData != null) {
224                if (!smsApplicationData.isComplete()) {
225                    receivers.remove(packageName);
226                }
227            }
228        }
229        return receivers.values();
230    }
231
232    /**
233     * Checks to see if we have a valid installed SMS application for the specified package name
234     * @return Data for the specified package name or null if there isn't one
235     */
236    private static SmsApplicationData getApplicationForPackage(
237            Collection<SmsApplicationData> applications, String packageName) {
238        if (packageName == null) {
239            return null;
240        }
241        // Is there an entry in the application list for the specified package?
242        for (SmsApplicationData application : applications) {
243            if (application.mPackageName.contentEquals(packageName)) {
244                return application;
245            }
246        }
247        return null;
248    }
249
250    /**
251     * Get the application we will use for delivering SMS/MMS messages.
252     *
253     * We return the preferred sms application with the following order of preference:
254     * (1) User selected SMS app (if selected, and if still valid)
255     * (2) Android Messaging (if installed)
256     * (3) The currently configured highest priority broadcast receiver
257     * (4) Null
258     */
259    private static SmsApplicationData getApplication(Context context, boolean updateIfNeeded) {
260        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
261        if (!tm.isSmsCapable()) {
262            // No phone, no SMS
263            return null;
264        }
265
266        Collection<SmsApplicationData> applications = getApplicationCollection(context);
267
268        // Determine which application receives the broadcast
269        String defaultApplication = Settings.Secure.getString(context.getContentResolver(),
270                Settings.Secure.SMS_DEFAULT_APPLICATION);
271
272        SmsApplicationData applicationData = null;
273        if (defaultApplication != null) {
274            applicationData = getApplicationForPackage(applications, defaultApplication);
275        }
276        // Picking a new SMS app requires AppOps and Settings.Secure permissions, so we only do
277        // this if the caller asked us to.
278        if (updateIfNeeded && applicationData == null) {
279            // Try to find the default SMS package for this device
280            Resources r = context.getResources();
281            String defaultPackage =
282                    r.getString(com.android.internal.R.string.default_sms_application);
283            applicationData = getApplicationForPackage(applications, defaultPackage);
284
285            if (applicationData == null) {
286                // Are there any applications?
287                if (applications.size() != 0) {
288                    applicationData = (SmsApplicationData)applications.toArray()[0];
289                }
290            }
291
292            // If we found a new default app, update the setting
293            if (applicationData != null) {
294                setDefaultApplication(applicationData.mPackageName, context);
295            }
296        }
297
298        // If we found a package, make sure AppOps permissions are set up correctly
299        if (applicationData != null) {
300            AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
301
302            // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
303            // are checking is for our current uid. Doing this check from the unprivileged current
304            // SMS app allows us to tell the current SMS app that it is not in a good state and
305            // needs to ask to be the current SMS app again to work properly.
306            if (updateIfNeeded || applicationData.mUid == android.os.Process.myUid()) {
307                // Verify that the SMS app has permissions
308                int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
309                        applicationData.mPackageName);
310                if (mode != AppOpsManager.MODE_ALLOWED) {
311                    Rlog.e(LOG_TAG, applicationData.mPackageName + " lost OP_WRITE_SMS: " +
312                            (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
313                    if (updateIfNeeded) {
314                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
315                                applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
316                    } else {
317                        // We can not return a package if permissions are not set up correctly
318                        applicationData = null;
319                    }
320                }
321            }
322
323            // We can only verify the phone and BT app's permissions from a privileged caller
324            if (updateIfNeeded) {
325                // Ensure this component is still configured as the preferred activity. Usually the
326                // current SMS app will already be the preferred activity - but checking whether or
327                // not this is true is just as expensive as reconfiguring the preferred activity so
328                // we just reconfigure every time.
329                PackageManager packageManager = context.getPackageManager();
330                configurePreferredActivity(packageManager, new ComponentName(
331                        applicationData.mPackageName, applicationData.mSendToClass));
332
333                // Verify that the phone, BT app and MmsService have permissions
334                try {
335                    PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
336                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
337                            PHONE_PACKAGE_NAME);
338                    if (mode != AppOpsManager.MODE_ALLOWED) {
339                        Rlog.e(LOG_TAG, PHONE_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
340                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
341                                PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
342                    }
343                } catch (NameNotFoundException e) {
344                    // No phone app on this device (unexpected, even for non-phone devices)
345                    Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
346                    applicationData = null;
347                }
348
349                try {
350                    PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
351                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
352                            BLUETOOTH_PACKAGE_NAME);
353                    if (mode != AppOpsManager.MODE_ALLOWED) {
354                        Rlog.e(LOG_TAG, BLUETOOTH_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
355                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
356                                BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
357                    }
358                } catch (NameNotFoundException e) {
359                    // No BT app on this device
360                    Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
361                }
362
363                try {
364                    PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
365                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
366                            MMS_SERVICE_PACKAGE_NAME);
367                    if (mode != AppOpsManager.MODE_ALLOWED) {
368                        Rlog.e(LOG_TAG, MMS_SERVICE_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
369                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
370                                MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
371                    }
372                } catch (NameNotFoundException e) {
373                    // No phone app on this device (unexpected, even for non-phone devices)
374                    Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
375                    applicationData = null;
376                }
377
378            }
379        }
380        return applicationData;
381    }
382
383    /**
384     * Sets the specified package as the default SMS/MMS application. The caller of this method
385     * needs to have permission to set AppOps and write to secure settings.
386     */
387    public static void setDefaultApplication(String packageName, Context context) {
388        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
389        if (!tm.isSmsCapable()) {
390            // No phone, no SMS
391            return;
392        }
393
394        // Get old package name
395        String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
396                Settings.Secure.SMS_DEFAULT_APPLICATION);
397
398        if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
399            // No change
400            return;
401        }
402
403        // We only make the change if the new package is valid
404        PackageManager packageManager = context.getPackageManager();
405        Collection<SmsApplicationData> applications = getApplicationCollection(context);
406        SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
407        if (applicationData != null) {
408            // Ignore OP_WRITE_SMS for the previously configured default SMS app.
409            AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
410            if (oldPackageName != null) {
411                try {
412                    PackageInfo info = packageManager.getPackageInfo(oldPackageName,
413                            PackageManager.GET_UNINSTALLED_PACKAGES);
414                    appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
415                            oldPackageName, AppOpsManager.MODE_IGNORED);
416                } catch (NameNotFoundException e) {
417                    Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
418                }
419            }
420
421            // Update the secure setting.
422            Settings.Secure.putString(context.getContentResolver(),
423                    Settings.Secure.SMS_DEFAULT_APPLICATION, applicationData.mPackageName);
424
425            // Configure this as the preferred activity for SENDTO sms/mms intents
426            configurePreferredActivity(packageManager, new ComponentName(
427                    applicationData.mPackageName, applicationData.mSendToClass));
428
429            // Allow OP_WRITE_SMS for the newly configured default SMS app.
430            appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
431                    applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
432
433            // Phone needs to always have this permission to write to the sms database
434            try {
435                PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
436                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
437                        PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
438            } catch (NameNotFoundException e) {
439                // No phone app on this device (unexpected, even for non-phone devices)
440                Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
441            }
442
443            // BT needs to always have this permission to write to the sms database
444            try {
445                PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
446                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
447                        BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
448            } catch (NameNotFoundException e) {
449                // No BT app on this device
450                Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
451            }
452
453            // MmsService needs to always have this permission to write to the sms database
454            try {
455                PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
456                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
457                        MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
458            } catch (NameNotFoundException e) {
459                // No phone app on this device (unexpected, even for non-phone devices)
460                Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
461            }
462        }
463    }
464
465    /**
466     * Tracks package changes and ensures that the default SMS app is always configured to be the
467     * preferred activity for SENDTO sms/mms intents.
468     */
469    private static final class SmsPackageMonitor extends PackageMonitor {
470        final Context mContext;
471
472        public SmsPackageMonitor(Context context) {
473            super();
474            mContext = context;
475        }
476
477        @Override
478        public void onPackageDisappeared(String packageName, int reason) {
479            onPackageChanged(packageName);
480        }
481
482        @Override
483        public void onPackageAppeared(String packageName, int reason) {
484            onPackageChanged(packageName);
485        }
486
487        @Override
488        public void onPackageModified(String packageName) {
489            onPackageChanged(packageName);
490        }
491
492        private void onPackageChanged(String packageName) {
493            PackageManager packageManager = mContext.getPackageManager();
494            // Ensure this component is still configured as the preferred activity
495            ComponentName componentName = getDefaultSendToApplication(mContext, true);
496            if (componentName != null) {
497                configurePreferredActivity(packageManager, componentName);
498            }
499        }
500    }
501
502    public static void initSmsPackageMonitor(Context context) {
503        sSmsPackageMonitor = new SmsPackageMonitor(context);
504        sSmsPackageMonitor.register(context, context.getMainLooper(), false);
505    }
506
507    private static void configurePreferredActivity(PackageManager packageManager,
508            ComponentName componentName) {
509        // Add the four activity preferences we want to direct to this app.
510        replacePreferredActivity(packageManager, componentName, SCHEME_SMS);
511        replacePreferredActivity(packageManager, componentName, SCHEME_SMSTO);
512        replacePreferredActivity(packageManager, componentName, SCHEME_MMS);
513        replacePreferredActivity(packageManager, componentName, SCHEME_MMSTO);
514    }
515
516    /**
517     * Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
518     */
519    private static void replacePreferredActivity(PackageManager packageManager,
520            ComponentName componentName, String scheme) {
521        // Build the set of existing activities that handle this scheme
522        Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
523        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(
524                intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER);
525
526        // Build the set of ComponentNames for these activities
527        final int n = resolveInfoList.size();
528        ComponentName[] set = new ComponentName[n];
529        for (int i = 0; i < n; i++) {
530            ResolveInfo info = resolveInfoList.get(i);
531            set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
532        }
533
534        // Update the preferred SENDTO activity for the specified scheme
535        IntentFilter intentFilter = new IntentFilter();
536        intentFilter.addAction(Intent.ACTION_SENDTO);
537        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
538        intentFilter.addDataScheme(scheme);
539        packageManager.replacePreferredActivity(intentFilter,
540                IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
541                set, componentName);
542    }
543
544    /**
545     * Returns SmsApplicationData for this package if this package is capable of being set as the
546     * default SMS application.
547     */
548    public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
549        Collection<SmsApplicationData> applications = getApplicationCollection(context);
550        return getApplicationForPackage(applications, packageName);
551    }
552
553    /**
554     * Gets the default SMS application
555     * @param context context from the calling app
556     * @param updateIfNeeded update the default app if there is no valid default app configured.
557     * @return component name of the app and class to deliver SMS messages to
558     */
559    public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
560        ComponentName component = null;
561        SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded);
562        if (smsApplicationData != null) {
563            component = new ComponentName(smsApplicationData.mPackageName,
564                    smsApplicationData.mSmsReceiverClass);
565        }
566        return component;
567    }
568
569    /**
570     * Gets the default MMS application
571     * @param context context from the calling app
572     * @param updateIfNeeded update the default app if there is no valid default app configured.
573     * @return component name of the app and class to deliver MMS messages to
574     */
575    public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
576        ComponentName component = null;
577        SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded);
578        if (smsApplicationData != null) {
579            component = new ComponentName(smsApplicationData.mPackageName,
580                    smsApplicationData.mMmsReceiverClass);
581        }
582        return component;
583    }
584
585    /**
586     * Gets the default Respond Via Message application
587     * @param context context from the calling app
588     * @param updateIfNeeded update the default app if there is no valid default app configured.
589     * @return component name of the app and class to direct Respond Via Message intent to
590     */
591    public static ComponentName getDefaultRespondViaMessageApplication(Context context,
592            boolean updateIfNeeded) {
593        ComponentName component = null;
594        SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded);
595        if (smsApplicationData != null) {
596            component = new ComponentName(smsApplicationData.mPackageName,
597                    smsApplicationData.mRespondViaMessageClass);
598        }
599        return component;
600    }
601
602    /**
603     * Gets the default Send To (smsto) application
604     * @param context context from the calling app
605     * @param updateIfNeeded update the default app if there is no valid default app configured.
606     * @return component name of the app and class to direct SEND_TO (smsto) intent to
607     */
608    public static ComponentName getDefaultSendToApplication(Context context,
609            boolean updateIfNeeded) {
610        ComponentName component = null;
611        SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded);
612        if (smsApplicationData != null) {
613            component = new ComponentName(smsApplicationData.mPackageName,
614                    smsApplicationData.mSendToClass);
615        }
616        return component;
617    }
618
619    /**
620     * Returns whether need to write the SMS message to SMS database for this package.
621     */
622    public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
623        if (packageName == null) return true;
624
625        if (SmsManager.getDefault().getAutoPersisting()) {
626            return true;
627        }
628
629        String defaultSmsPackage = null;
630        ComponentName component = getDefaultSmsApplication(context, false);
631        if (component != null) {
632            defaultSmsPackage = component.getPackageName();
633        }
634
635        if ((defaultSmsPackage == null || !defaultSmsPackage.equals(packageName)) &&
636                !packageName.equals(BLUETOOTH_PACKAGE_NAME)) {
637            // To write the message for someone other than the default SMS and BT app
638            return true;
639        }
640
641        return false;
642    }
643}
644