diff options
| author | 2022-03-01 17:21:36 +0000 | |
|---|---|---|
| committer | 2022-03-01 17:21:36 +0000 | |
| commit | c4293b56e22d9c3a9092102782a07103eaacba68 (patch) | |
| tree | b722e11e3fc7e5031c09778340d286950423b42f | |
| parent | 1cb902278522316b975d20d13f7b48633776efc7 (diff) | |
| parent | eb4e3d20da268a26cea68cb365ce7a26e384d351 (diff) | |
Merge "Restrict what activity launches show permission prompts" into tm-dev
7 files changed, 102 insertions, 22 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 7edab1dc6280..ed18a2219915 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -155,8 +155,10 @@ package android.app { public class ActivityOptions { 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 @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 void setLaunchActivityType(int); method public void setLaunchWindowingMode(int); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 5ddaa80b1a82..1d14307e6294 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -174,6 +174,13 @@ public class ActivityOptions extends ComponentOptions { 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. * @hide */ @@ -445,6 +452,7 @@ public class ActivityOptions extends ComponentOptions { private String mSplashScreenThemeResName; @SplashScreen.SplashScreenStyle private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED; + private boolean mIsEligibleForLegacyPermissionPrompt; private boolean mRemoveWithTaskOrganizer; private boolean mLaunchedFromBubble; private boolean mTransientLaunch; @@ -1243,6 +1251,8 @@ public class ActivityOptions extends ComponentOptions { mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH); mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE); mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS); + mIsEligibleForLegacyPermissionPrompt = + opts.getBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE); } /** @@ -1474,6 +1484,24 @@ public class ActivityOptions extends ComponentOptions { } /** + * 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. * * Use this option to start an activity in LockTask mode. Note that only apps permitted by @@ -1909,6 +1937,7 @@ public class ActivityOptions extends ComponentOptions { mSpecsFuture = otherOptions.mSpecsFuture; mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter; mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams; + mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt; } /** @@ -2084,6 +2113,10 @@ public class ActivityOptions extends ComponentOptions { if (mLaunchIntoPipParams != null) { b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams); } + if (mIsEligibleForLegacyPermissionPrompt) { + b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE, + mIsEligibleForLegacyPermissionPrompt); + } return b; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 5642744d3390..4235c1f6b0bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -3972,8 +3972,14 @@ public class CentralSurfaces extends CoreStartable implements mActivityLaunchAnimator.startPendingIntentWithAnimation( controller, animate, intent.getCreatorPackage(), - (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null, - null, getActivityOptions(mDisplayId, animationAdapter))); + (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) { // the stack trace isn't very helpful here. // Just log the exception message. diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index c637c6764092..f2ce0d4c49d3 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -34,6 +34,7 @@ import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; +import android.app.KeyguardManager; import android.app.TaskInfo; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; @@ -150,6 +151,7 @@ public final class PermissionPolicyService extends SystemService { private Context mContext; private PackageManagerInternal mPackageManagerInternal; private NotificationManagerInternal mNotificationManager; + private final KeyguardManager mKeyguardManager; private final PackageManager mPackageManager; public PermissionPolicyService(@NonNull Context context) { @@ -157,6 +159,7 @@ public final class PermissionPolicyService extends SystemService { mContext = context; mPackageManager = context.getPackageManager(); + mKeyguardManager = context.getSystemService(KeyguardManager.class); LocalServices.addService(PermissionPolicyInternal.class, new Internal()); } @@ -1046,12 +1049,21 @@ public final class PermissionPolicyService extends SystemService { } @Override - public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { - super.onActivityLaunched(taskInfo, activityInfo); - clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, - UserHandle.of(taskInfo.userId)); - showNotificationPromptIfNeeded(activityInfo.packageName, - taskInfo.userId, taskInfo.taskId); + public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo, + ActivityInterceptorInfo info) { + super.onActivityLaunched(taskInfo, activityInfo, info); + if (!shouldShowNotificationDialogOrClearFlags(info.intent, + info.checkedOptions)) { + 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); } + /** + * 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) { - if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user) - || ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user) - & FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) { + if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user) + & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { return; } try { @@ -1210,8 +1240,8 @@ public final class PermissionPolicyService extends SystemService { } if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS) - || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, - pkg.getPackageName(), user)) { + || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user) + || mKeyguardManager.isKeyguardLocked()) { return false; } @@ -1220,7 +1250,7 @@ public final class PermissionPolicyService extends SystemService { mNotificationManager = LocalServices.getService(NotificationManagerInternal.class); } boolean hasCreatedNotificationChannels = mNotificationManager - .getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0; + .getNumNotificationChannelsForPackage(pkgName, uid, true) > 0; int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user); boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0; boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java index 4df2e1782e4f..06c58baee1f9 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java @@ -43,9 +43,13 @@ public abstract class ActivityInterceptorCallback { 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) { } /** diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 658a17bd9ec7..c5fcd12efa78 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -188,10 +188,7 @@ class ActivityStartInterceptor { final SparseArray<ActivityInterceptorCallback> callbacks = mService.getActivityInterceptorCallbacks(); final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo = - new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid, - mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent, - mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid, - mActivityOptions); + getInterceptorInfo(); for (int i = 0; i < callbacks.size(); i++) { final ActivityInterceptorCallback callback = callbacks.valueAt(i); @@ -412,9 +409,17 @@ class ActivityStartInterceptor { void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { final SparseArray<ActivityInterceptorCallback> callbacks = mService.getActivityInterceptorCallbacks(); + ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(); for (int i = 0; i < callbacks.size(); 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); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 55d6df95e66f..0c8394e75fbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -352,6 +352,6 @@ public class ActivityStartInterceptorTest { spyOn(callback); mInterceptor.onActivityLaunched(null, null); - verify(callback, times(1)).onActivityLaunched(any(), any()); + verify(callback, times(1)).onActivityLaunched(any(), any(), any()); } } |