diff options
5 files changed, 88 insertions, 5 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 93cb57dd2459..1f3a8fce8ef1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -603,6 +603,8 @@ <protected-broadcast android:name="android.intent.action.DOCK_IDLE" /> <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" /> + <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -3995,6 +3997,12 @@ <permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" android:protectionLevel="signature" /> + <!-- @hide Permission that protects the + {@link android.provider.Telephony.Intents#ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL} + broadcast --> + <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index c09f02d16682..109ffe96571f 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -1170,6 +1170,17 @@ public final class Telephony { "android.provider.action.EXTERNAL_PROVIDER_CHANGE"; /** + * Same as {@link #ACTION_DEFAULT_SMS_PACKAGE_CHANGED} but it's implicit (e.g. sent to + * all apps) and requires + * {@link android.Manifest.permission#MONITOR_DEFAULT_SMS_PACKAGE} to receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL = + "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL"; + + /** * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a * {@link #DATA_SMS_RECEIVED_ACTION} intent. * diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 4b9b0fb84f16..845ab78ba4f3 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1499,6 +1499,21 @@ interface ITelephony { */ int getNetworkSelectionMode(int subId); + /** + * Get a list of SMS apps on a user. + */ + String[] getSmsApps(int userId); + + /** + * Get the default SMS app on a given user. + */ + String getDefaultSmsApp(int userId); + + /** + * Set the default SMS app to a given package on a given user. + */ + void setDefaultSmsApp(int userId, String packageName); + /** * Return the modem radio power state for slot index. * diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java index d8ef42962ee4..39722c66e170 100644 --- a/telephony/java/com/android/internal/telephony/SmsApplication.java +++ b/telephony/java/com/android/internal/telephony/SmsApplication.java @@ -209,7 +209,14 @@ public final class SmsApplication { * Support smsto Uri scheme. */ public static Collection<SmsApplicationData> getApplicationCollection(Context context) { - int userId = getIncomingUserId(context); + return getApplicationCollectionAsUser(context, getIncomingUserId(context)); + } + + /** + * Same as {@link #getApplicationCollection} but it takes a target user ID. + */ + public static Collection<SmsApplicationData> getApplicationCollectionAsUser(Context context, + int userId) { final long token = Binder.clearCallingIdentity(); try { return getApplicationCollectionInternal(context, userId); @@ -535,13 +542,20 @@ public final class SmsApplication { * needs to have permission to set AppOps and write to secure settings. */ public static void setDefaultApplication(String packageName, Context context) { + setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context)); + } + + /** + * Same as {@link #setDefaultApplication} but takes a target user id. + */ + public static void setDefaultApplicationAsUser(String packageName, Context context, + int userId) { TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); if (!tm.isSmsCapable()) { // No phone, no SMS return; } - final int userId = getIncomingUserId(context); final long token = Binder.clearCallingIdentity(); try { setDefaultApplicationInternal(packageName, context, userId); @@ -552,6 +566,8 @@ public final class SmsApplication { private static void setDefaultApplicationInternal(String packageName, Context context, int userId) { + final UserHandle userHandle = UserHandle.of(userId); + // Get old package name String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION, userId); @@ -628,7 +644,7 @@ public final class SmsApplication { if (DEBUG_MULTIUSER) { Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName); } - context.sendBroadcast(oldAppIntent); + context.sendBroadcastAsUser(oldAppIntent, userHandle); } // Notify the new sms app that it's now the default (if the new sms app has a receiver // to handle the changed default sms intent). @@ -646,8 +662,16 @@ public final class SmsApplication { if (DEBUG_MULTIUSER) { Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName); } - context.sendBroadcast(intent); + context.sendBroadcastAsUser(intent, userHandle); } + + // Send an implicit broadcast for the system server. + // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.) + final Intent intent = + new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); + context.sendBroadcastAsUser(intent, userHandle, + permission.MONITOR_DEFAULT_SMS_PACKAGE); + MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED, applicationData.mPackageName); } @@ -799,7 +823,18 @@ public final class SmsApplication { * @return component name of the app and class to deliver SMS messages to */ public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) { - int userId = getIncomingUserId(context); + return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context)); + } + + /** + * Gets the default SMS application on a given user + * @param context context from the calling app + * @param updateIfNeeded update the default app if there is no valid default app configured. + * @param userId target user ID. + * @return component name of the app and class to deliver SMS messages to + */ + public static ComponentName getDefaultSmsApplicationAsUser(Context context, + boolean updateIfNeeded, int userId) { final long token = Binder.clearCallingIdentity(); try { ComponentName component = null; diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 3822cbe8402e..2c8b908abbec 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -21,6 +21,7 @@ import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Rlog; @@ -328,4 +329,17 @@ public final class TelephonyPermissions { Rlog.e(LOG_TAG, "Phone process is down, cannot check carrier privileges"); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } + + /** + * Throws if the caller is not of a shell (or root) UID. + * + * @param callingUid pass Binder.callingUid(). + */ + public static void enforceShellOnly(int callingUid, String message) { + if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { + return; // okay + } + + throw new SecurityException(message + ": Only shell user can call it"); + } } |