Merge "Restrict what activity launches show permission prompts" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-01 17:21:36 +00:00
committed by Android (Google) Code Review
7 changed files with 102 additions and 22 deletions

View File

@@ -155,8 +155,10 @@ package android.app {
public class ActivityOptions { public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle); method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
method public boolean isEligibleForLegacyPermissionPrompt();
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public void setEligibleForLegacyPermissionPrompt(boolean);
method public static void setExitTransitionTimeout(long); method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int); method public void setLaunchActivityType(int);
method public void setLaunchWindowingMode(int); method public void setLaunchWindowingMode(int);

View File

@@ -173,6 +173,13 @@ public class ActivityOptions extends ComponentOptions {
*/ */
public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme"; public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";
/**
* Indicates that this activity launch is eligible to show a legacy permission prompt
* @hide
*/
public static final String KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE =
"android:activity.legacyPermissionPromptEligible";
/** /**
* Callback for when the last frame of the animation is played. * Callback for when the last frame of the animation is played.
* @hide * @hide
@@ -445,6 +452,7 @@ public class ActivityOptions extends ComponentOptions {
private String mSplashScreenThemeResName; private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle @SplashScreen.SplashScreenStyle
private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED; private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
private boolean mIsEligibleForLegacyPermissionPrompt;
private boolean mRemoveWithTaskOrganizer; private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble; private boolean mLaunchedFromBubble;
private boolean mTransientLaunch; private boolean mTransientLaunch;
@@ -1243,6 +1251,8 @@ public class ActivityOptions extends ComponentOptions {
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH); mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE); mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS); mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
mIsEligibleForLegacyPermissionPrompt =
opts.getBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE);
} }
/** /**
@@ -1473,6 +1483,24 @@ public class ActivityOptions extends ComponentOptions {
return this; return this;
} }
/**
* Whether the activity is eligible to show a legacy permission prompt
* @hide
*/
@TestApi
public boolean isEligibleForLegacyPermissionPrompt() {
return mIsEligibleForLegacyPermissionPrompt;
}
/**
* Sets whether the activity is eligible to show a legacy permission prompt
* @hide
*/
@TestApi
public void setEligibleForLegacyPermissionPrompt(boolean eligible) {
mIsEligibleForLegacyPermissionPrompt = eligible;
}
/** /**
* Sets whether the activity is to be launched into LockTask mode. * Sets whether the activity is to be launched into LockTask mode.
* *
@@ -1909,6 +1937,7 @@ public class ActivityOptions extends ComponentOptions {
mSpecsFuture = otherOptions.mSpecsFuture; mSpecsFuture = otherOptions.mSpecsFuture;
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter; mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams; mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt;
} }
/** /**
@@ -2084,6 +2113,10 @@ public class ActivityOptions extends ComponentOptions {
if (mLaunchIntoPipParams != null) { if (mLaunchIntoPipParams != null) {
b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams); b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
} }
if (mIsEligibleForLegacyPermissionPrompt) {
b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE,
mIsEligibleForLegacyPermissionPrompt);
}
return b; return b;
} }

View File

@@ -3972,8 +3972,14 @@ public class CentralSurfaces extends CoreStartable implements
mActivityLaunchAnimator.startPendingIntentWithAnimation( mActivityLaunchAnimator.startPendingIntentWithAnimation(
controller, animate, intent.getCreatorPackage(), controller, animate, intent.getCreatorPackage(),
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null, (animationAdapter) -> {
null, getActivityOptions(mDisplayId, animationAdapter))); ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, animationAdapter));
// TODO b/221255671: restrict this to only be set for notifications
options.setEligibleForLegacyPermissionPrompt(true);
return intent.sendAndReturnResult(null, 0, null, null, null,
null, options.toBundle());
});
} catch (PendingIntent.CanceledException e) { } catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here. // the stack trace isn't very helpful here.
// Just log the exception message. // Just log the exception message.

View File

@@ -34,6 +34,7 @@ import android.app.ActivityOptions;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal;
import android.app.KeyguardManager;
import android.app.TaskInfo; import android.app.TaskInfo;
import android.app.compat.CompatChanges; import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId; import android.compat.annotation.ChangeId;
@@ -150,6 +151,7 @@ public final class PermissionPolicyService extends SystemService {
private Context mContext; private Context mContext;
private PackageManagerInternal mPackageManagerInternal; private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManager; private NotificationManagerInternal mNotificationManager;
private final KeyguardManager mKeyguardManager;
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
public PermissionPolicyService(@NonNull Context context) { public PermissionPolicyService(@NonNull Context context) {
@@ -157,6 +159,7 @@ public final class PermissionPolicyService extends SystemService {
mContext = context; mContext = context;
mPackageManager = context.getPackageManager(); mPackageManager = context.getPackageManager();
mKeyguardManager = context.getSystemService(KeyguardManager.class);
LocalServices.addService(PermissionPolicyInternal.class, new Internal()); LocalServices.addService(PermissionPolicyInternal.class, new Internal());
} }
@@ -1046,12 +1049,21 @@ public final class PermissionPolicyService extends SystemService {
} }
@Override @Override
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
super.onActivityLaunched(taskInfo, activityInfo); ActivityInterceptorInfo info) {
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, super.onActivityLaunched(taskInfo, activityInfo, info);
UserHandle.of(taskInfo.userId)); if (!shouldShowNotificationDialogOrClearFlags(info.intent,
showNotificationPromptIfNeeded(activityInfo.packageName, info.checkedOptions)) {
taskInfo.userId, taskInfo.taskId); return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
if (CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
activityInfo.packageName, user)) {
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, user);
} else {
showNotificationPromptIfNeeded(activityInfo.packageName,
taskInfo.userId, taskInfo.taskId);
}
} }
}; };
@@ -1092,10 +1104,28 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId); launchNotificationPermissionRequestDialog(packageName, user, taskId);
} }
/**
* Determine if we should show a notification dialog, or clear the REVIEW_REQUIRED flag,
* from a particular package for a particular intent. Returns true if:
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
*/
private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
ActivityOptions options) {
if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
return true;
}
return Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.getCategories() != null
&& (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
|| intent.getCategories().contains(Intent.CATEGORY_LEANBACK_LAUNCHER)
|| intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER));
}
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) { private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user) if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
|| ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user) & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) {
return; return;
} }
try { try {
@@ -1210,8 +1240,8 @@ public final class PermissionPolicyService extends SystemService {
} }
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS) if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
pkg.getPackageName(), user)) { || mKeyguardManager.isKeyguardLocked()) {
return false; return false;
} }
@@ -1220,7 +1250,7 @@ public final class PermissionPolicyService extends SystemService {
mNotificationManager = LocalServices.getService(NotificationManagerInternal.class); mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
} }
boolean hasCreatedNotificationChannels = mNotificationManager boolean hasCreatedNotificationChannels = mNotificationManager
.getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0; .getNumNotificationChannelsForPackage(pkgName, uid, true) > 0;
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user); int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0; boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;

