SmsApplication.java revision 9c9341ecba5312a4a5be5f9fee3ffe1b582b65ce
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.app.AppOpsManager; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.content.pm.ResolveInfo; 25import android.content.res.Resources; 26import android.provider.Settings; 27import android.provider.Telephony.Sms.Intents; 28 29import java.util.Collection; 30import java.util.HashMap; 31import java.util.List; 32 33/** 34 * Class for managing the primary application that we will deliver SMS/MMS messages to 35 * 36 * {@hide} 37 */ 38public final class SmsApplication { 39 public static class SmsApplicationData { 40 /** 41 * Name of this SMS app for display. 42 */ 43 public String mApplicationName; 44 45 /** 46 * Package name for this SMS app. 47 */ 48 public String mPackageName; 49 50 /** 51 * The class name of the SMS receiver in this app. 52 */ 53 public String mSmsReceiverClass; 54 55 /** 56 * The class name of the MMS receiver in this app. 57 */ 58 public String mMmsReceiverClass; 59 60 /** 61 * The user-id for this application 62 */ 63 public int mUid; 64 65 public SmsApplicationData(String applicationName, String packageName, 66 String smsReceiverName, String mmsReceiverName, int uid) { 67 mApplicationName = applicationName; 68 mPackageName = packageName; 69 mSmsReceiverClass = smsReceiverName; 70 mMmsReceiverClass = mmsReceiverName; 71 mUid = uid; 72 } 73 } 74 75 /** 76 * Returns the list of available SMS apps defined as apps that are registered for both the 77 * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast 78 * receivers are enabled) 79 */ 80 public static Collection<SmsApplicationData> getApplicationCollection(Context context) { 81 PackageManager packageManager = context.getPackageManager(); 82 83 // Get the list of apps registered for SMS 84 Intent intent = new Intent(Intents.SMS_DELIVER_ACTION); 85 int flags = 0; 86 List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceivers(intent, flags); 87 88 intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION); 89 intent.setDataAndType(null, "application/vnd.wap.mms-message"); 90 List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceivers(intent, flags); 91 92 HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>(); 93 94 // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers) 95 for (ResolveInfo r : smsReceivers) { 96 String packageName = r.activityInfo.packageName; 97 if (!receivers.containsKey(packageName)) { 98 String applicationName = r.loadLabel(packageManager).toString(); 99 SmsApplicationData smsApplicationData = new SmsApplicationData(applicationName, 100 packageName, r.activityInfo.name, null, r.activityInfo.applicationInfo.uid); 101 receivers.put(packageName, smsApplicationData); 102 } 103 } 104 105 // Update any existing entries with mms receiver class 106 for (ResolveInfo r : mmsReceivers) { 107 String packageName = r.activityInfo.packageName; 108 SmsApplicationData smsApplicationData = receivers.get(packageName); 109 if (smsApplicationData != null) { 110 smsApplicationData.mMmsReceiverClass = r.activityInfo.name; 111 } 112 } 113 114 // Remove any entries (which we added for sms receivers) for which we did not also find 115 // valid mms receivers 116 for (ResolveInfo r : smsReceivers) { 117 String packageName = r.activityInfo.packageName; 118 SmsApplicationData smsApplicationData = receivers.get(packageName); 119 if (smsApplicationData != null && smsApplicationData.mMmsReceiverClass == null) { 120 receivers.remove(packageName); 121 } 122 } 123 return receivers.values(); 124 } 125 126 /** 127 * Checks to see if we have a valid installed SMS application for the specified package name 128 * @return Data for the specified package name or null if there isn't one 129 */ 130 private static SmsApplicationData getApplicationForPackage( 131 Collection<SmsApplicationData> applications, String packageName) { 132 if (packageName == null) { 133 return null; 134 } 135 // Is there an entry in the application list for the specified package? 136 for (SmsApplicationData application : applications) { 137 if (application.mPackageName.contentEquals(packageName)) { 138 return application; 139 } 140 } 141 return null; 142 } 143 144 /** 145 * Get the application we will use for delivering SMS/MMS messages. 146 * 147 * We return the preferred sms application with the following order of preference: 148 * (1) User selected SMS app (if selected, and if still valid) 149 * (2) Android Messaging (if installed) 150 * (3) The currently configured highest priority broadcast receiver 151 * (4) Null 152 */ 153 private static SmsApplicationData getApplication(Context context, boolean updateIfNeeded) { 154 Collection<SmsApplicationData> applications = getApplicationCollection(context); 155 156 // Determine which application receives the broadcast 157 String defaultApplication = Settings.Secure.getString(context.getContentResolver(), 158 Settings.Secure.SMS_DEFAULT_APPLICATION); 159 160 SmsApplicationData applicationData = null; 161 if (defaultApplication != null) { 162 applicationData = getApplicationForPackage(applications, defaultApplication); 163 } 164 // Picking a new SMS app requires AppOps and Settings.Secure permissions, so we only do 165 // this if the caller asked us to. 166 if (updateIfNeeded) { 167 if (applicationData == null) { 168 // Try to find the default SMS package for this device 169 Resources r = context.getResources(); 170 String defaultPackage = 171 r.getString(com.android.internal.R.string.default_sms_application); 172 applicationData = getApplicationForPackage(applications, defaultPackage); 173 } 174 if (applicationData == null) { 175 // Are there any applications? 176 if (applications.size() != 0) { 177 applicationData = (SmsApplicationData)applications.toArray()[0]; 178 } 179 } 180 181 // If we found a new default app, update the setting 182 if (applicationData != null) { 183 setDefaultApplication(applicationData.mPackageName, context); 184 } 185 } 186 return applicationData; 187 } 188 189 /** 190 * Sets the specified package as the default SMS/MMS application. The caller of this method 191 * needs to have permission to set AppOps and write to secure settings. 192 */ 193 public static void setDefaultApplication(String packageName, Context context) { 194 Collection<SmsApplicationData> applications = getApplicationCollection(context); 195 String oldPackageName = Settings.Secure.getString(context.getContentResolver(), 196 Settings.Secure.SMS_DEFAULT_APPLICATION); 197 SmsApplicationData oldSmsApplicationData = getApplicationForPackage(applications, 198 oldPackageName); 199 SmsApplicationData smsApplicationData = getApplicationForPackage(applications, 200 packageName); 201 202 if (smsApplicationData != null && smsApplicationData != oldSmsApplicationData) { 203 // Ignore OP_WRITE_SMS for the previously configured default SMS app. 204 AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 205 if (oldSmsApplicationData != null) { 206 appOps.setMode(AppOpsManager.OP_WRITE_SMS, oldSmsApplicationData.mUid, 207 oldSmsApplicationData.mPackageName, AppOpsManager.MODE_IGNORED); 208 } 209 210 // Update the secure setting. 211 Settings.Secure.putString(context.getContentResolver(), 212 Settings.Secure.SMS_DEFAULT_APPLICATION, smsApplicationData.mPackageName); 213 214 // Allow OP_WRITE_SMS for the newly configured default SMS app. 215 appOps.setMode(AppOpsManager.OP_WRITE_SMS, smsApplicationData.mUid, 216 smsApplicationData.mPackageName, AppOpsManager.MODE_ALLOWED); 217 } 218 } 219 220 /** 221 * Returns SmsApplicationData for this package if this package is capable of being set as the 222 * default SMS application. 223 */ 224 public static SmsApplicationData getSmsApplicationData(String packageName, Context context) { 225 Collection<SmsApplicationData> applications = getApplicationCollection(context); 226 return getApplicationForPackage(applications, packageName); 227 } 228 229 /** 230 * Gets the default SMS application 231 * @param context context from the calling app 232 * @param updateIfNeeded update the default app if there is no valid default app configured. 233 * @return component name of the app and class to deliver SMS messages to 234 */ 235 public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) { 236 ComponentName component = null; 237 SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded); 238 if (smsApplicationData != null) { 239 component = new ComponentName(smsApplicationData.mPackageName, 240 smsApplicationData.mSmsReceiverClass); 241 } 242 return component; 243 } 244 245 /** 246 * Gets the default MMS application 247 * @param context context from the calling app 248 * @param updateIfNeeded update the default app if there is no valid default app configured. 249 * @return component name of the app and class to deliver MMS messages to 250 */ 251 public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) { 252 ComponentName component = null; 253 SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded); 254 if (smsApplicationData != null) { 255 component = new ComponentName(smsApplicationData.mPackageName, 256 smsApplicationData.mMmsReceiverClass); 257 } 258 return component; 259 } 260} 261