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