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