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