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