View File

@@ -43,9 +43,13 @@ public abstract class ActivityInterceptorCallback {
public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info); public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/** /**
* Called when an activity is successfully launched. * Called when an activity is successfully launched. The intent included in the
* ActivityInterceptorInfo may have changed from the one sent in
* {@link #intercept(ActivityInterceptorInfo)}, due to the return from
* {@link #intercept(ActivityInterceptorInfo)}.
*/ */
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
} }
/** /**

View File

@@ -188,10 +188,7 @@ class ActivityStartInterceptor {
final SparseArray<ActivityInterceptorCallback> callbacks = final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks(); mService.getActivityInterceptorCallbacks();
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo = final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid, getInterceptorInfo();
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
mActivityOptions);
for (int i = 0; i < callbacks.size(); i++) { for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i); final ActivityInterceptorCallback callback = callbacks.valueAt(i);
@@ -412,9 +409,17 @@ class ActivityStartInterceptor {
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
final SparseArray<ActivityInterceptorCallback> callbacks = final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks(); mService.getActivityInterceptorCallbacks();
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo();
for (int i = 0; i < callbacks.size(); i++) { for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i); final ActivityInterceptorCallback callback = callbacks.valueAt(i);
callback.onActivityLaunched(taskInfo, activityInfo); callback.onActivityLaunched(taskInfo, activityInfo, info);
} }
} }
private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo() {
return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
mActivityOptions);
}
} }

View File

@@ -352,6 +352,6 @@ public class ActivityStartInterceptorTest {
spyOn(callback); spyOn(callback);
mInterceptor.onActivityLaunched(null, null); mInterceptor.onActivityLaunched(null, null);
verify(callback, times(1)).onActivityLaunched(any(), any()); verify(callback, times(1)).onActivityLaunched(any(), any(), any());
} }
} }