Skip to content

Commit

Permalink
improve PendingIntent security check compatibility
Browse files Browse the repository at this point in the history
This switches to secure-by-default instead of crash-by-default for API
31 to work around apps which have updated to API 31 without specifying
either FLAG_MUTABLE or FLAG_IMMUTABLE for PendingIntents. If the app
ends up needing the FLAG_MUTABLE behavior, it may crash later, but it
should still be obvious why it happened.

There are many apps with outdated Play services client libraries lacking
support for Android 12 which are nonetheless targeting API 31 or higher
and will crash in certain situations. Google Play services will ask the
client library to request runtime permissions from the user on behalf of
it when it thinks that they're required for an operation that's
requested. The older client libraries will cause a crash in the app by
trying to create a PendingIntent with no FLAG_MUTABLE or FLAG_IMMUTABLE
specified. This is a much more common issue on GrapheneOS since Play
services is a regular user installed app with no special access or
privileges, and starts without any standard runtime permissions granted
to it.
  • Loading branch information
thestinger authored and kdrag0n committed Jun 7, 2022
1 parent e23fc2f commit bf40d2d
Showing 1 changed file with 10 additions and 6 deletions.
16 changes: 10 additions & 6 deletions core/java/android/app/PendingIntent.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
import android.util.Log;
import android.util.proto.ProtoOutputStream;

import com.android.internal.os.IResultReceiver;
Expand Down Expand Up @@ -355,7 +356,7 @@ public static void setOnMarshaledListener(OnMarshaledListener listener) {
sOnMarshaledListener.set(listener);
}

private static void checkFlags(int flags, String packageName) {
private static int checkFlags(int flags, String packageName) {
final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;

Expand All @@ -372,8 +373,11 @@ private static void checkFlags(int flags, String packageName) {
+ " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality"
+ " depends on the PendingIntent being mutable, e.g. if it needs to"
+ " be used with inline replies or bubbles.";
throw new IllegalArgumentException(msg);
// secure-by-default instead of crash-by-default for better compatibility
Log.e(TAG, msg);
return flags | PendingIntent.FLAG_IMMUTABLE;
}
return flags;
}

/**
Expand Down Expand Up @@ -455,7 +459,7 @@ public static PendingIntent getActivityAsUser(Context context, int requestCode,
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
checkFlags(flags, packageName);
flags = checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
Expand Down Expand Up @@ -590,7 +594,7 @@ public static PendingIntent getActivitiesAsUser(Context context, int requestCode
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
checkFlags(flags, packageName);
flags = checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
Expand Down Expand Up @@ -642,7 +646,7 @@ public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
Intent intent, int flags, UserHandle userHandle) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
checkFlags(flags, packageName);
flags = checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
Expand Down Expand Up @@ -721,7 +725,7 @@ private static PendingIntent buildServicePendingIntent(Context context, int requ
Intent intent, int flags, int serviceKind) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
checkFlags(flags, packageName);
flags = checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
Expand Down

0 comments on commit bf40d2d

Please sign in to comment.