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